OpenGL基础39:GLSL内建变量与接口块

 

GLSL有几个以gl_为前缀的变量(内建变量),它们在着色器中能直接获取和使用,并且都有着很重要的意义,gl_Position 和 gl_FragCoord 就是两个典型的内建变量

一、顶点着色器变量

gl_Position:

顶点着色器裁切空间输出的位置向量。想让屏幕上渲染出东西,那么就必须使用,否则将什么都看不到,在第一次接触顶点着色器之后,就一直在用它了

gl_PointSize:

渲染出的点的大小,需要满足以下两个条件,gl_PointSize才会是有效的:

  • glEnable(GL_PROGRAM_POINT_SIZE):允许在着色器中设置点的大小
  • 绘制时渲染的基本图形(primitive)是 GL_POINTS

它是一个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_<condition>) out float gl_FragDepth:condition可以为以下4种之一

  • any:默认值,前置深度测试会被关闭
  • greater:深度值只会比gl_FragCoord.z大
  • less:深度值只会比gl_FragCoord.z小
  • unchanged:

如果发现不能用,说明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;
}

传递数据只需要块名一样,不需要实例名相同

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值