opengl之高级GLSL(1)-顶点着色器变量与片段着色器变量

  • Post author:
  • Post category:其他




官方教程位置


openglCN



GLSL的内建变量:

  • 我们已经学会使用顶点属性、uniform和采样器来完成这一任务了。然而,除此之外,GLSL还定义了另外几个以gl_为前缀的变量,它们能提供给我们更多的方式来读取/写入数据。我们已经在前面教程中接触过其中的两个了:

    顶点着色器的输出向量gl_Position

    ,和

    片段着色器的gl_FragCoord



顶点着色器变量

  • List item我们已经见过gl_Position了,它是顶点着色器的裁剪空间输出位置向量。如果你想在屏幕上显示任何东西,在顶点着色器中设置gl_Position是必须的步骤。这已经是它的全部功能了。

  • gl_PointSize:


    我们能够选用的其中一个图元是GL_POINTS,如果使用它的话,

    每一个顶点都是一个图元

    ,都会被渲染为一个点。我们可以通过OpenGL的glPointSize函数来设置渲染出来的点的大小,但我们也可以在顶点着色器中修改这个值。

    GLSL定义了一个叫做gl_PointSize输出变量,它是一个float变量,你可以使用它来设置点的宽高(像素)。在顶点着色器中修改点的大小的话,你就能对每个顶点设置不同的值了。

    在顶点着色器中修改点大小的功能默认是禁用的,如果你需要启用它的话,你需要启用OpenGL的GL_PROGRAM_POINT_SIZE:
glEnable(GL_PROGRAM_POINT_SIZE);
//一个简单的例子就是将点的大小设置为裁剪空间位置的z值,也就是顶点距观察者的距离。点的大小会随着观察者距顶点距离变远而增大。
//结果就是,当我们远离这些点的时候,它们会变得更大
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);    
    gl_PointSize = gl_Position.z;    
}

  • gl_VertexID

    (目前没有什么具体用途)

    gl_Position和gl_PointSize都是输出变量,因为它们的值是作为顶点着色器的输出被读取的。我们可以对它们进行写入,来改变结果。顶点着色器还为我们提供了一个有趣的输入变量,我们只能对它进行读取,它叫做gl_VertexID。

    整型变量gl_VertexID储存了正在绘制顶点的当前ID。当(使用glDrawElements)进行索引渲染的时候,这个变量会存储正在绘制顶点的当前索引。当(使用glDrawArrays)不使用索引进行绘制的时候,这个变量会储存从渲染调用开始的已处理顶点数量。

    虽然现在它

    没有什么具体的用途

    ,但知道我们能够

    访问

    这个信息总是好的。



片段着色器变量


  • gl_FragCoord

    :

    我们已经见过gl_FragCoord很多次了,因为gl_FragCoord的z分量等于对应片段的

    深度值

    。然而,我们也能使用它的x和y分量来实现一些有趣的效果。


    gl_FragCoord



    x和y

    分量是片段的窗口空间(Window-space)

    坐标

    ,其

    原点

    为窗口的左下角。我们已经使用glViewport设定了一个800×600的窗口了,所以片段窗口空间坐标的x分量将在0到800之间,y分量在0到600之间。

    通过利用片段着色器,我们可以根据片段的窗口坐标,计算出不同的颜色。

    gl_FragCoord

    的一个常见用处是用于对比

    不同片段计算的视觉输出效果

    ,这在技术演示中可以经常看到。比如说,我们能够将屏幕分成两部分,在窗口的左侧渲染一种输出,在窗口的右侧渲染另一种输出。下面这个例子片段着色器会根据窗口坐标输出不同的颜色:


    你可以将它用于测试不同的光照技巧。

void main()
{             
    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);        
}

在这里插入图片描述

自己代码效果:x小于400才进行光的反射渲染,注意X值随着角度发生改变

在这里插入图片描述


  • gl_FrontFacing

    :段着色器另外一个很有意思的输入变量是gl_FrontFacing。在

    面剔除教程

    中,我们提到

    OpenGL能够根据顶点的环绕顺序来决定一个面是正向还是背向面

    。如果我们



    (启用GL_FACE_CULL来)使用面剔除,那么gl_FrontFacing将会告诉我们当前片段是属于正向面的一部分还是背向面的一部分。举例来说,我们能够对正向面计算出不同的颜色。

    gl_FrontFacing变量是一个bool,如果当前片段是

    正向面的一部分那么就是true,否则就是false

    。比如说,我们可以这样子创建一个立方体,在内部和外部使用不同的纹理:

#version 330 core
out vec4 FragColor;

in vec2 TexCoords;

uniform sampler2D frontTexture;
uniform sampler2D backTexture;

void main()
{            
	//注意这里使用了 gl_FrontFacing来进行正反面判断
    if(gl_FrontFacing)
        FragColor = texture(frontTexture, TexCoords);
    else
        FragColor = texture(backTexture, TexCoords);
}

  • gl_FragDepth

    :输入变量gl_FragCoord能让我们读取当前片段的窗口空间坐标,并获取它的深度值,但是它是一个只读(Read-only)变量。我们不能修改片段的窗口空间坐标,但实际上修改片段的深度值还是可能的。GLSL提供给我们一个叫做gl_FragDepth的输出变量,我们可以使用它来在着色器内设置片段的深度值。

    要想设置深度值,我们直接写入一个0.0到1.0之间的float值到输出变量就可以了

gl_FragDepth = 0.0; // 这个片段现在的深度值为 0.0

如果着色器没有写入值到gl_FragDepth,它会自动取用gl_FragCoord.z的值。

然而,由我们自己设置深度值有一个很大的缺点,只要我们在片段着色器中对gl_FragDepth进行写入,OpenGL就会(像深度测试小节中讨论的那样)禁用所有的提前深度测试(Early Depth Testing)。它被禁用的原因是,OpenGL无法在片段着色器运行之前得知片段将拥有的深度值,因为片段着色器可能会完全修改这个深度值。

在写入gl_FragDepth时,你就需要考虑到它所带来的性能影响。然而,从OpenGL 4.2起,我们仍可以对两者进行一定的调和,在片段着色器的顶部使用深度条件(Depth Condition)重新声明gl_FragDepth变量:

layout (depth_<condition>) out float gl_FragDepth;

通过将深度条件设置为greater或者less,OpenGL就能假设你只会写入比当前片段深度值更大或者更小的值了。这样子的话,当深度值比片段的深度值要小的时候,OpenGL仍是能够进行提前深度测试的。

下面这个例子中,我们对片段的深度值进行了递增,但仍然也保留了一些提前深度测试:

#version 420 core // 注意GLSL的版本!
out vec4 FragColor;
layout (depth_greater) out float gl_FragDepth;

void main()
{             
    FragColor = vec4(1.0);
    gl_FragDepth = gl_FragCoord.z + 0.1;
}  



版权声明:本文为wqdqwass原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。