OpenGL study notes, "two" draw simple shapes

  Before you start drawing, simply look opengl drawing process. In opengl inside, all things are in 3D space, and we are 2D pixel screen and performance, so we need to convert 3D to 2D. opengl internal management of this process is called rendering pipeline, divided into two parts: 3D coordinates into 2D coordinates, pixel data of coordinates into 2D colored. Divided into 6 segments, the processes / steps, each process independently, to process data input and output as the data transfer mode. Each process / step, a short program / code components, so they can be called Shader / shader program. Gpu efficiency and procedures for these fragments is very high, and since the number of multi-core gpu, the amount may be executed concurrently is also very large.

  These six steps are as follows:

 

   Blue logo image above steps, the steps that we usually do shader programming. But now we are mainly doing the first step (Vertex Shader Vertex Shader) and fifth step (Fragment Shader fragment shaders) programming of these two steps. Vertex data input of the vertex shader processing, coordinate conversion is good, the process proceeds through the fragment shader rasterized pixel data to produce the final displayed color information.

  We want to draw on the screen a simple graphical, then you must provide the vertex shaders and fragment shaders in order to ultimately show style, color graphics normal.

1, a simple shader program

  First Vertex Shader, vertex shader program code

const char* vertexShaderSource = "#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "void main()\n{"
        "    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
        "}\0";

  Opengl version specified in the code, and then specify the type of input variables in vec3, this is our vertex data input, noting that here we use the additional layout (location = 0), this will be mentioned later. Then is the core shader programs main function, and here we give the built-in variable opengl gl_Position assignment that we pass over the vertex position attribute values.

  Followed Fragment Shader, fragment shader program code

const char* fragmentShaderSource = "#version 330 core\n"
        "out vec4 FragColor;\n"
        "void main()\n{"
        "    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
        "}\0";

  The same need to specify the version of opengl, then declare an out vec4 type variable, the output data represents a shader, that is a fragment shader processing pixel data of a good color values, without this return, we will be drawing out It will be blank or black.

  Then we need to link to a shader program according to the program code is compiled

   // build and compile shader
    // --------------------------------
    // create vertex shader
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    // attach shader source
    glShaderSource(vertexShader, 1, &vSource, nullptr);
    // compile shader
    glCompileShader(vertexShader);
    // check error
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // create fragment shader
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fSource, nullptr);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    // build and link shaderprogram
    // --------------------------------
    // create shader program
    unsigned int shaderProgram = glCreateProgram();
    // attach shader and link program
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    // check error
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
        std::cout << "ERROR::PROGRAM::ATTACH_LINK_FAILED\n" << infoLog << std::endl;
    }
    // delete shader 
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

  Create and compile shaders processes are similar:

  glCreateShader according to the parameter types, return types of shader we want to create;

  glShaderSource shader code provided bound to the shader;

  glCompileShader compile our shaders;

  glGetShaderiv can check our compiled results;

   After a good shader compiler, we need to create shader programs:

  glCreateProgram create a shader program returns an unsigned int type of index ID, we need to use in the back;

  glAttachShader will we compiled shader attached to the shader programs;

  glLinkProgram good links shader programs;

  glGetProgramiv can check our link results.

  Finally, we need to remove the compiled shader by glDeleteShader method.

  We finally need to use that method returns the index ID glCreateProgram shader programs.

2, the vertex data provided

  The above flowchart can be seen, the first step is input to the vertex data is processed, so we need to provide vertex data. Vertex data includes a plurality of types of data of the vertex position, color, texture coordinates, etc., here we simply to provide location data, if the color of one color is fixed directly in the fragment shader.

  We can provide vertex data one at a time, but this approach is too slow. Opengl has a previous list of Display concept, a package provides a set of data, so that high efficiency, but since the data is stored directly in the end of the GPU, the data can not be provided once the readjustment. Later, promotes the concept of VBO (vertex buffer object), but also a collection / packed a set of data, but compared to the Display List, data is collected at the CPU side, each rendering will pass once again. So this way compared to the first vertex data to provide a more efficient, but compared to the Display List mode, efficiency is slightly lower, but improved flexibility.

  So we can first declare an array of vertex data, and then bind to the target VBO:

float vertices[] = {
        -1.0f, -0.5f, 0.0f, // left
        0.0f, -0.5f, 0.0f, // right
        -0.5f, 0.5f, 0.0f // top
    };
unsigned int VBO;
glGenBuffers(1, &VBO);
... loop
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glUseProgram(shaderProgram); 
...draw
...loop

  glBindBuffer and glBufferData two methods, and a method of binding the vertex data assigned VBO VBO and objects. GPU then need to know how to use these data, glVertexAttribPointer method is responsible for GPU to know how to use the data.

  glVertexAttribPointer parameter corresponding to a property we mentioned in the vertex shader (layout = 0), the 2 parameters represents the number of data used in this attribute value (we used herein is the position of attributes, each of the three floats point data configuration), 3 indicates the data type parameter (here used is a float), 4 parameter indicating whether the data type conversion, parameter 5 represents the interval between each two data attributes (here since each attribute it is between the values ​​immediately, so the amount of data that a spacing attribute), 6 represents a start index parameter attribute data (here we fill 0, later it comes to a plurality of vertex attributes, the place of value will change);

  GlEnableVertexAttribArray method parameter, which specifies the activation properties of our configuration, I mentioned in the above configuration is (layout = 0) attributes, so here fill 0;

  In the above code, we can see that in the rendering loop, we have to call bind VBO, assignment VBO, set the data using the methods of the interface, a bit complicated. This time we need to introduce another concept vertex array object (VAO), to simplify our operations. VAO concept is used to record our binding, VBO assignment target, method of use setting data, after the introduction of VAO, we can adjust the rendering process: unsigned int VBO, VAO;     glGenVertexArrays ( . 1 , & VAO);

    glGenBuffers(1, &VBO);
    // bind the vertex array object
    glBindVertexArray(VAO);
    // bind the vertex buffer object
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    // fill data uage:GL_STATIC_DRAW, GL_DYNAMIC_DRAW, GL_STREAM_DRAW
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // tell opengl how it should interpret the vertex data
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
   ...loop
   glUseProgram(shaderProgram);
   glBindVertexArray(VAO);
   ...draw
   ...loop
   glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);

  You can see, our rendering process becomes simple data initialization and configuration, placed outside of the rendering process, rendering the time, only you need to bind VAO object, you can perform graphics operations directly. Of course, after the loop exits, we have freed VAO and VBO objects.

3, draw the first triangle

  In the above, we offer a vertex data contained three vertex position, created a shader program also introduces the concept of VAO, simplifies our rendering process. opengl drawing provides a simple method for our use, where we need to draw a triangle, we can use the following code to the drawing:

  glDrawArrays(GL_TRIANGLES, 0, 3);

  We draw a triangle, we first parameter type is filled triangles; second parameter specifies the starting position of the vertex data in the array, where we fill 0; third parameter indicates the number of points to be drawn, here we have three vertices, we pass 3 on it. Compiler implementation of our project, you can get a simple triangle:

 

 4, draw a rectangle

  In the above, we draw a triangle, and now we have to draw a rectangle, what to do?

  We know that a rectangle can be achieved by drawing two triangles, so we can change what our array of vertex data, vertex data provide two triangles

float vertices[] = {
    // first triangle
     0.5f,  0.5f, 0.0f,  // top right
     0.5f, -0.5f, 0.0f,  // bottom right
    -0.5f,  0.5f, 0.0f,  // top left 
    // second triangle
     0.5f, -0.5f, 0.0f,  // bottom right
    -0.5f, -0.5f, 0.0f,  // bottom left
    -0.5f,  0.5f, 0.0f   // top left
}; 

  We then modify the mapping function, the number of vertices to 6, we obtain a rectangle.

  But let's look at an array of data and found that in fact there is a repeat of such lower right corner of the triangle and the second lower-right corner of the first triangle. If the number of the rectangle to be drawn less have little effect, if the number of rectangles more, then we will have a lot of duplicate data, will take up extra memory, but also caused the project data complicated. So at this point we need to introduce another concept element buffer object (EBO) object to simplify our operations, this object is also translated in Chinese as the vertex index object. As the name suggests, this is a vertex index number, which tells how to use the GPU vertex data we provide.

  

   // indice data
    unsigned int indices[] = {  // note that we start from 0!
        0, 1, 3,   // first triangle
        1, 2, 3    // second triangle
    };
   unsigned int VAO, VBO, EBO;
    // gen
    glGenBuffers(1, &EBO);
    // bind VAO
    // bind VBO
    // bind EBO
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // set pointer
    ... render loop
    ... render loop
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);

  It simplifies the process. We need to provide a list of the index, the corresponding index is the vertex vertex data. EBO objects and then bind the assignment, with the rest of the operation is similar to before, compiler implementation, we get a simple rectangle.

 

  Here we need to note that, in the last delete VAO, VBO, EBO, they must not delete EBO VAO before deleting the object, because the object is actually inside the VAO EBO maintains a data object, if the first deleted EBO It will result in the removal abnormal.

  These are the simple graphical interface to use opoengl provided, draw.

  Here is the project's code.

 

Guess you like

Origin www.cnblogs.com/zhong-dev/p/11598177.html