本篇在讲什么 本篇记录对glsl中的变量uniform的认知和学习 本篇适合什么 适合初学Open的小白 适合想要学习OpenGL中uniform的人 本篇需要什么 对 C++语法有简单认知 对 OpenGL有简单认知 最好是有 OpenGL超级宝典蓝宝书 依赖 Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重实践,轻理论,快速上手 提供全流程的源码内容 |
★提高阅读体验★ 👉 ♠ 一级标题 👈👉 ♥ 二级标题 👈👉 ♣ 三级标题 👈👉 ♦ 四级标题 👈 |
目录
- ♠ uniform
- ♥ 什么是uniform
- ♥ uniform的作用
- ♥ 简单理解
- ♥ 定义
- ♣ 方式1
- ♣ 方式2
- ♣ 定义演示
- ♥ 赋值
- ♣ 获取位置赋值
- ♣ 直接赋值
- ♥ 实战演示
- ♠ 一致区块
- ♥ 定义
- ♥ 数据布局
- ♣ 标准布局
- ♥ 数据缓冲
- ♥ 完整示例
- ♠ 推送
- ♠ 结语
♠ uniform
♥ 什么是uniform
uniform
在glsl是一种特殊变量的限定符,这种变量被称为统一变量
♥ uniform的作用
能够直接将数据从应有程序传递到着色器阶段
♥ 简单理解
简单理解,用uniform定义在着色器中的变量,类似代码中的静态变量
♥ 定义
♣ 方式1
uniform vec4 out_color;
可以直接通过uniform
+类型+字段的方式定义在着色器内
♣ 方式2
layout(location = 0) uniform vec4 out_color;
可以在方式1
的基础上加上layout
位置限定符
♣ 定义演示
"#version 450 core \n"
" \n"
"uniform vec4 out_color1; \n"
" \n"
"layout(location = 0) uniform vec4 out_color2; \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = out_color1; \n"
"} \n"
以上是一个片段着色器的代码,我们在其中按照方式1
和方式2
定义了两个统一变量out_color1
和out_color2
♥ 赋值
定义uniform
后,我们需要在外部给其赋值,根据不同的定义方式,赋值的方式也略有不同
♣ 获取位置赋值
第一种赋值方式针对根据方式1
定义的变量,我们首先需要获取变量在程序中的位置,才可以通过接口赋值,看下述代码
int location = glGetUniformLocation(program, "out_color1");
glUniform4f(location, 1.0f, 1.0f, 1.0f, 1.0f)
要点1
:glGetUniformLocation
该接口可以根据定义的变量名,来获取
统一变量
在程序中的位置,参数1是创建好的程序,参数2是自定义的变量名,out_color1
是我们在上边定义代码中写的变量
要点2
:glUniform4f
我们在获取到变量位置后,可以通过
glUniform**
类似的接口给不同类型的统一变量赋值,参数1是位置,后边是需要赋的值
♣ 直接赋值
在上述定义2
的形式中,我们直接通过限定符layout
给变量设定了位置,如此一来我们就不需要再获取位置了,可以直接赋值
glUniform4f(0, 1.0f, 1.0f, 1.0f, 1.0f)
类似的形式还有很多,比如直接定义数组的赋值方式
GLfloat outColor[4] = {1.0f, 1.0f, 1.0f, 1.0f };
glUniform4fv(0, 1, outColor);
♥ 实战演示
我们已经知道了如何定义变量,还有如何赋值,下面我们就写一个简单的例子,画一个三角形,通过uniform
变量给三角形上色
注:该例子直接修改OpenGl超级宝典官方示例singletri.cpp
,只需修改片段着色器和render方法
- 片段着色器
static const char * fs_source[] =
{
"#version 450 core \n"
" \n"
"uniform vec4 out_color1; \n"
" \n"
"layout(location = 0) uniform vec4 out_color2; \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = out_color2; \n"
"} \n"
};
- render
virtual void render(double currentTime)
{
static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
glClearBufferfv(GL_COLOR, 0, green);
// 赋值方式1
//int location = glGetUniformLocation(program, "out_color1");
//glUniform4f(location, 1.0f, 1.0f, 1.0f, 1.0f);
// 赋值方式2
//glUniform4f(0, 1.0f, 1.0f, 1.0f, 1.0f);
// 赋值数组
GLfloat outColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
glUniform4fv(0, 1, outColor);
glUseProgram(program);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
很简单的一个例子,我们通过glUniform4f
或glUniform4fv
方法,给outColor赋值,得以给三角形绘制指定颜色
♠ 一致区块
我们已经了解到统一变量的定义、赋值和使用,但是仅仅是单一变量,还有种高级点的用法,可以将很多的统一变量组合成一个区块,称之为一致区块
♥ 定义
以下声明了一个名为TransformBlock
的一致区块
uniform TransformBlock{
float scale;
vec3 translation;
float rotation[3];
mat4 projection_matrix;
};
♥ 数据布局
命名好的区块会被写入到缓冲当中,在缓冲中,区块的数据布局可能不同,有两种数据布局
- 标准的、共识的数据布局(标准布局)
由于区块中的数据不同,占用的空间不同,该布局会根据每个统一变量的空间占用去存储
- OpenGL自己决定数据的布局方式(shared布局)
OpenGL自己决定存储方式,用之前并不知道各个数据位置,运行的时候需要先查询数据布局格式
♣ 标准布局
推荐使用标准布局,以下是标准布局的声明方式,需要用到限定符std140
去声明一致区块
layout uniform (std140) TransformBlock{
float scale;
vec4 color;
};
使用标准布局最重要的一点是我们需要知道区块内的统一变量,在缓存中的数据位置,我们简单理解一下数据的分布规则
- 每个类型都有固定长度
- 数据的起始点必须是其固定长度的倍数
我们以上边声明的TransformBlock
为例来简单介绍一下
- 类型
float
的长度是4,起始点是0,scale
需要四个字节,所以结束位置4 - 类型
vec4
的长度是16,因为前4位已被占用,并且4不是16的倍数,所以translation
在缓冲的起始点只能是16,需要16个字节,在32结束
♥ 数据缓冲
区块的缓冲和我们之前学习的缓冲类似,结构基本一致,分为以下几个步骤
- 创建和绑定缓冲
unsigned int ubo;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
- 分配内存
glBufferData(GL_UNIFORM_BUFFER, 4*8, NULL, GL_STATIC_DRAW);
- 将缓冲区对象内的范围绑定到索引的缓冲区目标
glBindBufferRange(GL_UNIFORM_BUFFER, 0, ubo, 0, 4 * 8);
- 更新数据
glBufferSubData(GL_UNIFORM_BUFFER, 0, 4 * 4, sColor);
♥ 完整示例
我们来看一个完整的演示示例吧,很简单,我们只通过区块内的统一变量去给三角形上色
注:该例子直接修改OpenGl超级宝典官方示例singletri.cpp
,只需修改startup
方法即可
virtual void startup()
{
static const char * vs_source[] =
{
"#version 450 core \n"
" \n"
" \n"
"void main(void) \n"
"{ \n"
" const vec4 vertices[] = vec4[](vec4( 0.25, -0.25, 0.5, 1.0), \n"
" vec4(-0.25, -0.25, 0.5, 1.0), \n"
" vec4( 0.25, 0.25, 0.5, 1.0)); \n"
" \n"
" gl_Position = vertices[gl_VertexID]; \n"
"} \n"
};
static const char * fs_source[] =
{
"#version 450 core \n"
" \n"
"layout (std140) uniform color_block \n"
"{ \n"
" vec4 out_color; \n"
"}; \n"
" \n"
"out vec4 color; \n"
" \n"
"void main(void) \n"
"{ \n"
" color = out_color; \n"
"} \n"
};
program = glCreateProgram();
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, fs_source, NULL);
glCompileShader(fs);
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, vs_source, NULL);
glCompileShader(vs);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLfloat sColor[] = { 1.0f, 0.5f, 0.0f, 1.0f };
unsigned int ubo;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, 4*8, NULL, GL_STATIC_DRAW);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, ubo, 0, 4 * 8);
glBufferSubData(GL_UNIFORM_BUFFER, 0, 4 * 4, sColor);
}
要点1:
在该片段着色器中我们声明了一个标准区块color_block
,其存有唯一变量out_color
,该变量会作为三角形颜色被赋值
要点2:
自定义颜色sColor
,作为数值通过glBufferSubData
接口更新到了区块内,以下是最终显示效果
♠ 推送
- Github
https://github.com/KingSun5
♠ 结语
若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。