3.4.3顶点着色器阶段处理
我们现在知道顶点着色器可以从输入装配程序接收哪些数据作为输入数据,以及哪些资源可以被主机应用程序绑定。
- 顶点绘冲区
- 常量缓冲区
- 着色器资源视图
我们还知道,顶点着色器程序提供了对单个顶点的自定义处理,而与调用管道的拓扑无关。那么,在顶点着色器程序中执行哪些类型的操作呢?某些类型的操作更适合这个阶段的组件吗?我们将考虑顶点着色器阶段通常用于哪些操作,并评论如何最好地使用管道的这一阶段。
几何操作/Geometric Manipulation
顶点着色器传统上用于在输入数据之后在管道中光栅化之前对其执行几何操作。这是因为模型的顶点包含表示渲染对象的几何曲面的信息。由于所有顶点都在管道通过顶点着色器,因此这是执行几何操作的逻辑位置。几个例子可以很容易地阐明这个主题。
通常在顶点着色器中执行的操作是将变换矩阵应用于顶点位置。此操作修改顶点的输入位置属性,以反映场景中几何体的位置,方法是将顶点位置与应用程序在常量缓冲区中提供的转换矩阵进行矩阵相乘。此外,对模型坐标系敏感的任何顶点属性也必须通过类似的矩阵相乘转换到新的坐标系中。顶点的位置和它们的法向量都定义了模型的物理表示,该物理表示通过变换矩阵进行修改。通过在顶点着色器中执行此计算,我们可以确定,在进一步处理之前,模型的所有几何体都将被转换到所需的坐标空间。
顶点着色器阶段中经常执行的另一个操作是顶点蒙皮。在模型上执行不同类型的坐标变换,其中骨骼在骨骼系统中按层次链接,每个顶点都指定给一个或多个骨骼。这些骨骼以不同的方式移动模型的不同部分,例如,模拟移动手臂时骨骼如何移动皮肤。与上面的标准转换示例一样,此操作修改模型的物理结构以提供所需的姿势。在进一步处理之前,在顶点着色器中执行此操作也是合适的。
顶点光源
几何操作和顶点蒙皮可以在顶点着色器中有效地执行,因为它们可以直接在顶点表示的数据上执行渲染几何体的物理形状、结构、方向和位置。当然,这些并不是可以在顶点着色器中执行的唯一操作类型。因为这个阶段是可编程的,所以可以在顶点着色器中执行许多特殊类型的处理,然后再传递到后面的阶段。这种类型的操作的一个常见示例是逐顶点照明。逐顶点照明计算从顶点反射的光量,通常使用某种形式的简化照明模型(Hoxley)。光照可以存储为输出顶点格式中的三分量或四分量浮点属性,其中值1.0表示有足够的颜色可用,0.0表示完全没有该颜色。
逐顶点照明值通过管道传递,最终到达光栅化器阶段,光栅化器将它们作为属性插入限制中的每个顶点之间。然后将这些插值应用于光栅化器生成的每个像素,并最终用于确定写入渲染目标的最终颜色。通过在顶点着色器中执行这些计算,照明量仅在稀疏的点集(顶点处)计算,而不是在模型生成的每个像素计算。生成的值在顶点之间进行插值,只要顶点在屏幕上相距不太远,计算的分辨率就会降低。
常规逐顶点计算
许多其他类型的计算可以遵循类似于上述照明的模型。如果可以在顶点着色器阶段执行计算,并且可以在每个像素级别使用数据的插值版本,那么在顶点着色器中执行计算是有意义的,因为每个顶点只执行一次计算。如果计算是在光栅化后进行的,则会执行更多次。
图3.16显示了顶点计算和每像素计算之间的计算频率差异。
从数学上讲,如果计算是其输入的线性组合,那么由于顶点属性之间的插值,逐顶点计算的结果将与逐像素计算的结果相同。即使计算不是线性的,逐顶点计算仍然可以提供相当好的全像素计算的近似值。此近似的有效性取决于计算的类型,以及最终光栅化基本体在最终渲染目标中显示的大小。如果在顶点之间生成的像素数量非常少,则插值的任何不准确都不太可能被注意到。这也意味着,若几何测量的输入集被创建为具有更大数量的顶点,则插值效果将不那么明显,代价是要处理的顶点数量增加。没有简单的规则来确定特定模型所需的详细程度。这是可用处理能力和所需图像质量的函数。作为本章后面内容的一个小指示,新的曲面细分阶段旨在通过动态生成适当数量的顶点,仅在需要的地方(可见的地方),在这方面取得平衡。这降低了整体顶点处理成本,同时可以产生小屏幕空间大小的基元,从而有效地提高了相同所需处理量的可用图像质量。
控制点处理
使用顶点着色器阶段的另一种方法是处理实际上不是顶点的数据,而是高阶基本体或控制面片的控制点。这些控制点仍然封装了位置的概念,也许还有方向的概念,即使它们没有直接定义网格的几何表面。因此,可以在顶点着色器中轻松地操纵它们。在这种情况下,处理后的控制点将沿着管线传递到曲面细分阶段,在那里对它们进行评估并用于生成顶点,这些顶点将实际定义网格的几何曲面。控制点的类型、控制点中包含的信息,以及以后如何评估控制点以生成顶点,都由开发人员决定,并在可编程阴影程序中实现,从而提供了高度的灵活性。在整本书中,这个主题将多次被重新审视(尤其是在第4章“细分管道”以及几个示例算法章节中)。
顶点缓存
在本章的前面,我们讨论了如何对每个顶点调用一次顶点着色器程序。虽然每个顶点有效地执行一次顶点着色器阶段,但是单独处理的顶点结果可以在多个基元之间共享。例如,如果一个顶点由三角形条带中的两个三角形基元共享,则可以对其进行一次处理,并且其结果可以在稍后的管道中,即在顶点着色器阶段之后,在两个三角基元中使用。使用任何“条带”基元拓扑类型都将允许这种类型的顶点重用。此外,当使用索引渲染时,即使是“列表”基本体拓扑类型也可以定义共享相同顶点的多个基本体。与无法共享顶点的渲染技术相比,这可以显著减少要处理的顶点数。
例如,考虑图3.17中所示的几何形状。在这三种情况中的每一种情况下,都使用不同的基元拓扑来定义所需的模型,其中要处理的顶点数量各不相同。
关于顶点缓存的一个额外考虑是,此操作依赖于硬件。缓存的大小(甚至它的存在)在不同的CPU之间可能有很大的差异。因此,最佳做法是确保在顶点或输入中尽可能紧密地引用任何共享顶点。在“条形”基元拓扑类型的情况下,这是自动完成的,因为每个基元都使用其前面的顶点来创建新的基元。在索引渲染的情况下,引用同一顶点的索引应尽可能紧密地放在一起,以确保缓存的顶点可以重用。