学习内容来自 此处
摘抄自用
学完才发现,最好的学习方式先读一遍教程。
然后对着代码,再和教程联系起来读一遍。
文章目录
入门
OpenGL
立即渲染模式从OpenGL实际运作中抽象掉了很多细节,因此它在易于学习的同时,也很难让人去把握OpenGL具体是如何运作的。现代函数要求使用者真正理解OpenGL和图形编程,它有一些难度,然而提供了更多的灵活性,更高的效率,更重要的是可以更深入的理解图形编程。
当我们使用一个对象时,通常看起来像如下一样(把OpenGL上下文看作一个大的结构体):
// 创建对象
unsigned int objectId = 0;
glGenObject(1, &objectId);
// 绑定对象至上下文
glBindObject(GL_WINDOW_TARGET, objectId);
// 设置当前绑定到 GL_WINDOW_TARGET 的对象的一些选项
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
// 将上下文对象设回默认
glBindObject(GL_WINDOW_TARGET, 0);
这一小段代码展现了你以后使用OpenGL时常见的工作流。我们首先创建一个对象,然后用一个id保存它的引用(实际数据被储存在后台)。然后我们将对象绑定至上下文的目标位置(例子中窗口对象目标的位置被定义成GL_WINDOW_TARGET)。接下来我们设置窗口的选项。最后我们将目标位置的对象id设回0,解绑这个对象。设置的选项将被保存在objectId所引用的对象中,一旦我们重新绑定这个对象到GL_WINDOW_TARGET位置,这些选项就会重新生效。
使用对象的一个好处是在程序中,我们不止可以定义一个对象,并设置它们的选项,每个对象都可以是不同的设置。在我们执行一个使用OpenGL状态的操作的时候,只需要绑定含有需要的设置的对象即可。
你好,窗口
GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文,定义窗口参数以及处理用户输入
创建窗口
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
int main()
{
if (!glfwInit()) {
std::cout << "Failed to init glf" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;// Window or context creation failed
}
glfwDestroyWindow(window);
glfwTerminate();//释放资源
return 0;
}
glfwWindowHint:
默认情况下,GLFW创建的OpenGL上下文可以有任何版本。您可以通过在创建之前设置GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR提示来要求最小的OpenGL版本。如果计算机不支持所需的最小版本,则创建上下文(和窗口)会失败。
我们将主版本号(Major)和次版本号(Minor)都设为3。我们同样明确告诉GLFW我们使用的是核心模式(Core-profile)
glfwCreateWindow函数需要窗口的宽和高作为它的前两个参数
视口
glViewport(0, 0, 800, 600);
glViewport函数来设置窗口的维度(Dimension):
glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。
OpenGL幕后使用glViewport中定义的位置和宽高进行2D坐标的转换,将OpenGL中的位置坐标转换为你的屏幕坐标。
例如,OpenGL中的坐标(-0.5, 0.5)有可能(最终)被映射为屏幕中的坐标(200,450)。注意,处理过的OpenGL坐标范围只为-1到1,因此我们事实上将(-1到1)范围内的坐标映射到(0, 800)和(0, 600)。
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetFramebufferSizeCallback回调函数,在window大小发生变化的时候,调用第二个自己写的函数
我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。因此,我们需要在程序中添加一个while循环,我们可以把它称之为渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环:
while(!glfwWindowShouldClose(window))
{
glfwSwapBuffers(window);
glfwPollEvents(); //两句话顺序很重要
}
当调用opengl的函数时,必须要有一个正确类型的当前上下文,而且在同一时间只有一个上下文可以被设置为一个单线程的当前上下文,每个线程在同一时间只能拥有一个当前上下文(current context),glfwCreateWindow函数可以帮助我们创建一个上下文。
glfwMakeContextCurrent(window);
渲染
// 渲染循环
while(!glfwWindowShouldClose(window))
{
// 输入
processInput(window);
// 渲染指令
...
// 检查并调用事件,交换缓冲
glfwPollEvents();
glfwSwapBuffers(window);
}
通过调用glClear函数来清空屏幕的颜色缓冲,它接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,可能的缓冲位有GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT。
调用了glClearColor来设置清空屏幕所用的颜色。当调用glClear函数,清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。
glClearColor函数是一个状态设置函数,而glClear函数则是一个状态使用的函数,它使用了当前的状态来获取应该清除为的颜色。
整体代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);//视口变换
}
//当窗口被第一次显示的时候framebuffer_size_callback也会被调用。
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
int main()
{
if (!glfwInit()) {
std::cout << "Failed to init glf" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;// Window or context creation failed
}
glfwMakeContextCurrent(window);//固定当前window
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//调整大小部分
//在调用任何OpenGL的函数之前我们需要初始化GLAD。
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
//渲染循环
while (!glfwWindowShouldClose(window))
{
//输入
processInput(window);//检查是否ESC
//渲染
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//交换缓冲 检查并调用事件
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();//释放资源
return 0;
}
你好,三角形
图形渲染管线的工作流程:
(1)图形渲染管线的输入
(2)顶点着色器(Vertex Shader),它把一个单独的顶点作为输入。顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理
(3)图元装配(Primitive Assembly)阶段将顶点着色器输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状
(4)图元装配阶段的输出会传递给几何着色器(Geometry Shader)。几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。
(5)几何着色器的输出会被传入光栅化阶段(Rasterization Stage),这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
(6)片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。
(7)在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶段,我们叫做Alpha测试和混合(Blending)阶段。这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend)。
顶点输入
标准化设备坐标(Normalized Device Coordinates, NDC):
一旦你的顶点坐标已经在顶点着色器中处理过,它们就应该是标准化设备坐标了,标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。
与通常的屏幕坐标不同,y轴正方向为向上,(0, 0)坐标是这个图像的中心,而不是左上角。
你的标准化设备坐标接着会变换为屏幕空间坐标(Screen-space Coordinates),这是使用你通过glViewport函数提供的数据,进行视口变换(Viewport Transform)完成的。所得的屏幕空间坐标又会被变换为片段输入到片段着色器中。
在真实的程序里输入数据通常都不是标准化设备坐标,所以首先必须先把它们转换至OpenGL的可视区域内。
然后:我们把顶点数据储存在显卡的内存中,用VBO这个顶点缓冲对象管理
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
//我们使用的任何(在GL_ARRAY_BUFFER目标上的)缓冲调用都会用来配置当前绑定的缓冲(VBO)。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//把之前定义的顶点数据复制到缓冲的内存中
顶点着色器
第一件事是用着色器语言GLSL(OpenGL Shading Language)编写顶点着色器,然后编译这个着色器,这样我们就可以在程序中使用它了。
//顶点着色器的源代码硬编码在代码文件顶部的C风格字符串中:
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
....
//创建一个着色器对象
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);//参数是需要创建的着色器类型
//把这个着色器源码附加到着色器对象上,然后编译它
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
片段着色器
片段着色器(Fragment Shader)是第二个也是最后一个我们打算创建的用于渲染三角形的着色器。片段着色器所做的是计算像素最后的颜色输出。
在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量。当在OpenGL或GLSL中定义一个颜色的时候,我们把颜色每个分量的强度设置在0.0到1.0之间。
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" //这句话就是定义颜色
"}\n\0";
...
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
着色器程序
着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本。
如果要使用刚才编译的着色器我们必须把它们链接(Link)为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。
已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。
当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下个着色器的输入。当输出和输入不匹配的时候,你会得到一个连接错误。
//创建一个程序对象
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
//之前编译的着色器附加到程序对象上
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
//链接
glLinkProgram(shaderProgram);
//激活这个程序对象
glUseProgram(shaderProgram);
//把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们了
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
链接顶点属性
我们已经把输入顶点数据发送给了GPU,并指示了GPU如何在顶点和片段着色器中处理它。但是,OpenGL还不知道它该如何解释内存中的顶点数据,以及它该如何将顶点数据链接到顶点着色器的属性上。我们需要告诉OpenGL怎么做。
我们必须在渲染前指定OpenGL该如何解释顶点数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);//以顶点属性位置值(“(location = 0)”)作为参数,启用顶点属性;
自此,所有东西都已经设置好了:我们使用一个顶点缓冲对象将顶点数据初始化至缓冲中,建立了一个顶点和一个片段着色器,并告诉了OpenGL如何把顶点数据链接到顶点着色器的顶点属性上。
顶点数组对象
顶点数组对象(Vertex Array Object, VAO)可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中。
这样的好处就是,当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。
//创建VAO
unsigned int VAO;
glGenVertexArrays(1, &VAO);
// ..:: 初始化代码(只运行一次 (除非你的物体频繁改变)) :: ..
// 1. 绑定VAO
glBindVertexArray(VAO);
// 2. 把顶点数组复制到缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
[...]
// ..:: 绘制代码(渲染循环中) :: ..
// 4. 绘制物体
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();
CODE
最后官方CODE
//by qi
//2021 10 07
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);//视口
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main()
{
// glfw: initialize and configure
// ------------------------------
if (!glfwInit()) {
std::cout << "Failed to init glf" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;// Window or context creation failed
}
glfwMakeContextCurrent(window);//固定当前window
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//调整大小部分
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexshader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexshader);
//check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexshader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//fragment shader
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentshader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentshader);
//check for shader compile errors
glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentshader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//link shaders
unsigned int shaderprogram = glCreateProgram();
glAttachShader(shaderprogram, vertexshader);
glAttachShader(shaderprogram, fragmentshader);
glLinkProgram(shaderprogram);
//check for linking errors
glGetProgramiv(shaderprogram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderprogram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vert[] = {
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.0f,0.5f,0.0f
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);//顶点数组对象
glGenBuffers(1, &VBO);//顶点缓冲对象
// bind the Vertex Array Object first
glBindVertexArray(VAO);
// then bind and set vertex buffer(s)
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);
//and then configure vertex attributes(s).
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//渲染循环
while (!glfwWindowShouldClose(window))
{
//输入
processInput(window);//检查是否ESC
//渲染
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画
glUseProgram(shaderprogram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glfw:交换缓冲区和轮询IO事件(按键按下/释放,鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderprogram);
glfwDestroyWindow(window);
glfwTerminate();//释放资源
return 0;
}
索引缓冲对象
再输入顶点重复的情况下,更好的解决方案是只储存不同的顶点,并设定绘制这些顶点的顺序。
很幸运,索引缓冲对象的工作方式正是这样的。和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。所谓的索引绘制(Indexed Drawing)正是我们问题的解决方案。
// ..:: 初始化代码 :: ..
// 1. 绑定顶点数组对象
glBindVertexArray(VAO);
// 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 复制我们的索引数组到一个索引缓冲中,供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 设定顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
[...]
// ..:: 绘制代码(渲染循环中) :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) //highlight!!
glBindVertexArray(0);
线框模式:
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)函数配置OpenGL如何绘制图元。第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制。
之后的绘制调用会一直以线框模式绘制三角形,直到我们用glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)将其设置回默认模式。
//长方形绘制
//by qi
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);//视口
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main()
{
// glfw: initialize and configure
// ------------------------------
if (!glfwInit()) {
std::cout << "Failed to init glf" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;// Window or context creation failed
}
glfwMakeContextCurrent(window);//固定当前window
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//调整大小部分
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexshader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexshader);
//check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexshader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//fragment shader
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentshader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentshader);
//check for shader compile errors
glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentshader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//link shaders
unsigned int shaderprogram = glCreateProgram();
glAttachShader(shaderprogram, vertexshader);
glAttachShader(shaderprogram, fragmentshader);
glLinkProgram(shaderprogram);
//check for linking errors
glGetProgramiv(shaderprogram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderprogram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vert[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f // top left
};
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
};
unsigned int VBO, VAO,EBO;
glGenVertexArrays(1, &VAO);//顶点数组对象
glGenBuffers(1, &VBO);//顶点缓冲对象
glGenBuffers(1, &EBO);
// bind the Vertex Array Object first
glBindVertexArray(VAO);
// then bind and set vertex buffer(s)
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//and then configure vertex attributes(s).
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
//应用线框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//渲染循环
while (!glfwWindowShouldClose(window))
{
//输入
processInput(window);//检查是否ESC
//渲染
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画
glUseProgram(shaderprogram);
glBindVertexArray(VAO);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//glBindVertexArray(0);
//glfw:交换缓冲区和轮询IO事件(按键按下/释放,鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
glDeleteBuffers(1,&EBO);
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderprogram);
glfwDestroyWindow(window);
glfwTerminate();//释放资源
return 0;
}
homework1
添加更多顶点到数据中,使用glDrawArrays,尝试绘制两个彼此相连的三角形:
(1)用glDrawElements:改三角形顶点索引
(2)使用glDrawArrays:改顶点序列
(3)results
homework2
创建相同的两个三角形,但对它们的数据使用不同的VAO和VBO
这部分没有改动
//by qi
//2021 10 07
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);//视口
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main()
{
// glfw: initialize and configure
// ------------------------------
if (!glfwInit()) {
std::cout << "Failed to init glf" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;// Window or context creation failed
}
glfwMakeContextCurrent(window);//固定当前window
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//调整大小部分
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexshader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexshader);
//check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexshader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexshader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//fragment shader
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentshader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentshader);
//check for shader compile errors
glGetShaderiv(fragmentshader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentshader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
//link shaders
unsigned int shaderprogram = glCreateProgram();
glAttachShader(shaderprogram, vertexshader);
glAttachShader(shaderprogram, fragmentshader);
glLinkProgram(shaderprogram);
//check for linking errors
glGetProgramiv(shaderprogram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderprogram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
然后是重点:
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vert[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
};
float vert2[] = {
-0.9f, -0.5f, 0.0f, // left
-0.0f, -0.5f, 0.0f, // right
-0.45f, 0.5f, 0.0f, // top
};
//unsigned int indices[] = { // note that we start from 0!
// 0, 1, 2, // first Triangle
// 1, 2, 3 // second Triangle
//};
unsigned int VBO[2], VAO[2];
glGenVertexArrays(2, VAO);//顶点数组对象 第一个参数是size,第二个参数是一个指针,指向数组
glGenBuffers(2, VBO);//顶点缓冲对象
//glGenBuffers(1, &EBO);
// first triangle setup
// --------------------
glBindVertexArray(VAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);//将VBO和vert联系起来
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//顶点属性配置+与顶点属性关联的顶点缓冲对象(此时VAO和VBO联系起来)
glEnableVertexAttribArray(0);
// second triangle setup
// ---------------------
glBindVertexArray(VAO[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert2), vert2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//and then configure vertex attributes(s).
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
//应用线框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//渲染循环
while (!glfwWindowShouldClose(window))
{
//输入
processInput(window);//检查是否ESC
//渲染
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画
glUseProgram(shaderprogram);
// draw first triangle using the data from the first VAO
glBindVertexArray(VAO[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
// then we draw the second triangle using the data from the second VAO
glBindVertexArray(VAO[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//glBindVertexArray(0);
//glfw:交换缓冲区和轮询IO事件(按键按下/释放,鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
//glDeleteBuffers(1,&EBO);
glDeleteVertexArrays(2, VAO);
glDeleteBuffers(2, VBO);
glDeleteProgram(shaderprogram);
glfwDestroyWindow(window);
glfwTerminate();//释放资源
return 0;
}
result:
homework3
创建两个着色器程序,第二个程序使用一个不同的片段着色器,输出黄色;再次绘制这两个三角形,让其中一个输出为黄色
//by qi
//2021 10 07
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);//视口
}
void processInput(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//顶点着色器源码
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
//片段着色器源码
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
const char* fragmentShaderSource2 = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);\n"
"}\n\0";
int main()
{
// glfw: initialize and configure
// ------------------------------
if (!glfwInit()) {
std::cout << "Failed to init glf" << std::endl;
return -1;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//设置版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (!window) {
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;// Window or context creation failed
}
glfwMakeContextCurrent(window);//固定当前window
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//调整大小部分
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexshader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexshader);
//fragment shader
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentshader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentshader);
unsigned int fragmentshader2 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentshader2, 1, &fragmentShaderSource2, NULL);
glCompileShader(fragmentshader2);
//link shaders
unsigned int shaderprogram = glCreateProgram();
glAttachShader(shaderprogram, vertexshader);
glAttachShader(shaderprogram, fragmentshader);
glLinkProgram(shaderprogram);
glDeleteShader(fragmentshader);
unsigned int shaderprogram2 = glCreateProgram();
glAttachShader(shaderprogram2, vertexshader);
glAttachShader(shaderprogram2, fragmentshader2);
glLinkProgram(shaderprogram2);
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader2);
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vert[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
};
float vert2[] = {
-0.9f, -0.5f, 0.0f, // left
-0.0f, -0.5f, 0.0f, // right
-0.45f, 0.5f, 0.0f, // top
};
//unsigned int indices[] = { // note that we start from 0!
// 0, 1, 2, // first Triangle
// 1, 2, 3 // second Triangle
//};
unsigned int VBO[2], VAO[2];
glGenVertexArrays(2, VAO);//顶点数组对象 第一个参数是size,第二个参数是一个指针,指向数组
glGenBuffers(2, VBO);//顶点缓冲对象
//glGenBuffers(1, &EBO);
// first triangle setup
// --------------------
glBindVertexArray(VAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);//将VBO和vert联系起来
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//顶点属性配置+与顶点属性关联的顶点缓冲对象(此时VAO和VBO联系起来)
glEnableVertexAttribArray(0);
// second triangle setup
// ---------------------
glBindVertexArray(VAO[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vert2), vert2, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//and then configure vertex attributes(s).
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
//应用线框模式
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//渲染循环
while (!glfwWindowShouldClose(window))
{
//输入
processInput(window);//检查是否ESC
//渲染
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//画
glUseProgram(shaderprogram);
// draw first triangle using the data from the first VAO
glBindVertexArray(VAO[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
// then we draw the second triangle using the data from the second VAO
glUseProgram(shaderprogram2);
glBindVertexArray(VAO[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
//glBindVertexArray(0);
//glfw:交换缓冲区和轮询IO事件(按键按下/释放,鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
//glDeleteBuffers(1,&EBO);
glDeleteVertexArrays(2, VAO);
glDeleteBuffers(2, VBO);
glDeleteProgram(shaderprogram);
glDeleteProgram(shaderprogram2);
glfwDestroyWindow(window);
glfwTerminate();//释放资源
return 0;
}