In OpenGL, everything is in 3D space, whereas screens and Windows are 2D arrays of pixels, so much of OpenGL’s work is about converting 3D coordinates into 2D pixels that fit your screen. The process of converting 3D coordinates to 2D coordinates is managed by OpenGL’s graphics rendering pipeline.

Learning Objectives:

  • Basic knowledge of graphics rendering pipeline;
  • Workflow of graphic rendering pipeline;

Basic knowledge of graphics rendering pipeline

In OpenGL, everything is in 3D space, and screens and Windows and so on are 2D pixel sets, so you need to convert.

  • Concept:

    • A bunch of raw graphic data passing throughA delivery lineDuring which it is processed through various changes and finally appears on the screen.
    • (The graphics rendering pipeline takes a set of 3D coordinates and turns them into colored 2D pixel output on your screen.)
  • The graphics rendering pipeline works in two main ways:

    • Convert 3D coordinates to 2D coordinates;
    • Convert 2D coordinates into actual colored pixels;

Workflow of graphics rendering pipeline

  1. Vertex shader:Vertex DataAn array (3D coordinates) is used as input data to the graphics rendering pipeline to convert 3D coordinates to another 3D coordinate.
  2. Primitive Assembly: takes the output of the vertex shader as a vertex input and adds all verticesAssembles to the specified primitive shape.
  3. Geometric shader: the output of the primitive assembly stage (the vertex set) is generated as its inputNew vertices construct new primitives to generate other shapes.
  4. Rasterization stage: geometry shader output as its input parameter,Map primitives to corresponding pixels on the final screenGenerated for use by the fragment shader. It is executed at this stageClippingAll pixels outside the view are discarded to improve execution efficiency.
  5. Fragment shader:Calculates the final color of a pixel. The fragment shader contains3D scene data (lighting, shadows, light colors, etc.)This data can be used to calculate the color of the final pixel.
  6. Alpha test and Blending: Tests the depth and Stencil values of a piece to determine whether the pixel is in front of or behind other objects and to determine whether to discard it. Detect the alpha value (which defines the transparency of the object) and Blend the object.

The graphics rendering pipeline is very complex and contains many configurable parts, all we need to do is configure vertex and fragment shaders. Next we’ll learn how to write vertex and fragment shaders in openGL programs.

Writing shader program main steps:

1. Vertex input 2. Vertex shader -- compiler shader 3. Fragment shader -- compiler shader 4. Link shader to shader program object 5. Link vertex propertiesCopy the code
  • Vertex input

Vertex data is a set of vertices. A Vertex is a collection of data in 3D coordinates. Vertex data is represented by Vertex attributes.

  1. Define vertext data (3D coordinates), standardize the vertex data coordinates for device coordinates (valid range — visible area of OpenGL);

    • Normalized Device Coordnates
      • Any coordinates that are located outside the range are discarded/cropped and will not be displayed on the screen.
      • Normalized device Coordinates will then be transformed to screent-space Coordinates [viewport transform via data provided by the glViewport function]
  2. The vertex data is sent as input to the first processing phase of the graphics rendering pipeline: the vertex shader.

  3. The vertex data is stored in the graphics card memory, using VBO (vertex buffer object) management.

    • The reason for using vertex buffer objects to manage memory is that you can send a lot of data to the graphics card at once.
    • Vertex Buffer Objects (VBO) manage memory — store a large amount of vertex data in the GPU.
    • Vertex buffer object creation steps:

      1. Create a VBO object; glGenBuffer()
      2. Bind the VBO object to the vertex buffer object; glBindBuffer()
      3. Copy user input vertex data into buffer memory; glBufferData()
      • Key points:
        • OpenGL has many buffer object types, among which the vertex buffer object type is GL_ARRAY_BUFFER

        • GlBufferData function – copies vertex data (user-defined data) into cached memory.

        • GlBufferData: The fourth parameter, which specifies how the graphics card manages the given data:

          1. GL_STATIC_DRAW: data will not or will not change;
          2. GL_DYNAMIC_DRAW: data will be changed multiple times;
          3. GL_STREAM_DRAW: Data changes each time it is drawn;
          // Create a vBO object
          unsigned int VBO;/ / VBO object id
          glGenBuffer(1,&VBO);
          
          // Bind the newly created buffer to the GL_ARRAY_BUFFER target
          //GL_ARRAY_BUFFER-- Buffer type of vertex buffer object
          glBindBuffer(GL_ARRAY_BUFFER,VBO);
          
          // Configure the current binding buffer (VBO) -- call the glBufferData function
          glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW)
          Copy the code
  • Vertex shader

    • Modern OpenGL requires at least one vertex and one fragment shader.

    • Write the vertex shader using GLSL (OpenGL Shading Language), then compile the shader and use it in the program.


    • Create a vertex shader

      #version 330 core
      layout (location=0) in vec 3 aPos;
      void main(a){
          gl_Position=vec4(aPos.x,aPos.y,aPos.z,1.0)}Copy the code
      1. Shader versions OpenGL 3.3 and later, using core mode.
      2. In keyword — represents the Input Vertex Attribute, data location (layout (location=0)), using vec3 3d floating point vector.
      3. Set the output of the vertex shader.
    • Compiler shader

    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";
    Copy the code

    In order to use it with OpenGL, its source code must be compiled dynamically at run time.

    • Compilation steps:
      1. Create a shader object and reference it with an ID; — glCreateShader(GL_VERTEX_SHADER);
      2. Append the shader source code to the shader object. — glShadderSource();
      3. Compiler shader — glCompileShader(vertexShader);
      4. Check the compile state — glGetShaderiv()
      ```c unsigned int vertexShader; vertexShader=glClreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader,1,&vertexShader,NULL); 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; } ` ` `Copy the code
  • Fragment Shader

    • Calculates the final color output of the pixel.

    • Color is represented in computer graphics as an array of four elements: red, green, blue, and alpha(transparency) components, often abbreviated RGBA. When defining a color in OpenGL or GLSL, we set the intensity of each component of the color to between 0.0 and 1.0

    • Different combinations of three color components can produce more than 16 million different colors!

    • Creation steps:

      1. Create fragment shader source code
           #version 330 core out vec4 FragColor; 
           void main(a) 
           { 
               FragColor = vec4(1.0 f.0.5 f.0.2 f.1.0 f); 
           }
      Copy the code
      1. Compile fragment shader
         unsigned int fragmentShader;
         fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 
         glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); 
         glCompileShader(fragmentShader);
      Copy the code
  • Shader Program Object

    • After combining multiple shadersLinks (link)For a shader program object, and then when rendering the object, activate the shader program.
      • Note: When linking shaders to a program, the output of each shader is linked to the input of the next shader. Links fail when input and output do not match.
    unisigned int shaderProgram;
    shaderProgram=glCreateProgram();
    
    
    glAttrachShader(shaderProgram,vertexShader);
    glAttrachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    
    glUserProgram(shaderProgram);
    Copy the code
    • Input vertex data has been sent to the GPU, with instructions on how to process it in vertex and fragment shaders.

    • OpenGL doesn’t know how to interpret vertex data in memory or how it should link vertex data to vertex shader properties;

  • Link vertex properties (parse vertex data)

    // Set the vertex property pointer
    glVertexAttribPointer(0.3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0); // Enable vertex properties
    Copy the code
    • GlVertexAttribPointer — Argument:
      • Layout (Location = 0) — Layout (Location = 0)
      • The size of the vertex property – is onevec3It’s made up of three values, so it has size 3
      • Specifies the type of data
      • Whether you want data to be normalized — set toGL_TRUE, all data will be mapped between 0 and 1;
      • Stride — The interval between successive sets of vertex attributes
      • The Offset of the starting position of the data in the buffer.
    • Vertex attributes are disabled by default
      • Enable – glEnableVertexAttribArray vertex attribute
  • Vertex property calls are stored in the VAO. The advantage is that when configuring Vertex property Pointers, you only need to execute those calls once, and then bind the corresponding VAO to draw the Object.

    • The creation of VAO
    unsigned int VAO;
    glGenVertexArrays(1,&VAO);
    
    / / VAO use
    glBindVertexArray(VAO);
    
    Copy the code
  • draw

    • GlDrawArrays (GL_TRIANGLES, 0, 3);
    • Parameters:
      1. Specify different primitives, such as GL_POINTS;
      2. Specifies theThe starting index of the vertex array;
      3. Number of drawing points;
  • Index Buffer Object (Element Buffer Object,EBO)

    • EBO is also a buffer for storing indexes. You can decide which vertices to draw by calling the index of the vertices in OpenGL. — To reduce the number of vertices created (to reduce duplicate data).
      
      float vertices[] = {
          0.5 f.0.5 f.0.0 f./ / the top right corner
          0.5 f.0.5 f.0.0 f./ / the bottom right hand corner
          0.5 f.0.5 f.0.0 f./ / the bottom left corner
          0.5 f.0.5 f.0.0 f / / the top left corner
      }; 
      
      unsigned int indices[] = { // Note that the index starts at 0!
          0.1.3.// The first triangle
          1.2.3 // The second triangle}; To drawCopy the code
    • Create index buffer object – drawn using glDrawElements
      unsigned int EBO; 
      glGenBuffers(1, &EBO);
      
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); 
      
      // Copy the index to the buffer
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
      glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
      Copy the code