3.3 可编程着色的演变
可编程着色框架的思想要回溯到1984年Cook的着色树[194]。图3.3显示了一个简单的着色器及其着色树。在80年代后期,根据这种思想,开发了着色语言RenderMan[30, 1283]。它在今天仍然广泛地用于电影产品渲染。在GPU本地支持可编程着色器之前,曾多次尝试使用多渲染通道实现可编程着色器的实时操作。在1999年,《Quake III: Arena》(雷神之锤3:竞技场)的脚本语言是此领域中第一个具有普遍性的商业上的成功[558,604]。在2000年, Peercy et al. [993]描述了一个系统,能够将RenderMan着色器在图形硬件上转换为多路运行。他们发现,要使这个系统能被广泛应用,还缺少GPU的两个特性:使用计算结果作为纹理坐标(依赖性纹理读取)的能力、在纹理与颜色缓冲中对扩展数据类型的范围和精度的支持。建议的一种数据类型是新的(当时的)16位浮点数表示。在当时,还没有商业上的GPU支持可编程着色器,虽然它们大多数都拥有高度可配置管道[898]。
图 3.3 简单的红铜色着色器的着色树,及其响应的着色语言程序。
在2001年,NVIDIA的GeForce 3是第一个支持可编程顶点着色器[778]的GPU,公开于DirectX 8.0和OpenGL扩展中。这些着色器使用类似汇编的语言(将被驱动联机转换为微码)进行编程。DirectX 8.0还包含了像素着色器,但像素着色器 SM 1.1 缺乏实际的编程性--有着相当限制(不多于12条指令,以及上面Peercy et al所说的两个元素)的这些程序被驱动转换到纹理混合状态(一起按次序进行硬件寄存器组合)。
这个时期的着色器还不支持流控制(分支),因此,条件必须通过计算所有的条目,再在结果中进行选择或插值来模拟。DirectX定义了一种着色器模型的概念以识别不同着色器能力的硬件。GeForce 3支持顶点着色器模型 1.1 和像素着色器模型 1.1(着色器模型 1.0 对应的硬件从来没上市过)。在2001,GPU向通用像素着色器编程模型发展。DirectX 8.1 添加了像素着色器模型 1.2 到 1.4(不同的版本用于不同的硬件),它在将来扩展了像素着色器的能力,添加了额外的指令和对依赖性纹理读取的更加广泛的支持。
2002年,DirectX 9.0发布,它包含了着色器模型 2.0(还有扩展版本2.X),实现了真正的可编程顶点和像素着色器。类似的功能也在OpenGL中通过各种扩展公布。添加了对任意依赖性纹理读取和16位浮点数的支持,最终完成了Peercy et al. 在2000年所说的需求元素。着色器增加了资源(如指令,纹理和寄存器)的上限,能够实现更加复杂的效果;还增加了流控制。着色器长度和复杂性的增长使得汇编编程模型更加笨重。幸运的是,DirectX 9.0还包含了一种新的着色器编程语言,绝爱哦作HLSL(高级着色器语言)。HLSL由微软和NVIDIA协作开发;NVIDIA还发布了自己的跨平台的变种,叫做Cg[818]。差不多同一个时间,OpenGL ARB(
Architecture Review Board)发布了针对OpenGL的同类语言,叫做GLSL[647, 1084](也叫做GLslang)。这些语言严重受到C语法和设计原理的影响,还包含着色语言RenderMan 的元素。
着色器模型 3.0在2004年引入,它是一种增量性的进步:将可选的特性转变为必须,进一步增加了资源上限,并在顶点着色器中增加了有限的纹理读取支持。当新一代游戏控制台(2005年后期:微软的Xbox 360,2006年:索尼PS3)被引入时,它们就装配了着色器模型3.0级别的GPU。固定功能管道没有完全绝迹:Nintendo的Wii控制台在2006后期上市,使用的就是固定功能GPU[207]。只不过,这差不多就是这种类型的最后一种控制台了;甚至,移动设备,如手机,也嗯哪个使用可编程着色器(参考节 18.4.3)。
也存在着色器开发的其他语言和环境。比如,Sh语言[837,838]允许GPU着色器通过一个C++库进行生成和组合。该开源项目运行于若干平台。另一方面,还引入几种可视化编程工具,可以让艺术家(他们中的大多数都不习惯C之类的编程)设计着色器。这些工具包含可视化图形编辑器(用来链接预定义的着色器构造块)和编译器(转换结果图形到着色语言)。图3.4显示了这种工具中的一种(mental mill,包含于NVIDIA的FX omposer 2)。McGuire et al. [847]调查了可视化着色器编程系统,提出了一种高级、抽象的概念扩展。
图 3.4 一个着色器设计的可视化图形系统。各种操作都被封装在函数框中,可在左侧选择。选择后,函数框在右侧显示函数的可调参数。函数的输入和输出框互相链接,以形成最终的结果。
编程性的一大步发展发生于2007年。着色器模型4.0(包含于DirectX 10.0[123]以及OpenGL的扩展),引入了几种重要的特性,比如几何着色器和流输出。
着色器模型4.0包含了一种用于所有着色器(顶点、像素和几何)的uniform编程模型,即前面所说的通用着色器内核。资源上限又被提高了,并且添加了对整数(包括位操作)的支持。另外,着色器模型4.0也指明,它仅支持高级语言着色器(DirectX的HLSL和OpenGL的GLSL),不再提供以前模型中的汇编语言接口。
GPU厂商、微软、和OpenGL ARB精化和扩展了可编程着色的能力。另外,新的编程模型,如NVIDIA的CUDA [211]和AMD的CTM[994],将目标指向了非图形的应用。GPU上的通用目的的计算(GPGPU)将在节18.3.1着重讨论。
3.3.1 着色器模型对比
略...
3.4 顶点着色器
顶点着色器是功能管道中的第一个阶段,如图3.2所示。虽然它是作任何图形处理的第一个阶段,但也要注意,在此之前需要一些数据操作。在DirectX的输入汇编器(input assembler)[123, 261]中,若干数据流被编织在一起,形成顶点和基元的集合,被下送给管道。比如,一个对象可以用一个位置数组和一个颜色数组表示。那么,输入汇编器将通过创建带有位置和颜色的顶点来创建该对象的三角形(或线或点)。第二个对象可能使用相同的位置数组(但使用不同的模型转换矩阵)和一个不同的颜色数组来表示。数据表示在节12.4.5中讨论。在输入汇编器中还支持实例化(instancing)。这允许对象可被使用一个单独的绘画调用进行多次绘画(每个实例使用不同的数据)。实例化的使用在节15.4.2中介绍。DirectX 10中输入汇编器还为每个实例、基元和顶点标上标识;该标识是一个数字,可以在之后的任何一个着色器阶段访问它。对于以前的着色器模型,这个数据必须显式指定。
三角形网格表示为顶点的集合和形成三角形的各个顶点的额外信息。顶点着色器是处理三角形网格的第一个阶段。形成三角形的数据对顶点着色器无效;正如其名字所代表的意思,它只处理输入的顶点。更通俗地讲,顶点着色器提供了一种方法来修改、创建和忽略与各个多边形的顶点关联的值(如颜色、法线、纹理坐标和位置)。通常,顶点着色器程序将顶点从模型空间转换到同质剪切空间;顶点着色器必须总是输出该位置。
该功能最先在2001年由DirectX 8引入。因为它是管道的第一个阶段,调用相对较少,所以可以在GPU或CPU上实现,它将把结果发送给GPU以进行光栅化。这么做将导致硬件从旧到新的过渡存在速度问题,而不是功能问题。当前生产的所有GPU都支持顶点着色。
顶点着色器本身和节3.2中叙述的通用内核虚拟机非常相似。顶点着色器程序处理每个传入的顶点,然后输出若干跨三角形或线的插值(老的着色器模型还支持输出点粒子对象的大小,该功能现在是几何着色器的一部分)。顶点着色器无法创建也无法销毁顶点;为某个顶点产生的值也无法传递给其他的顶点。由于每个顶点都被分别单独地对待,那么,在GPU上,任何数目的着色器处理器都可并行地应用到输入的顶点流。
下面的章节介绍了若干顶点着色器其效果,如阴影体的创建、用于动画拟合的顶点混合以及轮廓渲染。顶点着色器的其他用途有:
- 镜头效果:屏幕呈现为鱼眼、在水下,或其他方式的扭曲。
图 3.5 左侧是一个普通的茶壶。中间的经过了简单的剪切操作。右侧,噪声函数创建了扭曲模型的视野。
- 对象定义:网格仅创建一次,然后顶点着色器将其变形。
- 对象扭曲、弯曲和锥体操作。
- 过程变形,如旗帜、衣服的飘动,水的流动。
- 基元创建:向管道下发退化网格,让网格数据占据所需的空间。在更新的GPU上,该功能被几何着色器替代。
- 页面卷曲,热雾,水波纹等效果:使用整个帧缓冲的内容作为一个纹理,然后进行过程变形。
- 使用顶点纹理获取(着色器模型3.0以上)将纹理应用到顶点网格,这允许廉价地应用海洋表面和地形高度场[23, 703, 887]。
图3.5显示了顶点着色器的一些变形操作的效果。
顶点着色器的输出有若干种用法。一般是让各个实例的三角形随后被生成或光栅化,把生成的各个像素片段发送给像素着色器程序以继续处理。随着着色器模型4.0的引入,数据还可以继续被发送给几何着色器、流输出。这些项目是下面小节的主题。