内建变量
已知
gl_Position 顶点着色器的裁剪空间输出位置向量
FragColor 片元着色器的片元输出颜色值
未知
顶点着色器变量
gl_PointSize
GLSL定义了一个叫做gl_PointSize输出变量,它是一个float变量,你可以使用它来设置点的宽高(像素)。
在顶点着色器中修改点大小的功能默认是禁用的,启用它需要:
glEnable(GL_PROGRAM_POINT_SIZE);
我们可以通过OpenGL的glPointSize函数来设置渲染出来的点的大小
void glPointSize( GLfloat size);//默认值为1
但我们也可以在顶点着色器中修改这个值。
vertexshader:
gl_PointSize = 给定的值(float变量);
gl_VertexID
gl_Position和gl_PointSize都是输出变量,
gl_VertexID是输入变量(整型变量)。
gl_VertexID储存了正在绘制顶点的当前ID。
当(使用glDrawElements)进行索引渲染的时候,这个变量会存储正在绘制顶点的当前索引。
当(使用glDrawArrays)不使用索引进行绘制的时候,这个变量会储存从渲染调用开始的已处理顶点数量。
简单来说,他是个数据缓冲的计数器。
片段着色器变量
gl_FragCoord
gl_FragCoord的x和y分量是片段的窗口空间(Window-space)坐标,其原点为窗口的左下角。
gl_FragCoord的z分量等于对应片段的深度值。
使用举例:对不同屏幕区域使用不同的渲染方式,可用于对比不同的渲染效果。
if(gl_FragCoord.x < 400)
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
else
FragColor = vec4(0.0, 1.0, 0.0, 1.0);
gl_FrontFacing
gl_FrontFacing将会告诉我们当前片段是属于正向面的一部分还是背向面的一部分。
gl_FrontFacing变量是一个bool类型,正面返回true,背面返回false。
使用举例:对正面反面使用不同的纹理属性,可以用于渲染内外纹理不同的物体(物体内部也可见的情况下使用)。
if(gl_FrontFacing)
FragColor = texture(frontTexture, TexCoords);
else
FragColor = texture(backTexture, TexCoords);
前提:不开启面剔除。
gl_FragDepth
gl_FragCoord能让我们获取它的深度值,
gl_FragDepth可以用来在着色器内设置片段的深度值。
然而,由我们自己设置深度值有一个很大的缺点,只要我们在片段着色器中对gl_FragDepth进行写入,OpenGL就会禁用所有的提前深度测试(Early Depth Testing)。
它被禁用的原因是,OpenGL无法在片段着色器运行之前得知片段将拥有的深度值,因为片段着色器可能会完全修改这个深度值。
然而,从OpenGL 4.2起,我们仍可以对两者进行一定的调和,在片段着色器的顶部使用深度条件(Depth Condition)重新声明gl_FragDepth变量:
layout (depth_) out float gl_FragDepth;
condition可以为下面的值:
条件 | 描述 |
---|---|
any | 默认值。提前深度测试是禁用的,你会损失很多性能 |
greater | 你只能让深度值比gl_FragCoord.z更大 |
less | 你只能让深度值比gl_FragCoord.z更小 |
unchanged | 如果你要写入gl_FragDepth,你将只能写入gl_FragCoord.z的值 |
然后使用
gl_FragDepth = 深度值。来改写。
具体在何处使用还不得知。
接口块
out VS_OUT
{
vec2 TexCoords;
} vs_out;
发送到下一个着色器中的所有输出变量。
in VS_OUT
{
vec2 TexCoords;
} fs_in;
只要两个接口块的名字一样,它们对应的输入和输出将会匹配起来。实例名(vs.out–>fs.in)可以是随意的。
Uniform缓冲对象
它允许我们定义一系列在多个着色器中相同的全局Uniform变量。当使用Uniform缓冲对象的时候,我们只需要设置相关的uniform一次。当然,我们仍需要手动设置每个着色器中不同的uniform。并且创建和配置Uniform缓冲对象会有一点繁琐。
创建一个Uniform缓冲对象
unsigned int uboExampleBlock;
glGenBuffers(1, &uboExampleBlock);
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
glBufferData(GL_UNIFORM_BUFFER, 152, NULL, GL_STATIC_DRAW); // 分配152字节的内存
glBindBuffer(GL_UNIFORM_BUFFER, 0);
Uniform块
layout (std140) uniform Matrices
{
mat4 projection;
mat4 view;
};
内存布局
OpenGL——GLSL高级篇 &动感超人
这篇文章详细的介绍了各种内存布局的形式
共享(Shared)布局
由硬件定义偏移量,但是这样我们就不知道如何准确地填充我们的Uniform缓冲了。
所以要想获取具体的偏移量,需要
- glGetUniformIndices获取所有uniform变量的索引
- glGetActiveUniformsiv通过uniform 变量的索引来获取相应的偏移值、大小、类型等信息
- glGetActiveUniformBlockiv获取uniform缓存的索引,并获取整个块的大小
具体怎么使用我也不知道。。。。。先知道有这么个东西~~【菜狗.jpg】~~
std140
内存分配规则与c语言struct内存分配规则相同。
根据std140布局的规则,我们就能使用像是glBufferSubData的函数将变量数据按照偏移量填充进缓冲中了。
绑定点(Binding Point)
如同sampler(采样器)的绑定一样
lightShader.setInt("material.sampler", 0);//material。samlper接受第0号绑定点的纹理
glActiveTexture(GL_TEXTURE0);//激活第0号绑定点
glBindTexture(GL_TEXTURE_2D, texture1);//将纹理载入绑定点
在Uniform缓冲对象中有
1.将图示中的Lights Uniform块链接到绑定点2:
unsigned int lights_index = glGetUniformBlockIndex(shaderA.ID, "Lights");
glUniformBlockBinding(shaderA.ID, lights_index, 2);
从OpenGL4.2版本起,你也可以添加一个布局标识符,显式地将Uniform块的绑定点储存在着色器中,这样就不用再调用glGetUniformBlockIndex和glUniformBlockBinding了。
layout(std140, binding = 2) uniform Lights { ... };
2.绑定Uniform缓冲对象到相同的绑定点上
glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboExampleBlock);
// 或
glBindBufferRange(GL_UNIFORM_BUFFER, 2, uboExampleBlock, 0, 152);
glBindbufferBase(目标,一个绑定点索引,一个Uniform缓冲对象);
glBindBufferRange(目标,一个绑定点索引,一个Uniform缓冲对象,附加的偏移量,大小参数);
glBindBufferRange可以绑定Uniform缓冲的特定一部分到绑定点中。
向Uniform缓冲中添加数据
//在向缓存添加数据前,必须先绑定缓存
glBindBuffer(GL_UNIFORM_BUFFER, uboExampleBlock);
int b = true; // GLSL中的bool是4字节的,所以我们将它存为一个integer
glBufferSubData(GL_UNIFORM_BUFFER, 144, 4, &b);
//glBufferSubData(缓存类型, 偏移量, 替换空间大小, 内容的指针);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
一个简单的例子
- 首先更改
uniform mat4 projection;
uniform mat4 view;
为
layout(std140) uniform Matrices {
mat4 projection;
mat4 view;
};
在使用projection和view时仍直接调用(不是Matrices.projection,而是直接使用projection)。
- 设置绑定点的全局绑定缓存,将projection,view存入缓存
//Uniform Binding Object 存储投影,视口变化矩阵
unsigned int ubo;
glGenBuffers(1, &ubo);
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);//将ubo设有为两个mat4大小的内存
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(projection));//将projection存入第一个位置
//glm::value_ptr传入一个矩阵,返回一个数组。
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, ubo, 0, 2 * sizeof(glm::mat4));//将ubo全部内存设为0号绑定点
view = maincamera.getViewMetrix();
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
-
将Matrices绑定到0号绑定点。
若不使用着色器中定义uniform绑定的点**(
binding = 0),
我们有两种方法
- 第一种方法
unsigned int uniformBlockIndexRed = glGetUniformBlockIndex(lightShader.ID, "Matrices");
glUniformBlockBinding(lightShader.ID, uniformBlockIndexRed, 0);
- 第二种方法
Shader类中定义:
void Shader::setUniformBlockBinding(const std::string& name, int value) const {
glUniformBlockBinding(ID, glGetUniformBlockIndex(ID, name.c_str()), value);
}
用Shader类内方法轻松添加。
main->while循环中
lightShader.setUniformBlockBinding("Matrices", 0);
- 第三种方法
使用着色器中定义uniform绑定的点(binding = 0),如下
layout(std140, binding = 0) uniform Matrices {
mat4 projection;
mat4 view;
};
第三种方法仅限于OpenGL 4.2版本之上。