OpenGL Shader Program Analysis--Uniform Variables

Reprinted to: https://blog.csdn.net/cordova/article/details/52504118

background

In this tutorial we will encounter a new shader variable: Uniform Variables . The difference between coherent variables and ordinary attributes: the data contained in ordinary variables is vertex-specific, so they will load a new value from the vertex buffer when each shader is introduced; but the value of coherent variables is used throughout the draw call. remain unchanged. This means that after you load the value of the variable before the draw call, you can always get the same value every time the vertex shader is invoked. The main function of consistent variables is to save data such as lighting parameters (light position and direction, etc.), transformation matrices, handle of material objects, etc.

In this tutorial we can finally make the graphics move on the screen, we use a consistent variable whose value changes every frame and an idle callback provided by GLUT to achieve the effect of movement. The problem is that GLUT doesn't call our render callback repeatedly if it doesn't have to. GLUT only has to re-call our render callback for events like maxing and minifying a window or reappearing from behind another window. If we don't make any changes in the window layout after the app starts then the render callback will only be executed once, this can be verified by adding a prinf print statement to the render function, you will see that it will only be output once, but if you Minimizing the window and then maximizing the window will see the output again. 
In the previous tutorial, it was possible to only register the rendering callback in GLUT, but now we want to repeatedly change the value of a variable, we need to register an idle function callback. This idle function will be called by GLUT when it has not received any window events. You can use a dedicated function for this callback, do some stuff like time updates inside the function or just simply register such a render callback as an idle callback to be used. In our tutorial we will update the value of the variable in this render callback function.

Detailed source code

(1) glutIdleFunc(RenderSceneCB); 
Here we register the rendering callback as a global idle callback. Note that if you decide to use a dedicated idle callback then you need to add a glutPostRedisplay() after it to mark the window for repainting, otherwise the idle callback will be called continuously but the render function will not be called and you won't see the effect. glutPostRedisplay() marks that the current window needs to be redrawn, and this rendering callback will be called when the next GLUT main loop starts.

(2)  After linking the program, we can query the location of the uniform variable . This is another example of mapping the execution environment of a C/C++ application to the shader execution environment. You cannot directly access the shader's content and cannot change the value of its variables. When you compile a shader object, the GLSL compiler assigns an index to each uniform variable . In the internal representation of the shader, it obtains the variable value inside the compiler through the index value of the variable. The index can be obtained by setting the handle parameter and variable name parameter of the program object through the glGetUniformLocation function. If there is an error, it will return -1 . It is very important to check for errors (just like the above statement is checked by assert), otherwise this variable cannot be passed to the shader in future updates. There are two main reasons for this function to fail: either your variable name is wrong, or Optimized by the compiler, because if the GLSL compiler finds that the variable is not actually used in the shader, it will be discarded, so calling glGetUniformLocation to get the index will definitely fail.
gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale"); 
assert(gScaleLocation != 0xFFFFFFFF);
 

(3)  We maintain a static floating-point variable that changes a little in each rendering callback (if the value of 0.001 above changes too fast or too slow when running on your computer, you can adjust it) . The sin function is added to the above parameters, so that the value actually passed to the shader is actually the sine value of the Scale variable, and the scaling parameter value is smoothly cyclically transformed between -1.0 and 1.0. Note that the parameter of the sinf() function is a radian value instead of an angle value, but we don't care here, we just need a smooth transformation. The result value of the sinf() function is passed to the shader through the glUniform1f function. OpenGL provides a number of instance functions similar to glUniform1f, named in the form of glUniform{1234}{if}. In the second parameter of these functions, you can assign floats (suffixed with i) or integers (suffixed with f) to vectors of different dimensions (1D, 2D, 3D, 4D) as parameters, of course There are also other versions of the function that take other parameter forms: the address of a vector vector or a special matrix; the first parameter is the index location we get through the glGetUniformLocation() function.
static float Scale = 0.0f; 
Scale += 0.001f; 
glUniform1f(gScaleLocation, sinf(Scale));
 

Let's take a look at the changes we made in the VS vertex shader script (the FS script remains unchanged):

(4) uniform float gScale; 
Here a consistent variable is defined in the shader.

(5) gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0); 
We multiply the values ​​of the position vectors X and Y by the scale value that changes in the loop that applies each frame. 
Can you explain why the triangle flips up and down halfway through each loop?

Example 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;
GLuint gScaleLocation; // position intermediate variable

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

static void RenderSceneCB()
{
    glClear(GL_COLOR_BUFFER_BIT);

    // maintain a static floating point number that is slowly increasing
    static float Scale = 0.0f;
    Scale += 0.01f;
    // pass the value to the shader
    glUniform1f (gScaleLocation, class (Scale));

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

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(0);

    glutSwapBuffers();
}

static void InitializeGlutCallbacks()
{
    glutDisplayFunc(RenderSceneCB);
    // Register the render callback as a global idle callback
    glutIdleFunc(RenderSceneCB);
}

static void CreateVertexBuffer()
{
    Vector3f Vertices[3];
    Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
    Vertices[1] = Vector3f(1.0f, -1.0f, 0.0f);
    Vertices[2] = 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 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);

    // Query to get the location of the consistent variable
    gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
    // check for errors
    assert(gScaleLocation != 0xFFFFFFFF);
}

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

    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();

    CompileShaders();

    glutMainLoop();

    return 0;
}
The modified shader.vs script code compared to the previous tutorial:
#version 330 // Tell the compiler that our target GLSL compiler version is 3.3

layout (location = 0) in vec3 Position; // Bind the fixed-point attribute name and attribute, the corresponding mapping between the buffer attribute and the shader attribute in mode 2

uniform float gScale;

void main()
{
    gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0); // provide return value for glVertexAttributePointer
}

running result

The red triangle located in the center of the screen dynamically enlarges from 0 to the original size and then shrinks to disappear, and then flips to enlarge and then shrinks in such a cycle, which is the scaling transformation effect in Tutorial 8 later.



Guess you like

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