OpenGL shader program analysis -- index drawing

Reprinted from: https://blog.csdn.net/cordova/article/details/52564074

background

OpenGL provides several drawing functions, among which glDrawArrays() we used earlier is a function of sequential drawing . Sequential drawing is to scan every vertex of all primitives in the vertex buffer in turn from the specified offset, which is very simple and easy to use, but the disadvantage is that if a vertex is a common vertex of multiple primitives, then this vertex will be in the vertex The buffer appears multiple times, that is, there is no concept of vertex sharing. Vertex sharing is achieved by indexing the functions of the draw class. In addition to the vertex buffer, there is an additional index buffer , which stores the index values ​​of the vertices in the vertex buffer. Scanning the index buffer is similar to scanning the vertex buffer: each X index is sorted by the vertex of the primitive. Vertex sharing is a common vertex for multiple primitives, as long as the index of the vertex in the vertex buffer is repeated in the index buffer, it is not necessary to store the vertex in the vertex buffer multiple times. Sharing is important for efficient use of memory, because most objects are represented by some closed meshes of triangle primitives, and most vertices appear as one of the triangle's vertices in multiple triangle primitives.

Here is an example of sequential drawing:

write picture description here

For the vertex buffer above, if we render triangle primitives, the GPU will produce the following set of triangle vertices: V0/1/2, V3/4/5, V6/7/8, etc.

Then here's an example of index drawing:

write picture description here

In this case, the GPU will generate the following triangles: V2/0/1, V5/2/4, V6/5/7 and so on.

Using indexed drawing in OpenGL requires creating and maintaining an index buffer, which must be bound to the vertex buffer before the draw call, and requires different API interface functions.

Detailed source code

(1)GLuint IBO;

Add another buffer object refnum for the index buffer.

(2)

Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);

Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);

Vertices[3] = Vector3f(0.0f, 1.0f, 0.0f);

To demonstrate vertex sharing we need a more complex mesh (multiple triangle primitives). Many tutorials use the famous rotation cube to achieve that, which requires 8 vertices and 12 triangle primitives. Since I'm lazy I used a rotated pyramid cone instead, which only requires four vertices and four triangle primitives, and is easier to set up manually...

When looking down at these vertices from the top (along the Y axis), we can see the following vertex layout:

write picture description here

(3)unsigned int Indices[] =

{ 0, 3, 1,

1, 3, 2,

0, 1, 2 };

The index buffer is laid out using an array of indices that match the positions of vertices in the vertex buffer. By looking at this array and the vertex layout above, you can know that the last triangle of the array is the base of the pyramid, and the other three triangles form the surface of the pyramid. Although this pyramid is asymmetrical, it is easy to define the structure.

(4)glGenBuffers(1, &IBO);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);

After we use the index array to create and set up the index buffer, we can find that the only difference from creating the vertex buffer is that the GL_ARRAY_BUFFER parameter used by the vertex buffer indicates the type of buffer, while the index buffer type uses GL_ELEMENT_ARRAY_BUFFER .

(5)glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

In addition to binding the vertex buffer we must also bind the indexed buffer before drawing, again, using GL_ELEMENT_ARRAY_BUFFER as the buffer type.

(6)glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

The index draw function here we use is glDrawElements instead of glDrawArrays. The first parameter is the type of primitive to be rendered (same as glDrawArrays); the second parameter is the number of indices in the index buffer used to generate the primitive; the latter parameter is the data type of each index value. The GPU must be told the size of a single index value, otherwise the GPU cannot know how to parse the index buffer. The available types of index values ​​are: GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT. If the index range is small, a small data type should be selected to save space. If the index range is large, a larger data type should be selected as needed; the last parameter tells the GPU from the start of the index buffer to the first The index to be scanned is worth the offset in bytes. This is useful when using the same index buffer to store the indices of multiple objects. By defining the offset and the number of indices, the GPU can be told which object to render. In our example In we scan from the beginning so define the offset as 0. Note that the type of the last parameter is GLvoid*, so if it is not 0 it must be converted to that parameter type.

Sample Demo


#include <stdio.h>
#include <string.h>

#include <math.h>
#include <GL/glew.h>
#include <GL/freeglut.h>

#include "ogldev_util.h"
#include "ogldev_math_3d.h"

GLuint FEB;
// handle to index buffer object
GLuint IBO;
GLuint gWorldLocation;

const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";


static void RenderSceneCB()
{
    glClear(GL_COLOR_BUFFER_BIT);

    static float Scale = 0.0f;

    Scale += 0.01f;

    Matrix4f World;

    World.m[0][0] = cosf(Scale); World.m[0][1] = 0.0f; World.m[0][2] = -sinf(Scale); World.m[0][3] = 0.0f;
    World.m[1][0] = 0.0;         World.m[1][1] = 1.0f; World.m[1][2] = 0.0f        ; World.m[1][3] = 0.0f;
    World.m[2][0] = sinf(Scale); World.m[2][1] = 0.0f; World.m[2][2] = cosf(Scale) ; World.m[2][3] = 0.0f;
    World.m[3][0] = 0.0f;        World.m[3][1] = 0.0f; World.m[3][2] = 0.0f        ; World.m[3][3] = 1.0f;

    glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &World.m[0][0]);

    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

    // Bind the index buffer each time before drawing
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    // index to draw graphics
    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);

    glutSwapBuffers();
}


static void InitializeGlutCallbacks()
{
    glutDisplayFunc(RenderSceneCB);
    glutIdleFunc(RenderSceneCB);
}

static void CreateVertexBuffer()
{
    // the four vertices of the pyramid
    Vector3f Vertices[4];
    Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
    Vertices[1] = Vector3f(0.0f, -1.0f, 1.0f);
    Vertices[2] = Vector3f(1.0f, -1.0f, 0.0f);
    Vertices[3] = Vector3f(0.0f, 1.0f, 0.0f);

    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
}

// create index buffer
static void CreateIndexBuffer()
{
    // set of vertex indices for the four triangular faces
    unsigned int Indices[] = { 0, 3, 1,
                               1, 3, 2,
                               2, 3, 0,
                               0, 1, 2 };
    // create buffer
    glGenBuffers(1, &IBO);
    // bind buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    // add buffer data
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}

static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
{
    GLuint ShaderObj = glCreateShader(ShaderType);

    if (ShaderObj == 0) {
        fprintf(stderr, "Error creating shader type %d\n", ShaderType);
        exit(1);
    }

    const GLchar* p[1];
    p[0] = pShaderText;
    GLint Lengths[1];
    Lengths[0]= strlen(pShaderText);
    glShaderSource(ShaderObj, 1, p, Lengths);
    glCompileShader(ShaderObj);
    GLint success;
    glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
    if (!success) {
        GLchar InfoLog [1024];
        glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
        fprintf(stderr, "Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
        exit(1);
    }

    glAttachShader(ShaderProgram, ShaderObj);
}

static void CompileShaders()
{
    GLuint ShaderProgram = glCreateProgram();

    if (ShaderProgram == 0) {
        fprintf(stderr, "Error creating shader program\n");
        exit(1);
    }

    string vs, fs;

    if (!ReadFile(pVSFileName, vs)) {
        exit(1);
    };

    if (!ReadFile(pFSFileName, fs)) {
        exit(1);
    };

    AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
    AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);

    GLint Success = 0;
    GLchar ErrorLog[1024] = { 0 };

    glLinkProgram(ShaderProgram);
    glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
    if (Success == 0) {
        glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
        fprintf(stderr, "Error linking shader program: '%s'\n", ErrorLog);
        exit(1);
    }

    glValidateProgram(ShaderProgram);
    glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
    if (!Success) {
        glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
        fprintf(stderr, "Invalid shader program: '%s'\n", ErrorLog);
        exit(1);
    }

    glUseProgram(ShaderProgram);

    gWorldLocation = glGetUniformLocation(ShaderProgram, "gWorld");
    assert(gWorldLocation != 0xFFFFFFFF);
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
    glutInitWindowSize (1024, 768);
    glutInitWindowPosition (100, 100);
    glutCreateWindow("Tutorial 10");

    InitializeGlutCallbacks();

    // Must be done after glut is initialized!
    GLenum res = glewInit();
    if (res != GLEW_OK) {
      fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
      return 1;
    }

    printf("GL version: %s\n", glGetString(GL_VERSION));

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    CreateVertexBuffer();
    CreateIndexBuffer();

    CompileShaders();

    glutMainLoop();

    return 0;
}

The code of the shader script has not changed from the last tutorial

running result

A colored pyramid rotated around the Y axis.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325773448&siteId=291194637