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