OpenGL基础 - 统一变量Uniform

版权声明:本文作者靖心,靖空间地址:http://blog.csdn.net/kenden23/,未经本作者允许不得转载。 https://blog.csdn.net/kenden23/article/details/81106031

简单理解就是一个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;
  1. 在Shader中自定义Location

就是在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)重复了。

  1. 可以给uniform赋予默认值,如果C++客户端没有给uniform值得时候,GLSL会使用这个默认值:
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);

  1. 参考:

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

写这样文章除了要理解知识要点之外,还需要不断翻阅一些资料,简要地总结出来,其实只有实践使用过之后才会清楚哪些要点比较常用,需要理解透,记忆好,这样总结出来还是很花费时间的。

猜你喜欢

转载自blog.csdn.net/kenden23/article/details/81106031