关于opengl窗口设置可以参考:https://www.cnblogs.com/whiteblue/p/13830367.html
不忘推荐从入门到入土的教程:https://learnopengl-cn.github.io/01%20Getting%20started/01%20OpenGL/
所有笔记注释和我自己的理解在代码里~
#include"includes/glad.h" #include "includes/glfw3.h" #include<ctime> #include <iostream> void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow* window); // settings const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; //顶点着色器源码 const char* vertexShaderSource = "#version 330 core\n"//这是着色器语言GLSL语言,要告诉opengl着色器语言的版本号和我们使用的模式(OpenGL 3.3以及和更高版本中,GLSL版本号和OpenGL的版本是匹配的;如4.1-410) "layout(location=0) in vec3 p;\n" //关键词in为输入,本次只关心一个三维的点。 "void main()\n" "{\n" "gl_Position=vec4(p.x,p.y,p.z,1.0);\n"//作为下一阶段的输出。 "}\n\0"; //片段着色器源码 const char* fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n"//直接输出颜色。 "void main()\n" "{\n" " FragColor=vec4(1,0,0,1);"//最后一个参数是alpha透明度,1为不透明。 "}\n\0"; int main() { srand((int)(time(NULL))); // glfw: 对glfw的初始化 glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //告诉glfw使用的opgl(glad)主版本是3 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//告诉glfw使用的opgl(glad)次版本是3 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//告诉glfw我们使用的是核心模式,意味着我们只能使用OpenGL功能的一个子集 //创建窗口对象(本质为一typedef struct),函数参数分别是宽、高(不是像素)、窗口名称、?、?,返回对象的指针地址,后者是函数。 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "dawn666", NULL, NULL); //看窗口是否创建失败。 if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window);//真正设置窗口的函数,window只是定义的结构体。 //我们还需要注册window这个窗口的回调(视口)函数,使得实现窗口大小改变,视口同调。 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//使用window的宽高并调用回调函数。 //GLAD是用来加载管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD,初始化失败就会终止。 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } //***下面是对着色器使用。上一个着色器的输入为下一着色器输出; //着色器分为:顶点、几何、片段着色器。(中间很多步骤省略) //只需自定义顶点着色器和片段着色器即可,最后用着色器程序链接。 //定义着色器(绑定源码)->链接着色器(着色器程序)->定义顶点数组对象和缓冲对象......->over。 unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//返回顶点着色器ID glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//绑定源码第二个参数表示只有1个源码;第四个先不管 glCompileShader(vertexShader);//编译源码。 // 检查源码是否编译错误(必写。) 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; } //片段着色器; unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);// if (!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } //着色器程序,用来链接顶点和片段。 unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader);//先绑定 glLinkProgram(shaderProgram);//再链接,至此,简单的着色器已弄好,现在考虑怎么给他输入数据并使用。 //目前着色器程序弄好,两个单独的着色器就可以delete glDeleteShader(vertexShader); glDeleteShader(fragmentShader); //着色器是在GPU中的,我们要在CPU中定义数据并将其传输给GPU; //引入顶点数组对象和顶点缓冲对象VAO\VBO,顶点数组对象可以包含多个缓冲,如顶点缓冲对象和索引缓冲对象等等,顶点着色器里面包含了顶点属性,当然本次我们只需要顶点的坐标,不需要其他属性。 //每次使用着色器的时候,我们都要绑定顶点数组对象,同时许多设置都是针对我们最新绑定的顶点数组对象。即绑定后才使用函数。 float vertices[] = {//来个三角形和矩形吧。opengl处理3D,so Z=0。 0,0,0, 1,0,0, 1,1,0, 0,1,0, 0,0,0,//矩形。 -1.0,0,0, 0,0,0, -0.5,1,0,//triangle }; unsigned int indice[] = {0,1,2,3,4};//我们用索引画矩形,直接画三角形。这是索引顺序 unsigned int VAO, VBO,EBO;//EBO/IBO:索引缓冲; glGenVertexArrays(1,&VAO);//前面的参数1表示只有一种顶点类型。如:可以(2,VAO);不过VAO是数组。 glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO);//绑定VAO,以使用函数,进行点的输入。使用不同的VAO时要先绑定。 glBindBuffer(GL_ARRAY_BUFFER, VBO);//注意初始化时要将顶点缓冲VBO(索引缓冲EBO)绑定给VAO。 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//利用函数将数据给VAO中。最后一个参数指我们希望显卡如何管理给定的数据。它有三种形式:GL_STATIC_DRAW :数据不会或几乎不会改变。GL_DYNAMIC_DRAW:数据会被改变很多。GL_STREAM_DRAW :数据每次绘制时都会改变 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indice), indice, GL_STATIC_DRAW);//索引缓冲同理 //数据给了VAO后,当我们使用着色器时,需要将VAO的数据给顶点着色器,我们要告诉opengl怎么解析顶点数据(如传入的位置等)。顶点着色器中称为顶点属性。 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); //这时VAO可以将数据正常传给顶点着色器了。但真正使用,我们还要启用顶点属性。 /*第一个参数指定我们要配置的顶点属性。还记得我们在顶点着色器中使用layout(location = 0)定义了position顶点属性的位置值(Location)吗?它可以把顶点属性的位置值设置为0。因为我们希望把数据传递到这一个顶点属性中,所以这里我们传入0。 第二个参数指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。 第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中vec*都是由浮点数值组成的)。 下个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。 第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)。要注意的是由于我们知道这个数组是紧密排列的(在两个顶点属性之间没有空隙)我们也可以设置为0来让OpenGL决定具体步长是多少(只有当数值是紧密排列时才可用)。一旦我们有更多的顶点属性,我们就必须更小心地定义每个顶点属性之间的间隔,我们在后面会看到更多的例子(译注: 这个参数的意思简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节)。 最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换。它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。我们会在后面详细解释这个参数。*/ glEnableVertexAttribArray(0);//0是启用的初始位置参数。 //至此,各种初始化绑定好了。VAO只是个例子可以试试了。 // 渲染循环render loop;我们都是先绑定要渲染的东西,再使用着色器执行。 // ----------- while (!glfwWindowShouldClose(window)) { // 通过函数调用判断用户是否按下ESC,按下则设置参数为该窗口可以关闭。 // ----- processInput(window); // render渲染循环 // ------ glClearColor((float)(rand()%500)/500, (float)(rand() % 500) / 500, (float)(rand() % 500) / 500, (float)(rand() % 500) / 500);//清空窗口的后缓冲,并设置后缓冲所用的颜色值。 glClear(GL_COLOR_BUFFER_BIT);//清空窗口前缓冲缓冲。 //glfwWindowShouldClose函数在我们每次循环的开始前检查一次GLFW是否被要求退出,如果是的话该函数返回true然后渲染循环便结束了,之后为我们就可以关闭应用程序了。 //glfwPollEvents函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。 //glfwSwapBuffers函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。 glBindVertexArray(VAO);//其实之前绑定了,不用绑定。 glUseProgram(shaderProgram);//可以使用着色器了。 glDrawArrays(GL_TRIANGLES, 5, 3);//画;从5开始,长度为3。 glDrawElements(GL_LINE_STRIP, 5, GL_UNSIGNED_INT, 0); //第二个参数是画的顶点数,第三个是索引类型,最后一个参数里我们可以指定EBO中的偏移量(或者传递一个索引数组,但是这是当你不在使用索引缓冲对象的时候),但是我们会在这里填写0。 //glBindVertexArray(0); glfwSwapBuffers(window);//交换前后缓冲 glfwPollEvents(); } //当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源。我们可以在main函数的最后调用glfwTerminate函数来完成。 //---------------------------------------------------------------------------- glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glDeleteProgram(shaderProgram); glfwTerminate(); return 0; } void processInput(GLFWwindow* window)//是否退出窗口 { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); } //当用户改变窗口的大小的时候,视口也应该被调整。我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用。 void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height);//glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。 //*我们实际上也可以将视口的维度设置为比GLFW的维度小,这样子之后所有的OpenGL渲染将会在一个更小的窗口中显示,这样子的话我们也可以将一些其它元素显示在OpenGL视口之外。 }
posted on
浙公网安备 33010602011771号