GLSL有几个以gl_为前缀的变量(内建变量),它们在着色器中能直接获取和使用,并且都有着很重要的意义,gl_Position 和 gl_FragCoord 就是两个典型的内建变量
gl_Position:
顶点着色器裁切空间输出的位置向量。想让屏幕上渲染出东西,那么就必须使用,否则将什么都看不到,在第一次接触顶点着色器之后,就一直在用它了
gl_PointSize:
渲染出的点的大小,需要满足以下两个条件,gl_PointSize才会是有效的:
它是一个float变量,可以以像素的方式设置点的高度和宽度,在着色器中描述每个顶点做为点被绘制出来的大小:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texture;
out vec2 texIn;
out vec3 normalIn;
out vec3 fragPosIn;
uniform mat4 model; //模型矩阵
uniform mat4 view; //观察矩阵
uniform mat4 projection; //投影矩阵
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
gl_PointSize = gl_Position.z;
texIn = texture;
fragPosIn = vec3(model * vec4(position, 1.0f));
normalIn = mat3(transpose(inverse(model))) * normal;
}
看上去比较惊悚,改变了绘制模式为GL_POINTS,着色器修改如上:当顶点距离观察者更远的时候,它就会变得更大,后面的一些粒子效果也是通过这个实现的
gl_VertexID:
上面的gl_Position和gl_PointSize都是输出变量,它们的值是作为顶点着色器的输出被读取的,因此可以向它们写入数据来影响结果,而gl_VertexID是一个输入变量,只能够读取
gl_VertexID是个整型变量,它储存着我们绘制的当前顶点的ID,当进行索引渲染(glDrawElements)时,这个变量保存着当前绘制的顶点的索引,当用的不是索引绘制(glDrawArrays)时,这个变量保存的是从渲染开始起直到当前处理的这个顶点的(当前顶点)编号,目前没什么用
gl_FragCoord:
在《OpenGL基础29:深度测试》这一章就用过了,只读输入变量vec4,其中x和y为窗口坐标(别忘了openGL默认以左下为原点),其中小数部分恒为(0.5, 0.5),这是因为原点并非(0, 0)而是(0.5, 0.5)的原因,整数部分就是数第几个像素点了,若viewport范围 为(0, 0, 2560, 1440)时, x, y 的取值范围就为(0.5, 0.5, 2559.5, 1439.5);z坐标为当前片元的深度信息,由顶点坐标系处理过后系统插值得到,第4个分量为 1/w
用这个可以实现很多好玩的东西,例如修改之前的天空盒着色器如下:
#version 330 core
in vec3 texIn;
out vec4 color;
uniform samplerCube skybox;
void main()
{
if(gl_FragCoord.x < 400)
color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
else
color = texture(skybox, texIn);
}
通过gl_FragCoord,就可以实现类似于分屏的效果,着色器可以给出2个完全不同的效果,一个显示在左半部分,一个显示在右半部分
gl_FrontFacing:
在《OpenGL基础32:面剔除》这一章中,知道了OpenGL如何根据顶点绘制顺序判断一个面是正面还是背面,并在开启面剔除后直接背面直接不进行绘制,而gl_FrontFacing变量正能告诉我们当前片段是某个正面的一部分还是背面的一部分
有了这个之后,就可以对于一个面的正反两面,分别使用不同的纹理或者展现不同的效果
当然了,不要开启面剔除,不然就毫无意义
gl_FragDepth:
上面的gl_FragCoord是只读的,不能修改它的值,而如果你想修改深度值,那么也是OK的,GLSL提供了一个叫做gl_FragDepth的变量,可以用它在着色器中设置像素的深度值
不过一旦设置了gl_FragDepth,OpenGL就会关闭所有的前置深度测试,因为OpenGL并不会知道你会设成什么值
深度测试是在片段着色器已经模板测试执行之后,但是现在大部分的GPU都提供一个叫做提前深度测试(Early Depth Testing)的硬件特性,提前深度测试允许深度测试在片段着色器之前运行,只要清楚一个片段永远不会是可见的(它在其他物体之后),就能提前丢弃这个片段,是一个很不错的优化手段
这样的话,设置gl_FragDepth就会影响OpenGL的性能
一环扣一环,如果我们能保证设置的深度值一定比gl_FragCoord.z更大或者更小,那么其实OpenGL就可以不用关闭前置深度测试因为不影响结果,因此,在OpenGL4.x版本后,它允许我们做个承诺,以保证我们设置的gl_FragDepth值满足一定条件
layout (depth_
如果发现不能用,说明openGL的版本可能比较低
着色器之间传数据通过 in 和 out 关键字,只需要在当前着色器中声明一个输出,在下一个着色器中声明一个同类型同名字的输入就可以,当然,也可以将一堆数据包在一个“块”里一起传过去,和结构体很像,在这里是接口块(Interface Blocks):
修改之前的着色器,非常简单:
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texture;
out VS_OUT
{
vec2 texIn;
}param;
uniform mat4 model; //模型矩阵
uniform mat4 view; //观察矩阵
uniform mat4 projection; //投影矩阵
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
param.texIn = texture;
}
///
#version 330 core
out vec4 color;
in VS_OUT
{
vec2 texIn;
}param;
uniform sampler2D texOut;
void main()
{
vec4 texColor = texture(texOut, param.texIn);
if(texColor.a < 0.1 || (texColor.r < 0.5 && texColor.g < 0.5 && texColor.b < 0.5))
discard;
color = texColor;
}
传递数据只需要块名一样,不需要实例名相同