简单理解就是一个GLSL shader中的全局常量,可以随意在任意shader(vertex shader, geometry shader, or fragment shader)访问,不同的shader中uniform是一起链接的,初始化之后,不能修改其值,否则会引起编译错误。
1 定义以及使用Uniform变量的方法:
1) 使用location的方法定位赋值:
vertex shader简单理解就是一个GLSL shader中的全局常量,可以随意在任意shader(vertex shader, geometry shader, or fragment shader)访问,不同的shader中uniform是一起链接的,初始化之后,不能修改其值,否则会引起编译错误。
详细的理解,我会写一篇解析篇,这个是实例篇版本,所以解析简单点,先简单理解,然后实例,然后想深入理解点,再看解析,或者从实例中领悟。
1 定义以及使用Uniform变量的方法:
1) 使用location的方法定位赋值:
vertex shader
#version 400
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
out vec3 outColor;
uniform mat4 wmat;
void main()
{
Color = color;
gl_Position = wmat * vec4(position, 1.f);
}
1.1) 使用Uniform的location设置Unfirom值
OpenGL程序代码中写:
glm::mat amat; // define a mat, and give it any value you need.
...
GLuint loc = glGetUniformLocation(program, "wmat");
if (loc >= 0) // if fail, loc == -1
{
glUniformMatrix4fv(loc, 1, GL_FALSE, &amat[0][0]);
}
1.2) 同时,如果是Uniform数组,也可以直接这样获取location:
GLSL中定义:
uniform vec3 Array[10];
C++客户端程序获取:
GLuint loc = glGetUniformLocation(program, “Array[1]”);
1.3) 如果是Uniform Struct,也可以直接获取
GLSL中定义:
struct aStruct
{
vec3 lightPos;
vec4 lightColor;
mat4x3 otherMat;
}myStruct;
C++客户端程序获取(注意前缀名字是myStruct,不是aStruct):
GLuint loc = glGetUniformLocation(program, "myStruct.lightPos")
这个名字变量的获取风格类C了,对于C程序员十分友好。
Uniform Struct(结构体)其实还有一个专业名字:Uniform block – 统一变量块,是属于Interface Block – 接口块的一种。
其实就是定义在GLSL中的一个结构体Struct
例子:
uniform MyBlock // 定义在shader中
{
vec4 color1;
vec4 color2;
float r1;
float r2;
}myBlock;
就是在Shader中自定义一个uniform的location:
layout(location = 2) uniform mat4 worldMat;
这样在C++客户端程序中就可以直接赋值,无需要寻址了
glUniformMatrix4fv(2, 1, GL_FALSE, &worldMat[0][0]);
不过这种自定义location,需要十分小心,如果赋予两个uniform同样的location,那就会产生链接错误。
尤其是同时赋予多个uniform location的时候要注意:
layout(location = 2) uniform vec4 aVec[8];
那么aVec的uniform location值范围是[2, 10)
所以如果这样定义:
layout(location = 2) uniform vec4 aVec[8];
layout(location = 5) uniform mat4 aMat[2];
那么就是非法的,会引起链接错误。因为aMat的uniform location地址范围值是[5, 7)和aVec的值范围[2, 10)重复了。
uniform vec4 greenColor = vec4(0.0, 1.0, 0.0, 1.0);
uniform vec3 vers[4] = vec3[](
vec3(0.3, -0.8, 0.1),
vec3(0.2, 1.0, 0.2),
vec3(0.6, -1.0, 0.3),
vec3(0.9, 0.7, 0.4)
);
2 高级用法:统一变量缓冲对象 – Uniform buffer object
这个用法稍微有点难,坑比较多的,所以,建议初学者可以暂时跳过,因为一开始学OpenGL,应该暂时不需要使用,使用前面的方法操作Uniform应该就很足够了。
这里先抛砖引玉,简单举例应用一下,一些小坑,小知识点不详细展开,会后续教程中补全的,还会专门讲这个用法。当然,看完下面这一段也足够可以使用这个知识点了。
这个是OpenGL客户端程序和GLSL中的Uniform变量对应的对象,也可以是和变量块Uniform block, 变量数组Uniform array对应。
目的是为了方便在OpenGL客户端操作(修改值)GLSL的Uniform对象,尤其是一组uniform值得时候,更加方便修改。
例如在GLSL中定义:
layout (std140, binding = 0) uniform MATS
{
mat4 mvMat;
vec3 aPos;
};
layout (std140, binding = 1) uniform MAT_BLOCK
{
mat4 matPool[120];
};
在C++客户端程序中修改:
// 第一个(binding = 0):
struct ABlock
{
glm::mat4 aMatrix;
glm::vec3 pos;
};
glGenBuffers(1, &aMapBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, aMapBuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ABlock), NULL, GL_DYNAMIC_DRAW);
// Careful: The parameter 0: is correspondant to binding = 0; not location.
// 0 是对应 GLSL中的bingding = 0; 不是location = 0
glBindBufferBase(GL_UNIFORM_BUFFER, 0, aMapBuffer);
ABlock* mapBlock = (ABlock*)glMapBufferRange(GL_UNIFORM_BUFFER,
0,
sizeof(ABlock),
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
mapBlock->aMatrix = glm::mat(1); // set any value you want here to GLSL
mapBlock->pos = glm::vec3(1); // set any value you want here to GLSL
glUnmapBuffer(GL_UNIFORM_BUFFER);
// 第二个(binding = 1)
struct MatPool
{
glm::mat4 viewMat;
glm::mat4 worldMat;
glm::mat4 modelMat
};
glGenBuffers(1, &matBuffer);
glBindBuffer(GL_UNIFORM_BUFFER, matBuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(MatPool), NULL, GL_DYNAMIC_DRAW);
// Careful: The parameter 1: is correspondant to binding = 1; not location.
// 1 是对应 GLSL中的bingding = 1; 不是location = 1
glBindBufferBase(GL_UNIFORM_BUFFER, 1, uniforms_buffer);
MatPool* mBlock = (MatPool*)glMapBufferRange(GL_UNIFORM_BUFFER,
0,
sizeof(MatPool),
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
glm::mat4 modelMat = glm::mat(1);
glm::mat4 viewMat = glm::mat(1);
glm::mat4 worldMat = glm::mat(1);
mBlock->viewMat = viewMat;
mBlock->worldMat = worldMat;
mBlock->modelMat = modelMat;
glUnmapBuffer(GL_UNIFORM_BUFFER);
第二个设置的时候,可以看到C++客户端的Struct结构和GLSL的结构体是不一样的,C++客户端是三个mat4的结构体,GLSL是一个结构体数组,我故意举这样的例子,是为了说明只要数据对应就行了,不一定要格式严格对应。
GLSL中利用binding设置索引位置,可以使用多个Uniform block绑定到客户端使用,当然也可以使用下面函数取得这个索引位置,就可以不在GLSL中明显声明,即可以省略binding = 0;由GLSL自动分配索引位置。
GLuint glGetUniformBlockIndex( GLuint program, const char *uniformBlockName );
还有其他各种用法,其实就没有那么实用了,这里就不贴出来,可以根据需要的时候,再进一步查阅。
3 赋值函数
可以使用glUniform*这个函数来设置不同值类型的uniform,OpenGL的设计风格是使用不同类型的后缀变形函数来设置,参考All kinds of set up uniform value examples:
GLAPI/glUniform - OpenGL Wiki
www.khronos.org
这里摘录一些,摘录这些的原因是只要知道这些赋值方法,那么其他类值得赋值方法也能推演出来:
glUniform1i(loc, (int)value); // setup uniform one int value;值得注意的是bool的uniform只也是使用这个函数赋值,没有glUniform1b这样的函数。
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void glUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2);
void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
Uniform (GLSL) - OpenGL Wiki
www.khronos.org
GLAPI/glBindBufferBase - OpenGL Wiki
www.khronos.org
Uniform Buffer Object
www.khronos.org
参考书本:OpenGL Superbible, OpenGL Shading Language
写这样文章除了要理解知识要点之外,还需要不断翻阅一些资料,简要地总结出来,其实只有实践使用过之后才会清楚哪些要点比较常用,需要理解透,记忆好,这样总结出来还是很花费时间的。