First remember a few nouns
- Vertex Array Object, VAO
- Vertex Buffer Object, VBO
- Index Buffer Objects: Element Buffer Object, EBO or Index Buffer Object, IBO
Some conceptual points
- Everything in OpenGL is in 3D space. Screens and windows are arrays of 2D pixels. Most of OpenGL's work is to change 3D coordinates into 2D pixels that fit on the screen.
- The 3D to 2D processing is managed by the graphics rendering pipeline.
Pipeline: A process in which a stack of raw graphical data passes through a pipeline and changes are processed to make it appear on the screen.
Change handling: 3D coordinates to 2D coordinates, 2D coordinates to colored pixels
Coordinates: Accurately represents the position of a point in 2D space
Pixel: Approximate value of this point (should be understood as approximate location, pixel fills coordinates)
Graphic Rendering Pipeline (Pipeline)
- Function: 3D Coordinates - > Colored 2D Pixels
- Shader: Quick data processing ()
- Phase: Vertex Data->Vertex Shader->Shape (Primitive) Assembly->Geometry Shader->Rasterization->Fragment Shader->Testing and Mixing
Vertex data: vertex data is entered into the vertex shader
Vertex shader: 3D coordinates - > another 3D coordinate
Shape assembly: vertex shader vertex - > assembly to specified meta shape
Is the data rendered as a series of points, a triangle, or a long line? GL_POINTS, GL_TRIANGLES, GL_LINE_STRIP
Geometric Shader: A series of points in the form of primitives - > Generate new vertices to construct new primitives
Rasterization: Primitives are mapped to pixels on the final screen to generate segments for use by the segment shader
Fragment Shader: Calculate Final Color
It is also where all OpenGL advanced effects come into being. Data that usually contains 3D scenes (light, shadow, color of light) will be used to calculate the color of the final pixel
Test and Blend: Blend fragments, transparency, order of coverage, etc.
Preliminary usage process
- Define at least one vertex shader
- You can use the default geometry shader
- Define at least one segment shader
Coding this process:
- Enter some vertex data for OpenGL. (3D coordinates)
About coordinates
float vertices[] = { /* X Axis, Y-Axis, Z-Axis (plane graphics do not require Z-Axis) */ -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f };
- Vertex data passed to vertex shader
Through the vertex buffer object, a large amount of data is sent to the graphics card at one time. After the data is sent to the memory on the graphics card, the vertex shader can immediately access the vertices.
//Generate a VBO object using the glGenBuffers function and a buffer ID unsigned int VBO; glGenBuffers(1, &VBO); //Binding Buffer Object Type GL_ARRAY_BUFFER is vertex buffer type glBindBuffer(GL_ARRAY_BUFFER, VBO); //Assign vertex data to buffer memory glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); /* glBufferData Is a function designed to copy user-defined data to the current binding buffer Parameter 1: Target buffer type GL_ARRAY_BUFFER is vertex buffer type Parameter 2: Specify the size of the transmitted data in bytes Parameter 3: The actual data you want to send Parameter 4: Specifies how we want the graphics card to manage the given data GL_STATIC_DRAW : The data will not or will hardly change. GL_DYNAMIC_DRAW: The data will change a lot. GL_STREAM_DRAW : The data changes every time it is drawn. */
- Write vertex shader (vertex shader language)
- Version declaration
- In keyword, declares all input vertex properties in the vertex shader, the location value of the location variable
- Place inside a function
- Location data is assigned to a predefined gl_Position variable
#version 330 core layout (location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); }
- Compile Vertex Shader
- Temporarily hard-code the above code into a code file C-style string
- Create a shader object, referenced by ID, so the type is unsigned int
- Provide the type of shader you need to create to glCreateShader, vertex shader GL_VERTEX_SHADER
- Attach shader source to shader object
- Compile
- Check compilation success
//*Temporarily hard-code the above code in the code file**C-style string** 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"; //Create a shader object, referenced by ID, so the type is unsigned int unsigned int vertexShader; //Provide the type of shader you need to create to glCreateShader, vertex shader GL_VERTEX_SHADER vertexShader = glCreateShader(GL_VERTEX_SHADER); //Attach shader source to shader object //glShaderSource function //The first parameter is the shader object to be compiled as. //The second parameter specifies the number of source strings passed, where there is only one. //The third parameter is the vertex shader's true source code //The fourth parameter is set to NULL first. glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); //Compile glCompileShader(vertexShader); //Check compilation success int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); //Print information if unsuccessful if(!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; }
- Write fragment shaders, compile shaders
#version 330 core out vec4 FragColor; void main() { // R G B Transparency FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); } 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" "}\0"; unsigned int fragmentShader; //GL_FRAGMENT_SHADER Fragment Shader fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader);
- Final Link to Shader Program Object
//Create Program Object unsigned int shaderProgram; shaderProgram = glCreateProgram(); //Link Shader //Additional vertex shader glAttachShader(shaderProgram, vertexShader); //Additional Fragment Shader glAttachShader(shaderProgram, fragmentShader); //link glLinkProgram(shaderProgram); //Determine whether a link is successful or not int success; char infoLog[512]; glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if(!success) { glGetShaderInfoLog(shaderProgram, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } //Activator Object glUseProgram(shaderProgram); //After the glUseProgram function call, this program object is used in every shader call and rendering call //Delete shader object after program call glDeleteShader(vertexShader); glDeleteShader(fragmentShader);
- Link Vertex Properties (specify how OpenGL interprets vertex data before rendering)
*Use the glVertexAttribPointer function to tell OpenGL how to parse vertex data (applied to vertex-by-vertex attributes)
/* The first parameter specifies the vertex properties that we want to configure. Remember that we used layout(location = 0) in the vertex shader to define the location value of the position vertex attribute? It can set the position value of the vertex property to zero. Because we want to pass data to this vertex attribute, here we pass in 0. The second parameter specifies the size of the vertex attribute. The vertex attribute is a vec3, which consists of three values, so the size is 3. The third parameter specifies the type of data, here is GL_ FLOAT (vec* in GLSL is composed of floating point values). The next parameter defines whether we want the data to be standardized (Normalize). If we set it to GL_TRUE, all data is mapped between 0 (for signed data, -1) and 1. We set it to GL_FALSE. The fifth parameter, called Stride, tells us the interval between consecutive sets of vertex attributes. Since the next set of location data is after three floats, we set the step size to 3 * sizeof(float). Note that because we know this array is tightly aligned (there is no gap between the two vertex attributes), we can also set it to 0 to allow OpenGL to determine the exact step size (only available if the values are tightly aligned). Once we have more vertex attributes, we have to define the interval between each vertex attribute more carefully, and we will see more examples later (Note: This parameter simply means how many bytes are between the second occurrence of this attribute and the entire array 0). The last parameter is of type void*, so we need to do this strange cast. It represents the Offset of the starting position of the location data in the buffer. Since the location data is at the beginning of the array, this is 0. We will explain this parameter in more detail later. */ //Set resolution glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); //Start Vertex Properties glEnableVertexAttribArray(0);
- Comprehensive Coding
We used a vertex buffer object to initialize the vertex data into the buffer, established a vertex and a segment shader, and showed OpenGL how to link the vertex data to the vertex properties of the vertex shader. Draw an object in OpenGL, and the code looks like this:
// 0. Copy the vertex array to the buffer for OpenGL use glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 1. Set vertex property pointer glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 2. Use the shader program when rendering an object glUseProgram(shaderProgram); // 3. Draw objects someOpenGLFunctionThatDrawsOurTriangle();
Index Buffer Object (EBO)
Increase indexing while keeping VAO and VBO unchanged
float vertices[] = { 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 starts at 0! 0, 1, 3, // First triangle 1, 2, 3 // Second triangle }; unsigned int EBO; glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
.