第二十课,高级GLSL

内建变量

已知

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高级篇 &动感超人
这篇文章详细的介绍了各种内存布局的形式
第二十课,高级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);

一个简单的例子

  1. 首先更改
uniform mat4 projection;
uniform  mat4 view;

layout(std140) uniform Matrices {
    mat4 projection;
    mat4 view;
};

在使用projection和view时仍直接调用(不是Matrices.projection,而是直接使用projection)。

  1. 设置绑定点的全局绑定缓存,将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);
  1. 将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版本之上。

上一篇:着色器基础


下一篇:蒙特卡洛法计算圆周率π(Python)