openGL学习<三>、图形渲染管道(显示三角形)

发布时间 2023-09-30 10:03:27作者: FeiYull
参考:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
  • 1、图形渲染管线
  • 2、顶点渲染器
  • 3、片段渲染器
  • 4、生成着色器程序
  • 5、

 

1、图形渲染管线

  图形渲染管线分为几个阶段小任务,对于每一个阶段任务程序(例如给10亿个像素着色),GPU都可以并行执行,这些小程序叫做着色器(Shader)。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)。

  图形渲染管线包含:

  • a:3D坐标转换为2D坐标,
  • b:2D坐标转变为有颜色的像素

  说白了,点 = 顶点(Vertex) + 顶点属性(Vertex Attribute),例如:点 = 坐标 + 颜色,3D点的渲染过程就是不断在修改其坐标、属性的过程。如下图,是图形渲染管线流程,它的每个流程不是在修改点的位置,就是修改点的属性。

 

 

 

 

 图1   图形渲染管线

 

  对于大多数场合,我们只需要配置顶点和片段着色器就行了

2、顶点渲染器

2.1、顶点输入

 1 //顶点输入:
 2 float vertices[] = {
 3     -0.5f, -0.5f, 0.0f,
 4      0.5f, -0.5f, 0.0f,
 5      0.0f,  0.5f, 0.0f
 6 };
 7 unsigned int VBO; // ID
 8 glGenBuffers(1, &VBO);// 生成缓冲
 9 glBindBuffer(GL_ARRAY_BUFFER, VBO);  // 将缓冲绑定到 GL_ARRAY_BUFFER 类型目标上
10 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将vertices复制到缓冲中

2.2、定义顶点着色器

1 #GLSL顶点着色器的源代码:
2 #version 330 core
3 layout (location = 0) in vec3 aPos;
4 
5 void main()
6 {
7     gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
8 }

2.3、编译顶点着色器

 1 //运行时动态编译它的源代码:
 2 const char *vertexShaderSource = "#version 330 core\n"
 3     "layout (location = 0) in vec3 aPos;\n"
 4     "void main()\n"
 5     "{\n"
 6     "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
 7     "}\0";
 8 
 9 //创建顶点着色器:
10 unsigned int vertexShader;// 着色器ID
11 vertexShader = glCreateShader(GL_VERTEX_SHADER); 
12 //着色器源码附加到着色器对象
13 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
14 glCompileShader(vertexShader); // 着色器源码编译

   最后还要验证下编译是否成功

3、片段渲染器

   和上一节流程一致。

3.1、定义片段着色器

1 #version 330 core
2 out vec4 FragColor;
3 
4 void main()
5 {
6     FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
7 } 

3.2、编译片段着色器

 1 //运行时动态编译它的源代码:
 2 const char *fragmentShaderSource = "#version 330 core\n"
 3                                    "out vec4 FragColor;\n"
 4                                    "uniform vec4 ourColor;\n"
 5                                    "void main()\n"
 6                                    "{\n"
 7                                    "   FragColor = ourColor;\n"
 8                                    "}\n\0";
 9 //创建片段着色器:
10 unsigned int fragmentShader;// 着色器ID
11 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
12 //着色器源码附加到着色器对象
13 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
14 glCompileShader(fragmentShader);// 着色器源码编译

  最后还要验证下编译是否成功

 4、生成着色器程序

  将上述顶点、片段着色器链接为着色器程序。比较简单,没什么JB好说的。

 1 // 创建着色器程序
 2 unsigned int shaderProgram;
 3 shaderProgram = glCreateProgram();
 4 
 5 // 将顶点、片段着色器链接为:着色器程序
 6 glAttachShader(shaderProgram, vertexShader);
 7 glAttachShader(shaderProgram, fragmentShader);
 8 glLinkProgram(shaderProgram);
 9 
10 //验证是否链接成功
11 //...
12 
13 // 释放顶点、片段着色器
14 glDeleteShader(vertexShader);
15 glDeleteShader(fragmentShader);
16 
17 
18 // ...
19 
20 // while循环中使用
21 glUseProgram(shaderProgram);

 

 

 

 

 

 

 

绘制三角形

  1 //
  2 // Created by sry on 2021/7/6.1
  3 //
  4 #include <glad/glad.h>
  5 #include <GLFW/glfw3.h>
  6 #include<iostream>
  7 using namespace std;
  8 static int p = 1;
  9 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
 10 {
 11     glViewport(0, 0, width, height);  // opengl_frame glfw_frame new_frame,知道为啥有这么多JB帧了吧
 12 }
 13 /*
 14  *
 15  * 这里我们检查用户是否按下了返回键(Esc)(如果没有按下,glfwGetKey将会返回GLFW_RELEASE。
 16  * 如果用户的确按下了返回键,我们将通过glfwSetwindowShouldClose使用把WindowShouldClose属性设置为 true的方法关闭GLFW。
 17  *
 18  * */
 19 void processInput(GLFWwindow *window)
 20 {
 21     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
 22         glfwSetWindowShouldClose(window, true);
 23     if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) // 按下1变为线框显示
 24         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
 25     else if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) // 按下A变为填充显示
 26         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 27 }
 28 
 29 // glsl 渲染语言,这和C++调用Matlab差不多
 30 const char *vertexShaderSource = "#version 330 core\n"
 31                                  "layout (location = 0) in vec3 aPos;\n"
 32                                  "void main()\n"
 33                                  "{\n"
 34                                  "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
 35                                  "}\0";
 36 
 37 const char *fragmentShaderSource = "#version 330 core\n"
 38                                    "out vec4 FragColor;\n"
 39                                    "void main()\n"
 40                                    "{\n"
 41                                    "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
 42                                    "}\n\0";
 43 
 44 int main()
 45 {
 46     // <1>、初始化GLFW
 47     glfwInit();
 48     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // opengl3.3
 49     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 50     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 只用核心通用模块
 51 
 52     // <2>、glfw窗口创建
 53     GLFWwindow* window = glfwCreateWindow(800, 600, "shiruiyu", NULL, NULL);
 54     if (window == NULL)
 55     {
 56         cout << "fuck" << endl;
 57         glfwTerminate(); // Release
 58         return -1;
 59     }
 60     glfwMakeContextCurrent(window); // 通知GLFW将我们窗口的上下文设置为当前线程的主上下文了。
 61     /*
 62      * 告诉OpenGL渲染窗口的尺寸大小
 63      * OpenGL坐标范围只为-1到1,因此我们事实上将(-1到1)范围内的坐标映射到(0, 800)和(0, 600)。
 64      * */
 65     // 对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用
 66     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
 67     /*
 68      * <3>、利用glad加载所有OpenGL函数地址
 69      * GLAD是用来管理OpenGL的函数指针的,所以在调用任何OpenGL的函数之前我们需要初始化GLAD
 70      * */
 71     if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress))
 72     {
 73         cout << "Failed to init GLAD" << endl;
 74         return -1;
 75     }
 76     // <4>、构建、编译shader program
 77     // 创建顶点渲染器,vertex shader
 78     unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
 79     glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
 80     glCompileShader(vertexShader);
 81     // 验证渲染器是否正常
 82     int success = 0;
 83     char infoLog[512];
 84     glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
 85     if (!success)
 86     {
 87         glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
 88         std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
 89     }
 90     // 创建线段渲染器,fragment shader
 91     unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
 92     glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
 93     glCompileShader(fragmentShader);
 94     // 验证渲染器是否正常
 95     success = 0;
 96     glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
 97     if (!success)
 98     {
 99         glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
100         std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
101     }
102     // 链接渲染器,link shaders
103     unsigned int shaderProgram = glCreateProgram();
104     // 将两个着色器加到shaderProgram中,所以下面释放这两个着色器。
105     glAttachShader(shaderProgram, vertexShader);
106     glAttachShader(shaderProgram, fragmentShader);
107     glLinkProgram(shaderProgram);
108     // 检测链接错误
109     success = 0;
110     glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
111     if (!success)
112     {
113         glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
114         std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
115     }
116     // 释放两个着色器
117     glDeleteShader(vertexShader);
118     glDeleteShader(fragmentShader);
119 
120     // set up vertex data (and buffer(s)) and configure vertex attributes
121     // ------------------------------------------------------------------
122     float vertices[] = {
123             -0.5f, -0.5f, 0.0f, // left
124             0.5f, -0.5f, 0.0f, // right
125             0.0f,  0.5f, 0.0f  // top
126     };
127     // VAO:顶点数组对象,存储调用VBO的历史过程
128     unsigned int VBO, VAO;
129     glGenVertexArrays(1, &VAO);
130     glBindVertexArray(VAO); // 绑定VAO,// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
131 
132     // VBO: 顶点缓冲对象
133     glGenBuffers(1, &VBO); // 利用缓冲区发送数据,效率更高
134     glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定VBO
135     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 将数据传送到VBO
136 
137 
138     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
139     glEnableVertexAttribArray(0);
140 
141     // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
142     glBindBuffer(GL_ARRAY_BUFFER, 0);
143     // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
144     // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
145     glBindVertexArray(0); // 解绑????
146 
147     // 循环渲染
148     while(!glfwWindowShouldClose(window)) // 检查一次GLFW是否被要求退出
149     {
150         // 输入
151         processInput(window);
152 
153         // 渲染指令
154         glClearColor(1.f, 0.3f, 0.3f, 1.0f);
155         glClear(GL_COLOR_BUFFER_BIT);
156 
157         // 画第一个三角形
158         glUseProgram(shaderProgram);
159         glBindVertexArray(VAO);
160         glDrawArrays(GL_TRIANGLES, 0, 3);// Primitive:GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP
161 
162 
163 
164 
165 
166         // 检查并调用事件,交换缓冲
167         glfwPollEvents(); // 检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。
168         glfwSwapBuffers(window); // 函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
169 
170     }
171 
172     glDeleteVertexArrays(1, &VAO);
173     glDeleteBuffers(1, &VBO);
174     glDeleteProgram(shaderProgram);
175 
176     // release
177     glfwTerminate();
178 
179     return 0;
180 }

绘制矩形

  1 #include <glad/glad.h>
  2 #include <GLFW/glfw3.h>
  3 
  4 #include <iostream>
  5 
  6 void framebuffer_size_callback(GLFWwindow* window, int width, int height);
  7 void processInput(GLFWwindow *window);
  8 
  9 // settings
 10 const unsigned int SCR_WIDTH = 800;
 11 const unsigned int SCR_HEIGHT = 600;
 12 
 13 const char *vertexShaderSource = "#version 330 core\n"
 14                                  "layout (location = 0) in vec3 aPos;\n"
 15                                  "void main()\n"
 16                                  "{\n"
 17                                  "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
 18                                  "}\0";
 19 const char *fragmentShaderSource = "#version 330 core\n"
 20                                    "out vec4 FragColor;\n"
 21                                    "void main()\n"
 22                                    "{\n"
 23                                    "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
 24                                    "}\n\0";
 25 
 26 int main()
 27 {
 28     // glfw: initialize and configure
 29     // ------------------------------
 30     glfwInit();
 31     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 32     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 33     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 34 
 35 #ifdef __APPLE__
 36     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
 37 #endif
 38 
 39     // glfw window creation
 40     // --------------------
 41     GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
 42     if (window == NULL)
 43     {
 44         std::cout << "Failed to create GLFW window" << std::endl;
 45         glfwTerminate();
 46         return -1;
 47     }
 48     glfwMakeContextCurrent(window);
 49     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
 50 
 51     // glad: load all OpenGL function pointers
 52     // ---------------------------------------
 53     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
 54     {
 55         std::cout << "Failed to initialize GLAD" << std::endl;
 56         return -1;
 57     }
 58 
 59 
 60     // build and compile our shader program
 61     // ------------------------------------
 62     // vertex shader
 63     unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
 64     glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
 65     glCompileShader(vertexShader);
 66     // check for shader compile errors
 67     int success;
 68     char infoLog[512];
 69     glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
 70     if (!success)
 71     {
 72         glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
 73         std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
 74     }
 75     // fragment shader
 76     unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
 77     glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
 78     glCompileShader(fragmentShader);
 79     // check for shader compile errors
 80     glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
 81     if (!success)
 82     {
 83         glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
 84         std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
 85     }
 86     // link shaders
 87     unsigned int shaderProgram = glCreateProgram();
 88     glAttachShader(shaderProgram, vertexShader);
 89     glAttachShader(shaderProgram, fragmentShader);
 90     glLinkProgram(shaderProgram);
 91     // check for linking errors
 92     glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
 93     if (!success) {
 94         glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
 95         std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
 96     }
 97     glDeleteShader(vertexShader);
 98     glDeleteShader(fragmentShader);
 99 
100     // set up vertex data (and buffer(s)) and configure vertex attributes
101     // ------------------------------------------------------------------
102     float vertices[] = {
103             0.5f,  0.5f, 0.0f,  // top right
104             0.5f, -0.5f, 0.0f,  // bottom right
105             -0.5f, -0.5f, 0.0f,  // bottom left
106             -0.5f,  0.5f, 0.0f   // top left
107     };
108     unsigned int indices[] = {  // note that we start from 0!
109             0, 1, 3,  // first Triangle
110             1, 2, 3   // second Triangle
111     };
112     unsigned int VBO, VAO, EBO;
113     glGenVertexArrays(1, &VAO);
114     glGenBuffers(1, &VBO);
115     glGenBuffers(1, &EBO);
116     // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
117     glBindVertexArray(VAO);
118 
119     glBindBuffer(GL_ARRAY_BUFFER, VBO);
120     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
121 
122     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
123     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
124 
125     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
126     glEnableVertexAttribArray(0);
127 
128     // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
129     glBindBuffer(GL_ARRAY_BUFFER, 0);
130 
131     // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.
132     //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
133 
134     // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
135     // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
136     glBindVertexArray(0);
137 
138 
139     // uncomment this call to draw in wireframe polygons.
140     // 画成线框
141     glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
142 
143     // render loop
144     // -----------
145     while (!glfwWindowShouldClose(window))
146     {
147         // input
148         // -----
149         processInput(window);
150 
151         // render
152         // ------
153         glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
154         glClear(GL_COLOR_BUFFER_BIT);
155 
156         // draw our first triangle
157         glUseProgram(shaderProgram);
158         glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
159         //glDrawArrays(GL_TRIANGLES, 0, 6);
160         glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
161         // glBindVertexArray(0); // no need to unbind it every time 
162 
163         // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
164         // -------------------------------------------------------------------------------
165         glfwSwapBuffers(window);
166         glfwPollEvents();
167     }
168 
169     // optional: de-allocate all resources once they've outlived their purpose:
170     // ------------------------------------------------------------------------
171     glDeleteVertexArrays(1, &VAO);
172     glDeleteBuffers(1, &VBO);
173     glDeleteBuffers(1, &EBO);
174     glDeleteProgram(shaderProgram);
175 
176     // glfw: terminate, clearing all previously allocated GLFW resources.
177     // ------------------------------------------------------------------
178     glfwTerminate();
179     return 0;
180 }
181 
182 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
183 // ---------------------------------------------------------------------------------------------------------
184 void processInput(GLFWwindow *window)
185 {
186     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
187         glfwSetWindowShouldClose(window, true);
188 }
189 
190 // glfw: whenever the window size changed (by OS or user resize) this callback function executes
191 // ---------------------------------------------------------------------------------------------
192 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
193 {
194     // make sure the viewport matches the new window dimensions; note that width and 
195     // height will be significantly larger than specified on retina displays.
196     glViewport(0, 0, width, height);
197 }
View Code