OpenGL shader program analysis--composite transformation

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

Note: This tutorial needs to add more header files, which is basically complete. The AntTweakBar library is used in it. The installation is very simple. Like other plugins before, go to the official website to download the resource package, and the lib folder is already compiled. Good dll and lib libraries can also be recompiled by running the VS project. 
Copy the dll file in the lib folder to the Windows/System32 directory, copy the lib file to, for example, my C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\lib directory, and add the files in the include folder. The AntTweakBar.h header file can be copied to, for example, my C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include directory.

background

In the previous tutorials we established several basic graphics transformations that give us the flexibility to move objects anywhere in the 3D world. We still have a lot to learn (camera controls, perspective projection, etc.), but as you may have figured out, composite graphics transformations are also necessary. In most cases, you will want to scale objects to fit in the 3d world, rotate objects to the proper orientation, move objects somewhere, etc. So far we have practiced each single graph transformation. In order to implement the above series of transformations, we need to left multiply the first transformation matrix on the vertex position, and then multiply the result obtained by the next transformation matrix, etc. All transformation matrices are left multiplied by the vertex position to achieve multiple transformations. A stupid way is to apply each transformation matrix in the shader to implement all transformations, but this is inefficient, because these matrices are the same for all vertices, only the position of the vertices changes, so it is repeated for each vertex. position to perform this series of matrix multiplication operations. Fortunately, there are some rules in linear algebra that can be said to make our lives easier. They tell us that for a given set of matrices M0...Mn and a vector V the following equations are converted:

Mn * Mn-1 * ... * M0 * V = (Mn* Mn-1 * ... * M0) * V

So if you order:

N = Mn * Mn-1 * ... * M0

So:

Mn * Mn-1 * ... * M0 * V = (Mn * Mn-1 * ... * M0) * V = N * V

This means that we can calculate N in one go, then pass it as a uniform variable to the shader and multiply each vertex position to complete all the transformations, which only requires the GPU to perform a matrix/vector multiplication for each vertex.

How to arrange the order of each transformation matrix when calculating N? The first thing you have to remember is that the vector is initially left-multiplied by the rightmost matrix (M0 in the above example). The vector is then transformed by all transformation matrices from the right to the left. In 3d graphics you usually want to scale the object, then rotate it, then translate it, then do the camera transformation and finally project to the 2d screen. Let's first see what happens when we rotate first and then translate:

write picture description here

Then look at what happens when you translate first and then rotate:

write picture description here

It can be seen that if you translate the object first, it will be difficult to set the final position of the object, because when you move the object away from the coordinate origin, then rotating the object will cause the translation effect of the object at the same time (it rotates around the origin, not around itself). ), which we hope to avoid. By rotating first and then moving, you can avoid the interdependence of these two operations, which is why you try to model symmetrically around the origin, so that when you scale or rotate the object, there will be no side effects, and the object will remain as symmetrical as before after scaling and rotating. .

Detailed source code

(1) 
#define ToRadian(x) ((x) * M_PI / 180.0f)

#define ToDegree(x) ((x) * 180.0f / M_PI)

In this tutorial we are going to use specific angle values. Trigonometric functions in the standard C library take radian values ​​as arguments. The above macro definition can realize the conversion between degrees and radians .

(2)


inline Matrix4f operator*(const Matrix4f& Right) const
{
    Matrix4f Ret;
    for (unsigned int i = 0 ; i < 4 ; i++) {
       for (unsigned int j = 0 ; j < 4 ; j++) {
           Ret.m[i][j] = m[i][0] * Right.m[0][j] +
                         m[i][1] * Right.m[1][j] +
                         m[i][2] * Right.m[2][j] +
                         m[i][3] * Right.m[3][j];
       }
    }

    return Ret;
}

Here is the operator that defines matrix multiplication . It can be seen that the value of the resulting matrix is ​​the dot product of each row of the left matrix and each column of the right matrix in turn. This matrix multiplication operation is very important to implement the pipeline class.

(3)

class Pipeline
{
    public:
       Pipeline() { ... }
       void Scale(float ScaleX, float ScaleY, float ScaleZ) { ... }
       void WorldPos(float x, float y, float z) { ... }
       void Rotate(float RotateX, float RotateY, float RotateZ) { ... }
       const Matrix4f* GetTrans();
    private:
       Vector3f m_scale;
       Vector3f m_worldPos;
       Vector3f m_rotateInfo;
       Matrix4f m_transformation;
};

The pipeline class abstracts all the transformed data information of an object. There are now 3 private vector member variables to store the object's scale, position and rotation per pixel in world space . There are also interface functions to set their values, and to get the final composite transformation matrix representing all transformations.

(4)

const Matrix4f* Pipeline::GetTrans()
{
    Matrix4f ScaleTrans, RotateTrans, TranslationTrans;
    InitScaleTransform(ScaleTrans);
    InitRotateTransform(RotateTrans);
    InitTranslationTransform(TranslationTrans);
    m_transformation = TranslationTrans * RotateTrans * ScaleTrans;
    return &m_transformation;
}

This function initializes the three separate transformation matrices and multiplies them one by one and returns the final product . Note that the multiplication order is fixed as described above. If you want to be more flexible you can set a bitmask that defines the transformation order. Also note that it always takes the final composite transformation matrix as a Chen member variable. You can do optimization by checking the flag, when the last transformation function has no change, you can return the last stored transformation matrix to avoid repeated calculations.

This function uses private methods to generate different transformations as described in the previous tutorial. This class will also be extended in the next tutorial for camera control and perspective transformation.

(5)

Pipeline p;
p.Scale (sinf (Scale * 0.1f), sinf (Scale * 0.1f), sinf (Scale * 0.1f));
p.WorldPos (class (Scale), 0.0f, 0.0f);
p.Rotate (sinf (Scale) * 90.0f, sinf (Scale) * 90.0f, sinf (Scale) * 90.0f);
glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());

The above statement is the change made in the render function. We instantiate a pipeline class object, and pass it to the shader after initializing the configuration. Check the effect of the final image by adjusting the parameters.

Sample Demo

The extension tool function in the matrix4x4 header file is used;

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

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

#include "ogldev_util.h"
#include "ogldev_pipeline.h"

GLuint FEB;
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.001f;

    // Instantiate a pipeline class object, pass it to the shader after initializing the configuration
    Pipeline p;
    p.Scale (sinf (Scale * 0.1f), sinf (Scale * 0.1f), sinf (Scale * 0.1f));
    p.WorldPos (class (Scale), 0.0f, 0.0f);
    p.Rotate (sinf (Scale) * 90.0f, sinf (Scale) * 90.0f, sinf (Scale) * 90.0f);
    glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, (const GLfloat*)p.GetWorldTrans());

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

    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);

    glutSwapBuffers();
}


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

static void CreateVertexBuffer()
{
    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);
}

static void CreateIndexBuffer()
{
    unsigned int Indices[] = { 0, 3, 1,
                               1, 3, 2,
                               2, 3, 0,
                               0, 1, 2 };

    glGenBuffers(1, &IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
    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 11");

    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;
}

running result

The composite transformation effect of zoom, rotation and translation can be designed and adjusted by yourself, and you can see the color tetrahedrons freely rotate in and out of space.





Guess you like

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