OpenGL Learning Road 5 - Using Uniform Variables

The code is on github

This article is based on the tutorial: ogldev for extended learning, starting from scratch step by step, recording the learning process

1. Uniform storage limiter

  • The uniform modifier can specify a variable that is set in the application before the shader runs, it will not change during primitive processing
  • The uniform variable is shared between all available shader stages, it must be defined as a global variable
  • Variables of any type (including structures and arrays) can be set as uniform variables
  • The shader cannot write to the uniform variable nor change its value

example: declare a uniform variable in the shader

uniform float gScale; 

The gScale variable can be referenced by name in the shader. If you need to set its value in the user application, you need two steps:
1. Use the glGetUniformLocation() function to get the index of gScale in the list
2. Use glUniform*() Or the glUniformMatrix*() series of functions to set the value of the uniform variable (gScale is a basic float type variable so use glUniform1f())

Simply put, a Uniform variable is a type that an application uses to pass to a shader and a value that doesn't change when the shader is running.

2. Code Explanation

2.1 opengl_math:

#ifndef __OPENGL_MATH_H
#define __OPENGL_MATH_H

//向量        
typedef float   Vector3f[3];                

//向量赋值
inline void LoadVector3(Vector3f v, const float x, const float y, const float z)
{
    v[0] = x; v[1] = y; v[2] = z;
}

#endif

There is no difference from the previous section, because this section only increases the application of the uniform storage limiter

2.2 main.cpp:

#include <stdio.h>
#include <string>
#include <math.h>
#include <gl/glew.h>
#include <gl/freeglut.h>
#include <fstream>
#include <assert.h>
#include "opengl_math.h"

using namespace std;

GLuint VBO;
GLint gScaleLocation; //位置中间变量

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

bool ReadFile(const char* pFileName, string &outFile)
{
    ifstream f(pFileName);

    bool ret = false;

    if (f.is_open()) {
        string line;
        while (getline(f, line)) {
            outFile.append(line);
            outFile.append("\n");
        }
        f.close();
        ret = true;
    }
    else {
        fprintf(stderr, "%s:%d: unable to open file `%s`\n", __FILE__, __LINE__, pFileName);
    }
    return ret;
}


static void Render()
{
    glClear(GL_COLOR_BUFFER_BIT);

    static float Scale = 0.0f;
    Scale += 0.01f;
    //将值传递给shader
    glUniform1f(gScaleLocation, sinf(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(Render);

    //将渲染回调注册为全局闲置回调
    glutIdleFunc(Render);
}

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

    //查询获取一致变量的位置
    gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
    //检查错误
    assert(gScaleLocation != 0xFFFFFFFF);
}


static void CreateVertexBuffer()
{
    Vector3f Vertices[3];

    LoadVector3(Vertices[0], -1.0f, -1.0f, 0.0f);
    LoadVector3(Vertices[1], 1.0f, -1.0f, 0.0f);
    LoadVector3(Vertices[2], 0.0f, 1.0f, 0.0f);

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

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

    InitializeGlutCallbacks();

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

Here only look at the different parts compared to the previous section.

2.2.1 Global idle callback function

Added a sentence after registering the drawing function

glutIdleFunc(Render);

glutIdleFunc() is used to set the global idle callback function, that is, when the program is idle (when no window event arrives), the function Render() is called

2.2.2 Get the location of the Uniform variable

//查询获取一致变量的位置
gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
//检查错误  assert(gScaleLocation != 0xFFFFFFFF);

As mentioned before, to set the value of a uniform variable in an application, the first step is to find the index of the uniform variable in the list

GLint glGetUniformLocation (GLuint program ,
constchar* name);

Returns the index value corresponding to the uniform variable name in the shader program. If name does not match all uniform variables in the enabled shader program, or name is the name of an internally reserved shader variable, the return value is -1

assert(gScaleLocation != 0xFFFFFFFF);

If -1 is returned, print a message to stderr, and then terminate the program by calling abort (ie, report an error and terminate the program)

2.2.3 Setting the value of the Uniform variable

static float Scale = 0.0f;
Scale += 0.01f;
glUniform1f(gScaleLocation, sinf(Scale));

In the Render() rendering function, the sin value of an increasing static floating-point number is transmitted through glUniform1f, that is, the value of sinf(Scale) changes cyclically between -1 and 1

2.3 shader

shader.vs:

#version 330

layout(location = 0) in vec3 Position;

uniform float gScale;

void main()
{
    gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0);
}

The uniform variable is used in the vertex shader, and through it, the position of the three vertices of the triangle is changed
- uniform float gScale: declare the gScale variable
- gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0 ): use the gScale variable by using the gScale name

shader.fs:

#version 330

out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0,0.0,0.0,1.0);
}

Fragment shader unchanged

3. Operation results

write picture description here
It can be seen that the triangle changes from large to small and then from small to large, and each time it changes from large to small, it flips the position, because the value of sinf(Scale) varies between -1 and 1

Guess you like

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