实时渲染(第三版):第三章 图形处理单元 3.5 3.6

来源:互联网 发布:来访人员软件 编辑:程序博客网 时间:2024/05/18 01:14
 

3.5 几何着色器

    在2006年后期,随着DIrectX 10的发布,几何着色器被增加到硬件加速图形管道。它在管道中紧随顶点着色器之后,它的使用是可选的。它是着色器模型4.0的必选部分,但并没有在早期的着色器模型中使用。

    几何着色器的输入是一个单独的对象及其顶点。该对象通常是网格中的一个三角形、一个线段或者只是简单的一个点。另外,几何着色器可以定义和处理扩展的基元。特别地,可以传入三角形以外的三个顶点,还可以使用折线中的两个相邻的顶点,参考图3.6。


图 3.6 几何着色器的输入是单独的某种类型:点、线段、三角形。另外,还可以使用右侧的两个基元,它们包含和线、三角形对象相邻的顶点。

    几何着色器处理基元,输出零个或多个基元,以点、折线和三角带的形式。例如,单独的一个几何着色器程序的调用可以输出一个以上的三角带。需要重点注意的是,几何着色器也可以没有任何输出。在这种情况下,网格能够有选择性修改(编辑顶点、增加新基元、删除其他的东西)。

    几何着色器程序被设置为输入一种类型的对象,输出一种类型的对象;输入和输出的类型无须匹配。比如,输入多个三角形,但输出它们的重心(多个点)。即使输入和输出的类型匹配,各个顶点所携带的数据也可能被省略或扩展。例如,可以计算三角形的平面法线,将之添加到每个输出的顶点数据。和顶点着色器类似,几何着色器必须为产生的每个顶点输出一个同质剪切空间位置。

    几何着色器确保输出结果和基元的输入次序相同。这影响性能,因为,如果若干着色器单元并行运行,则结果就必须被保存并排序。作为功能和效率的折中,在着色器模型4.0中,每个执行中可以生成的值有一个限制,即不能超过1024个32位值。因此,生成上千个灌木叶但使用单个的叶子作为输入是不可行的,也不是几何着色器的推荐用法。另外,也不推荐使用简单表面到更加缜密的三角形网格的镶嵌[123]。该阶段关注的是用程序修改输入数据或生成有限数目的拷贝,而不是大规模地替换或放大。例如,生成六个转换后数据拷贝以同时渲染立方图的六个面(参考节8.4.3)。另外几种可以充分利用几何着色器的算法有:从点数据创建各种大小的粒子;挤压轮廓上的毛刺以进行皮毛渲染;为阴影算法寻找对象的边缘。图3.7显示了更多的内容。这些应用和其他一些应用将在本书的其余部分进行讨论。


图 3.7 几何着色器的一些应用。左侧,使用GS(几何着色器)即时执行元球(metaball)等值面镶嵌。中间,使用GS和流输出执行线段的分形细分,并用GL生成告示板用于显示。右侧,使用顶点着色器和GS(带流输出)进行布料模拟。

3.5.1 流输出

    GPU管道的标准应用是发送数据给顶点着色器,然后光栅化产生的三角形并在像素着色器中处理它们。数据总是通过管道,但无法访问中间的结果。流输出的思想在着色器模型4.0中引入。在顶点被顶点着色器(还有可选的几何着色器)处理之后,除了发送给光栅化阶段之外,它们还可以输出到一个流(如,一个有序数组)中。事实上,完全可以关闭光栅化,然后将管道纯粹地当作非图形的流处理器。这种方式下,处理的数据可被送回管道,可以进行迭代处理。这种类型的操作对模拟水的流动或其他粒子效果特别有用,如节10.7所述。

3.6 像素着色器

    在顶点和几何着色器之后,基元被剪切并做好光栅化准备(在后面章节介绍),这些阶段在其处理步骤上相对固定,不可编程(除了一个例外,即像素着色器程序可以指定插值的类型,如投影修正或屏幕空间或不使用)。三角形被遍历,顶点的值被跨三角形区域进行插值。像素着色器是下一个可编程阶段。在OpenGL中,这个阶段被称为片段着色器,从某种意义上说,它是一个更加贴切的名字。它的意思是说,三角形全部或部分地覆盖了像素单元,所描绘的材质要么透明要么不透明。光栅化不会直接影响像素的存储颜色,但它会在一定程度上生成描述三角形如何覆盖像素单元的数据。之后,在合并阶段,使用片段数据修改像素存储的内容。

    顶点着色器程序的输出成为像素着色器程序的输入。在着色器模型4.0中,共计16个向量(每个向量有4个值)可以从顶点着色器传递给像素着色器(在DirectX 10.1中,顶点着色器的输入和输出都是32个向量)。当使用几何着色器时,它可以输出32个向量到像素着色器[261]。

    随着着色器模型3.0的引入,额外增加了像素着色器的输入。比如,三角形的哪个面可见被增加为一个输入标志。这点对于在一个单独的通道中对三角形的前后面渲染不同的材质很重要。片段的屏幕位置在像素着色器中也有效。

    像素着色器的限制是它只能影响传递给它的片段。这就是说,当一个像素着色器程序执行时,它无法直接地向相邻的像素发送结果。相反地,它使用来自顶点的插值数据,和任何存储的常量及纹理数据一起,计算结果,而该结果只能影响一个单独的像素。不过,这个限制并不像它听起来那般严重,因为相邻的像素最终可以被使用图像处理技术进行处理,如节10.9所述。

    像素着色器访问相邻像素的信息(尽管不那么直接)的一种情况是,梯度或衍生信息的计算。像素着色器拥有一种能力,它可以采用任意的值并计算一个数量,该数量是它沿着屏幕的x和y坐标轴改变的每个像素的个数。这在各种计算和纹理取址中有用。这些梯度对诸如过滤(参考节6.2.2)之类的操作特别重要。大多数GPU实现了这个特性,以2*2或更多为一组进行像素处理。当像素着色器需要一个梯度值时,就返回相邻像素间的差值。该实现的后果之一是梯度信息无法被着色器中的某些部分(如受动态流控制影响的部分--组中的所有像素都必须处理相同的指令)所访问。即时在离线渲染系统[31]中,这也是一个根本性的限制。访问梯度信息是像素着色器特有的能力,其他任何可编程着色器阶段都无法共享。

   像素着色器程序通常为最后的合并阶段设置片段的合并色;它也可以修改光栅化阶段生成的深度值。它不能修改模板值,而是把它传递给合并阶段。在着色器模型2.0及其以上版本中,像素着色器还可以丢弃输入的片段数据,如不产生输出。这些操作可能会以性能为代价,因为这之后无法使用GPU经常执行的优化(参考节18.3.7)。在着色器模型4.0[123]中,雾化计算和alpha测试之类的操作已经从合并阶段移动到像素着色器中。

   目前的像素着色器能够执行大量的处理。在一个单独的渲染通道中计算任何数目的值的能力带来了多渲染目标(MRT)的想法。我们并不将像素着色器程序的结果保存到一个单独的颜色缓冲,而是为每个片段产生多个向量,并将这些向量保存到不同的缓冲中。这些缓冲必须是相同的尺寸,部分架构还要求它们拥有相同的位深(虽然根据需要,它们会拥有不同的格式)。和可显示的颜色缓冲不同,任何额外目标还有其他限制。例如,通常无法执行抗锯齿。虽然有这些限制,但MRT功能对更加有效地执行渲染算法是一个强大的辅助。如果要从相同的数据集计算若干中间结果图像,那么仅需要一个单独的渲染通道,而不是每个输出缓冲一个通道。和MRT相关的另一个关键能力是把这些结果图像当作纹理并进行读取。