Ch4-2 顶点着色器

顶点着色器(vertex shader)是图形管线中的一个着色器,它可以处理来自顶点缓冲区的数据,或根据一定的规则直接生成顶点数据。
若非Task/Mesh管线,那么顶点着色器是图形管线中最初执行的着色器。

顶点着色器逐顶点发生调用。

顶点着色器的内置输入

用GLSL编写的顶点着色器有以下内置输入:

全局变量

int gl_VertexIndex

表示当前顶点的索引

int gl_InstanceIndex

表示当前实例的索引

int gl_DrawID

在多重绘制或间接绘制命令中,表示当前绘制的索引

int gl_BaseVertex

表示本次绘制中的起始顶点索引

int gl_BaseInstance

表示本次绘制中的起始实例索引

对于非索引绘制命令,比如vkCmdDraw(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance),gl_BaseVertex值为firstVertex,gl_VertexIndex必然不小于该值,类似地,gl_BaseInstance值为firstInstance,gl_InstanceIndex必然不小于该值。

对于索引绘制命令,比如vkCmdDrawIndexed(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance),gl_BaseVertex值为vertexOffset,gl_VertexIndex为命令缓冲区中的与firstIndex相应的索引值加上vertexOffset。

从OpenGL转来的程序员注意gl_InstanceIndex语义同gl_InstanceID不同(gl_InstanceID从0开始,不计算gl_BaseInstance)。
gl_DrawID是从OpenGL所使用的GLSL继承而来的,因此不写作gl_DrawIndex。

顶点着色器的内置输出

用GLSL编写的顶点着色器有以下内置输出:

预定义块gl_PerVertex 的成员

vec4 gl_Position

顶点位置

float gl_PointSize

点的大小,适用于绘制点

float gl_ClipDistance[]

自定义剪裁参数

float gl_CullDistance[]

自定义剔除参数

  • 所有的输出都是可选的,非必须输出。

  • gl_PerVertex意味着这些输出是相对于每个顶点而言的,gl_PerVertex是没有实例名称的预定义块,意味着你不需要使用任何前缀就可以直接使用它的成员,即直接写gl_Position而不是写gl_PerVertex.gl_Position

gl_Position

gl_Position与片段渲染在屏幕上的位置及深度值的关系参见NDC坐标
如果顶点着色器之后只有一个片段着色器,或没有任何着色器(旨在不输出颜色,只输出深度值到片段所在位置),应当输出gl_Position(若是连gl_Position都不必输出的情形,必是纯粹的计算和数据存储,应使用//TODO Ch4-6 计算着色器)。
gl_Position在栅格化时发生插值,插值方式考虑透视(即相当于插值修饰符smooth)。

gl_ClipDistance

gl_ClipDistance数组中的每个元素代表着到不同剪裁平面的距离。
“剪裁”的效果大致等价于:对于任意片段,若gl_ClipDistance数组中的任一元素数值小于0,则该片段被丢弃。
上述只是一种直观且便于理解的解释,因为这里所谓的剪裁发生在栅格化前,其底层实现可能是修改图元,并不需要涉及片段。

剪裁可能会生成新的顶点,例如:
若三角形有一个角被裁掉,会生成两个新的顶点构成新的边,变成一个四边形(即两个三角形图元)。
(该行为的结果影响管线统计

要使用gl_ClipDistance,需开启设备特性中的shaderClipDistance,gl_ClipDistance数组元素个数最大不超过硬件限制中的maxClipDistances。

利用gl_ClipDistance在3D渲染中将模型切开:

_images/ch4-2-2.png
  • 用例:有一种简单粗暴的渲染镜面效果的方法——将物体沿着镜面翻转然后再渲染一遍,典型的情况比如渲染物体在水面上的倒影,倒影当然只会映照出水面以上的物体,利用gl_ClipDistance即可轻易地达成这一效果。

绘制三角形的三条边(图元类型为三角形,绘制模式为VK_POLYGON_MODE_LINE),令gl_ClipDistance[0]为gl_Position.x的效果如下:

_images/ch4-2-1.png
  • 在英伟达显卡上你可能会看到中央被截断的地方也构成了一条边,那是硬件特定的行为。

gl_CullDistance

gl_CullDistance数组中的每个元素代表着到不同剔除平面的距离,则:
对于任意图元,若构成其图形的所有顶点皆满足到某一剔除平面的距离小于0(比如gl_CullDistance[0]都小于0),则图元被整个丢弃。

要使用gl_CullDistance,需开启设备特性中的shaderCullDistance,gl_CullDistance数组元素个数最大不超过硬件限制中的maxCullDistances。