OpenGL learning notes I
Refer to the official website Hello Triangle
The purpose of this note is to refine the key content and better master the content of opengl in combination with your own understanding and translation
1, Render pipeline
The division of Graphics Pipeline (mostly translated as pipeline, which actually refers to the process that a pile of original graphics data passes through a transmission pipeline and finally appears on the screen after various changes) is roughly as follows:
The functions of each part are as follows:
-
The main function of vertex shader is to transform 3D coordinates into another kind of 3D coordinates
At the same time, the coordinates are transformed. -
Primitive assembly takes the output of vertex shader as input to assemble basic primitives (triangles, etc.).
-
Geometry shader generates new primitives that are used in the new geometry.
-
Rasterization stage maps primitives into pixels to generate fragments for fragment shader shading. Before fragment shader runs, invisible parts will be clipped to improve rendering efficiency.
-
The fragment shader calculates the final color of pixels, which is also the place where all OpenGL advanced effects are produced. It will calculate the final pixel value using the data in the 3D scene (illumination, shadow, light color and so on).
-
Alpha testing and blending. Alpha is transparency, which is used for perspective division.
A rendering pipeline should have at least two parts: vertex shader and fragment shader. Vertex shader is used to construct the most basic points, and fragment shader is used to directly output pixels. The middle part involving vertex changes can be ignored directly, and there can be no 3D scene, lighting and other information.
2, Normalized device coordinate
I don't think the official translation of normalized device coordinate here is very good. Normalized translation is much easier to understand. This coordinate refers to that the coordinate input of OpenGL must be between [- 1.0f, +1.0f], such as the following three points:
float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f };
3, Vectors in vertex shaders
The vector in GLSL has four dimensions rgbw, w is used for perspective division;
rgbw is data of float type. A float is 4 bytes, so a vertex in the vertex shader needs 12 bytes. There is no redundancy between vertex data, which is tightly packed, and there is no gap between values. If you use c language a lot, you will smile when you read it. This is the usual routine of void * and buffer in c language. In fact, it is true, When I read the following opengl syntax about the use of int * and buffer processing, I found that the memory processing idea of c language has played a great role in opengl data processing.
4, Draw triangle
I'll put out the codes in this part together with the codes in the exercises after class. The notes are detailed. Please see the notes for details:
Core function main function, which provides functions to draw triangles, draw rectangles (drawSquare()), draw two independent triangles (drawTwoTriangles()), and create different shader programs to draw two triangles with different colors (drawTwoTrianglesWithDifferentColor()).
#include <iostream> #include <glad/glad.h> #include <GLFW/glfw3.h> #pragma comment(lib,"glfw3.lib") using namespace std; float vertices[]; void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow* window); unsigned int creatShaderProc(); void drawTriangle(); //Basic triangle drawing void drawSquare(); //Draw a rectangle void drawTwoTriangles(); //Draw two triangles unsigned int creatShaderProcOrange(); //Create an orange Shader program void drawTwoTrianglesWithDifferentColor(); //Draw two triangles of different colors int main() { glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //Set glfw maximum version glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//Set glfw minimum version glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Set to core mode //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); //create a window GLFWwindow* window = glfwCreateWindow(600, 600, "LearnOpenGL", NULL, NULL); if (window == NULL) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); //Set as current window glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //The callback function of the current window to change the window size //Initialize glad to facilitate glfs function call if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } glViewport(0, 0, 200, 300); //Set the position and size of the display area //Render Loop while (!glfwWindowShouldClose(window)) { processInput(window); //Detect the input and handle it accordingly //Rendering instructions //Clear screen glClearColor(0.1f, 0.1f, 0.1f, 1.0f); //Status setting function: set the screen color after emptying, which is dark blue-green at this time glClear(GL_COLOR_BUFFER_BIT); //Status use function: only clear the color buffer, and other buffer bit parameters include GL_DEPTH_BUFFER_BIT and GL_STENCIL_BUFFER_BIT //drawSquare(); // Normal splicing of two triangles //drawTwoTriangles(); // Two separate triangles drawTwoTrianglesWithDifferentColor(); //Two different colors of triangles glfwSwapBuffers(window); //Swap the color buffer and display it to the window glfwPollEvents(); //Check the trigger event, update the window status, and call the corresponding callback function } glfwTerminate(); //Release / delete allocated resources return 0; } //Callback function when changing window size void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } //Check the input and whether there is a key press void processInput(GLFWwindow* window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) //If the user does not press ESC, return to GLFW_RELEASE glfwSetWindowShouldClose(window, true); } //Create shader programs, create multiple shaders and link them unsigned int creatShaderProc() { //Create vertex shader, vertex shader source code //GLSL language GLSL330 corresponds to opengl3 Version 3 core mode //Multiline string R "(...)" "Medium" and (cannot have interval) //layout position in: input vec3: 3D vector apos: variable name const char ShaderSource[] = R"( #version 330 core layout(location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); } )"; const GLchar* vertexShaderSource = (GLchar*)&ShaderSource; //The glShaderSource function specifies the data of this type when specifying the shader source code //Compiling vertex shaders unsigned int vertexShader; vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); //Detect compilation results 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; } /// //Create clip shader const char fragSource[] = R"( #version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0f, 0.2f, 0.2f, 1.0f); } )"; const GLchar* fragShaderSource = (GLchar*)fragSource; unsigned int fragmentShader; fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragShaderSource, NULL); glCompileShader(fragmentShader); /// //Link shader unsigned int shaderProgram; shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); //Attach shader to program object glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); //Check whether the link is successful glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); cout << "shader link to program failed!" << infoLog << endl; } return shaderProgram; } ///Render a triangle using a VAO vertex array object void drawTriangle() { //Vertex 3D coordinates float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; unsigned int VBO; glGenBuffers(1, &VBO); //1: To generate the number of buffer objects, VBO: the array used to store the buffer objects //1. Copy the vertex array to the buffer for opengl // glBindBuffer(GL_ARRAY_BUFFER, VBO); // Multiple buffers of different types can be bound at the same time // Copy the data to the memory of the current binding buffer, and specify the form of graphics card management cache, STATIC,DYNAMIC,STREAM // glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); /*2.Set vertex attribute pointer For the glVertexAttribPointer function, the parameters are: location value, vertex attribute size (dimension), Data type, whether it is standardized (mapped to 0 or - 1 to 1), step size (interval size between continuous vertex attribute groups), The offset of the position data from the buffer start position */ // From the currently bound GL_ ARRAY_ Get the data from VBO in buffer, and the vertex attribute 0 will now be linked to his vertex data // glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // glEnableVertexAttribArray(0); // Take the vertex attribute value as the parameter, enable the vertex attribute, and the vertex attribute is disabled by default //3. Use shader program // glUseProgram(shaderProgram); // Activate program object // glDeleteShader(vertexShader); // The shader object is no longer used. Delete it // glDeleteShader(fragmentShader); // 3. Draw objects //someOpenGLFunctionThatDrawsOurTriangle(); /*Save vertex attribute pointer configuration with VAO vertex array object glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);*/ unsigned int VAO; glGenVertexArrays(1, &VAO); //1. Bind VAO glBindVertexArray(VAO); //2. Bind VBO and read it into the buffer, and specify how the graphics card manages the cache glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //3. Set vertex attribute pointer glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); //... //4. Draw objects unsigned int shaderProgram = creatShaderProc(); glUseProgram(shaderProgram); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3); //GL_TRIANGLE: type of entity to draw 0: starting index of vertex array, 3: number of vertices to draw } //Render a rectangle with EBO index buffer void drawSquare() { //Introduce index buffer float vertices[] = { //vertex data 0.5f, 0.5f, 0.0f, // Upper right corner 0.5f, -0.5f, 0.0f, // Lower right corner -0.5f, -0.5f, 0.0f, // lower left quarter -0.5f, 0.5f, 0.0f // top left corner }; unsigned int indices[] = { //Note that the index data starts from 0! 0, 2, 3, // First triangle 0, 1, 2 // Second triangle }; unsigned int VAO, VBO, EBO; //Bind VAO, VBO and EBO. VAO will store the call of VBO. Unbind VBO before unbinding VAO, otherwise the storage will be gone glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //Set vertex attribute pointer glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); unsigned int shaderProgram = creatShaderProc(); //Render Loop glUseProgram(shaderProgram); glBindVertexArray(VAO); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //Wireframe mode polygon: polygon GL_FRONT_AND_BACK: applied to the front and back of the drawing_ Line: draw with line glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); //glDrawArrays(GL_TRIANGLES, 0, 6); // set the count to 6 since we're drawing 6 vertices now (2 triangles); not 3! glBindVertexArray(0); } void drawTwoTriangles() { //Draw a triangle with two vertices next to each other //Introduce index buffer float vertices[] = { //vertex data // first triangle -0.9f, -0.5f, 0.0f, // left -0.0f, -0.5f, 0.0f, // right -0.45f, 0.5f, 0.0f, // top // second triangle 0.0f, -0.5f, 0.0f, // left 0.9f, -0.5f, 0.0f, // right 0.45f, 0.2f, 0.0f // top }; unsigned int VAO, VBO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //Set vertex attribute pointer glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); unsigned int shaderProgram = creatShaderProc(); //Render Loop glUseProgram(shaderProgram); glBindVertexArray(VAO); //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // Wireframe mode polygon: polygon GL_ FRONT_ AND_ Back: applied to the front and back of the drawing_ Line: draw with line //Draw using buffer instead of element glDrawArrays(GL_TRIANGLES, 0, 6); // set the count to 6 since we're drawing 6 vertices now (2 triangles); not 3! glBindVertexArray(0); } //Draw different colors 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* fragmentShader1Source = "#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* fragmentShader2Source = "#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"; //Create shader programs, create multiple shaders and link them unsigned int creatShaderProcOrange() { //Create vertex shader, vertex shader source code //GLSL language GLSL330 corresponds to opengl3 Version 3 core mode //Multiline string R "(...)" "Medium" and (cannot have interval) //layout position in: input vec3: 3D vector apos: variable name const char ShaderSource[] = R"( #version 330 core layout(location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); } )"; const GLchar* vertexShaderSource = (GLchar*)&ShaderSource; //The glShaderSource function specifies the data of this type when specifying the shader source code //Compiling vertex shaders unsigned int vertexShader; vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); //Detect compilation results 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; } /// //Create clip shader const char fragSource[] = R"( #version 330 core out vec4 FragColor; void main() { FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); } )"; const GLchar* fragShaderSource = (GLchar*)fragSource; unsigned int fragmentShader; fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragShaderSource, NULL); glCompileShader(fragmentShader); /// //Link shader unsigned int shaderProgram; shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); //Attach shader to program object glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); //Check whether the link is successful glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); cout << "shader link to program failed!" << infoLog << endl; } return shaderProgram; } void drawTwoTrianglesWithDifferentColor() { //Draw a triangle with two vertices next to each other //Introduce index buffer float vertices0[] = { //vertex data // first triangle -0.9f, -0.5f, 0.0f, // left -0.0f, -0.5f, 0.0f, // right -0.45f, 0.5f, 0.0f, // top }; float vertices1[] = { //vertex data // second triangle 0.0f, -0.5f, 0.0f, // left 0.9f, -0.5f, 0.0f, // right 0.45f, 0.2f, 0.0f // top }; unsigned int VAOs[2], VBOs[2]; glGenVertexArrays(2, VAOs); glGenBuffers(2, VBOs); glBindVertexArray(VAOs[0]); glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices0), vertices0, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // Vertex attributes stay the same glEnableVertexAttribArray(0); glBindVertexArray(VAOs[1]); glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); // Vertex attributes stay the same glEnableVertexAttribArray(0); unsigned int shaderProgram = creatShaderProc(); unsigned int shaderProgramOrange = creatShaderProcOrange(); glUseProgram(shaderProgram); glBindVertexArray(VAOs[0]); glDrawArrays(GL_TRIANGLES, 0, 3); glUseProgram(shaderProgramOrange); glBindVertexArray(VAOs[1]); glDrawArrays(GL_TRIANGLES, 0, 3); }