RTR4 第三章 图形处理单元 3.4-3.6
***本章节由于本人了解度较低,查阅部分资料后可能仍有较多名词表述翻译问题,欢迎讨论指正。部分名词考虑到使用习惯可能会酌情不翻译请注意***3.4 可编程着色和API的演变
可编程着色框架的构想可以追溯到1984年,当时Cook的着色树。图3.4中显示了一个简单的着色器及其相应的着色树。RenderMan 着色语言是在1980年代后期基于此思想开发的。今天,它与其他不断发展的规范(例如,Open Shading Language(OSL)项目)一起仍用于电影制作渲染。
图3.4 一个简单的铜着色器的着色树及其相应的着色器语言程序(在Cook 之后)
消费级图形硬件由3dfx Interactive公司于1996年10月1日首次成功引入。有关该年开始的时间表,请参见图3.5。他们的Voodoo显卡能够以高质量和高性能渲染游戏《雷神之锤》,因此很快就被采用。该硬件实现了固定功能的管线。在GPU原生支持可编程着色器之前,曾有几次尝试通过多次渲染过程实时实现可编程着色操作。
图3.5 一些API和图形硬件版本的时间表
雷神之锤3:竞技场 的脚本语言是1999年在该领域的首个广泛性的商业上的成功。如本章开头所述,NVIDIA的GeForce256是第一个被称为GPU的硬件,但它不可编程。 但是,它是可配置的。
在2001年初,NVIDIA的GeForce 3是第一个支持可编程vertex着色器的GPU,可通过DirectX 8.0和OpenGL扩展进行公开。这些着色器以类似于汇编的语言进行编程,驱动程序将这些着色器即时转换为微码。像素着色器也包含在DirectX 8.0中,但是像素着色器没有达到实际的可编程性-驱动程序将受支持的有限“程序”转换为纹理混合状态,然后将其连接在一起,将硬件“寄存器组合器”连接在一起。这些“程序”不仅被限制了长度(不超过12条指令),而且缺少重要的功能。Peercy等人从对RenderMan的研究中发现,依赖的纹理读取和浮点数据对于真正的可编程性至关重要。
着色器此时不允许进行流控制(分支),因此可以通过计算两个项以及在结果之间进行选择或插值来模拟条件着色。 DirectX定义了一个着色器模型(Shader Model,简称SM)的概念,以区分具有不同着色器功能的硬件。 2002年,DirectX9.0发布,其中包括Shader Model 2.0,该着色器具有真正可编程的顶点和像素着色器。使用各种扩展在OpenGL下也暴露了类似的功能。添加了对任意依赖纹理读取的支持以及16位浮点值的存储,最终完成了Peercy等人确定的一组要求。着色器资源(例如指令,纹理和寄存器)的限制增加了,因此着色器变得能够具有更复杂的效果。也添加了对流控制的支持。着色器的长度和复杂性不断增长,使得汇编编程模型变得越来越麻烦。幸运的是,DirectX 9.0也包含了HLSL。这个着色语言是由Microsoft与NVIDIA合作开发的。大约在同一时间,OpenGL ARB(OpenGL架构审查委员会)发布了GLSL,这是一种与OpenGL非常相似的语言。这些语言在很大程度上受到C语言的语法和设计理念的影响,并包含来自RenderMan着色语言的元素。
Shader Model 3.0于2004年推出,并添加了动态流控制,使着色器功能更加强大。它还将可选功能转变为需求,进一步增加了资源限制,并增加了对顶点着色器中纹理读取的有限支持。当在2005年末(微软的Xbox 360)和2006年末(索尼电脑娱乐的PLAYSTATION3系统)推出新一代游戏机时,它们都配备了Shader Model 3.0级GPU。任天堂的Wii是值得注意的最后几个具有固定功能的GPU之一,该GPU最初于2006年末交付。在这个时间点,纯固定功能的管道早已不复存在。着色器语言已经发展到可以使用各种工具来创建和管理它们的程度。图3.6显示了使用Cook的着色树概念的一个这样的工具的屏幕快照。
图3.6:用于着色器设计的可视着色器图形系统。各种操作都封装在功能框内,可在左侧选择。选中后,每个功能框都有可调参数,如右侧所示。每个功能框的输入和输出相互链接以形成最终结果,如中间框架的右下角所示。(来自mentalimages inc.的“ mental mill”截图)
可编程性的下一个巨大的进步也在接近2006年底的时候到来。被包含于DirectX 10.0 中的ShaderModel 4.0发布。它引入了几个主要功能,例如几何体着色器和流输出。Shader Model 4.0包括针对所有着色器(顶点,像素和几何着色器)的统一编程模型,这个统一着色器设计在早些章节中已经描述。 资源限制进一步增加,并增加了对整数数据类型(包括按位运算)的支持。 OpenGL 3.3中GLSL3.30的引入也提供了类似的着色器模型。
2009年发布了DirectX 11和Shader Model 5.0,其中添加了曲面细分着色器和计算着色器(Comupte Shader,以下仍然使用Compute Shader描述),也称为DirectCompute。该版本还专注于更有效地支持CPU多处理,这是第18.5节中讨论的主题。 OpenGL在版本4.0中添加了细分,而在4.3中添加了Compute Shader。DirectX和OpenGL的发展方式有所不同。两者都为特定版本发布设置了一定程度的硬件支持。 Microsoft控制DirectX API,并直接与独立硬件供应商(IHV)(例如AMD,NVIDIA和Intel)以及游戏开发人员和计算机辅助设计软件公司合作,以确定要暴露的功能。 OpenGL由非营利组织Khronos Group管理的硬件和软件供应商联盟开发。由于涉及的公司数量众多,API功能通常在DirectX中引入OpenGL之后的某个时间出现。但是,OpenGL允许扩展(特定于供应商或更通用的扩展),从而允许在正式支持发行版之前就能使用最新的GPU功能。
API的下一个重大变化是由AMD在2013年推出了MantleAPI。Mantle是与视频游戏开发商DICE合作开发的,其初衷是消除大部分图形驱动程序的开销,并将此控件直接交给开发人员。除了这种重构之外,还进一步支持有效的CPU多处理。这类新的API的专注于大大减少CPU在驱动程序上花费的时间以及更有效的CPU多处理器支持(第18章)。在Mantle中率先提出的想法已由Microsoft采纳,并于2015年作为DirectX 12发布。请注意,DirectX 12并不专注于暴露新的GPU功能-DirectX 11.3暴露了相同的硬件功能。两种API均可用于将图形发送到虚拟现实系统,例如OculusRift和HTCVive。但是,DirectX 12是对API的彻底重新设计,可以更好地映射到现代GPU架构。低开销的驱动程序对于应用程序在以下场景很有用:CPU驱动程序成本引起瓶颈,或者使用更多CPU处理器的图形可能会提高性能。从较早的API移植可能很困难,并且幼稚的实现可能会导致性能降低。
苹果在2014年发布了自己的低开销API(称为Metal)。Metal首先在iPhone 5S和iPad Air等移动设备上可用,一年后通过OS X El Capitan支持了新的Macintohes系统。除效率外,减少CPU使用率还可以节省功耗,对于移动设备来说这是一个很重要的因素。该API具有自己的着色语言,适用于图形和GPU计算程序。
AMD将其Mantle的技术提供给了Khronos Group,后者于2016年初发布了自己的新API,名为Vulkan。 与OpenGL一样,Vulkan可在多种操作系统上工作。 Vulkan使用一种称为SPIRV的新的高级中间语言,该语言可用于着色器表示和一般GPU计算。预编译的着色器是可移植的,因此可以在支持所需功能的任何GPU上使用。 由于不需要显示窗口,Vulkan也可以用于非图形的GPU计算。 Vulkan与其他低开销驱动程序的显着区别是,它旨在与多种系统一起使用,从工作站到移动设备。
在移动设备上,一般标准是使用OpenGL ES。“ ES”代表嵌入式系统,因为该API是为移动设备而开发的。标准OpenGL在当时在其某些调用结构中相当庞大且缓慢,并且需要支持很少使用的功能。 OpenGL ES 1.0于2003年发布,是OpenGL 1.3的简化版本,描述了固定功能的管道。虽然DirectX的发布与支持它们的图形硬件的发布保持同步,但是开发针对移动设备的图形支持却没有以相同的方式进行。例如,2010年发布的第一台iPad实现了OpenGL ES1.1。2007年,OpenGL ES 2.0规范已发布,提供了可编程的阴影。它基于OpenGL 2.0,但没有固定功能组件,因此与OpenGL ES 1.1不向后兼容。 OpenGL ES 3.0于2012年发布,提供了多个渲染目标,纹理压缩,转换反馈,实例化,更广泛的纹理格式和模式以及着色器语言改进等功能。 OpenGL ES 3.1添加了Compute Shader,并且3.2添加了几何和曲面细分着色器,以及其他功能。第23章更详细地讨论了移动设备的体系结构。
OpenGL ES的一个分支是基于浏览器的API WebGL,由JavaScript调用。该API的第一版于2011年发布,可在大多数移动设备上使用,因为它的功能等效于OpenGL ES 2.0。与OpenGL一样,扩展允许访问更高级的GPU功能。 WebGL 2假定支持OpenGLES 3.0.
WebGL特别适合尝试功能或在教室中使用:
它是跨平台的,可在所有个人计算机和几乎所有移动设备上使用。
驱动程序批准由浏览器处理。即使一个浏览器不支持特定的GPU或扩展,通常另一个浏览器也支持。
代码被解释而不是编译,并且仅需要文本编辑器即可进行开发。
大多数浏览器都内置了调试器,并且代码在任何网站运行都可以被检视到。
程序可以通过上传到网站或Github的方式来进行部署。
更高级别的场景图形和效果库(例如three.js )使您可以轻松访问各种更复杂效果的代码,例如阴影算法,后处理效果,基于物理的阴影和延迟渲染。
3.5 顶点着色器
顶点着色器是图3.2所示功能管线中的第一阶段。虽然这是直接在程序员控制下的第一阶段,但值得注意的是,某些数据操作在此阶段之前发生。在DirectX所谓的输入汇编器(input assembler)中,可以将几个数据流编织在一起,以形成沿管线发送的一组顶点和图元。例如,一个对象可以由一个位置阵列和一个颜色阵列表示。输入汇编器将通过创建具有位置和颜色的顶点来创建此对象的三角形(或直线或点)。第二个对象可以使用相同的位置数组(以及不同的模型转换矩阵)和不同的颜色数组表示。数据表示将在16.4.5节中详细讨论。输入汇编器中也支持执行实例化(instancing)。这允许一个对象被绘制多次,每个实例具有一些变化的数据,所有这些都可以通过一个drawcall进行。第18.4.2节介绍了实例化的使用。
三角形网格由一组顶点表示,每个顶点与模型表面上的特定位置关联。除了位置外,每个顶点还有其他可选属性,例如颜色或纹理坐标。在网格的顶点处也定义了表面法线,这似乎是一个奇怪的选择。在数学上,每个三角形都有一个定义明确的表面法线,直接使用三角形的法线进行着色似乎更有意义。但是,在渲染时,三角形网格通常用于表示基础曲面,而顶点法线通常用于表示该表面的方向,而不是三角形网格本身的方向。第16.3.4节将讨论计算顶点法线的方法。图3.7显示了两个三角形网格的侧视图,这些三角形网格代表曲面,一个平滑,另一个具有锐利的折痕。
图3.7 代表曲面(红色)的三角形网格(黑色,顶点法线)的侧视图。在左侧,平滑的顶点法线用于表示平滑表面。右边的中间顶点已被复制并指定了两个法线,表示折痕。
顶点着色器是处理三角形网格的第一阶段。 顶点着色器无法获取那些描述形成哪些三角形的数据。 顾名思义,它专门处理传入的顶点。顶点着色器提供了一种修改,创建或忽略与每个三角形的顶点关联的值的方法,例如其颜色,法线,纹理坐标和位置。 通常,顶点着色器程序会将顶点从模型空间(model space)转换为同构的裁剪空间(clip space)(第4.7节)。 顶点着色器必须始终输出该位置。
顶点着色器与前面描述的统一着色器几乎相同。传入的每个顶点由顶点着色器程序处理,然后输出多个在三角形或直线上内插的值。顶点着色器既不能创建也不能破坏顶点,并且一个顶点生成的结果不能传递到另一个顶点。由于每个顶点都是独立处理的,因此可以将GPU上任意数量的着色器处理器并行应用于传入的顶点流。
输入装配(input assembly)通常表示为在执行顶点着色器之前发生的过程。这是一个物理模型通常与逻辑模型不同的例子。从物理上讲,获取数据以创建顶点可能会在顶点着色器中发生,并且驱动程序将悄悄地为每个着色器添加适当的指令,这些指令对于程序员是不可见的。
接下来的章节介绍了几种顶点着色器效果,例如用于动画关节的顶点混合和轮廓渲染。顶点着色器的其他用途包括:
对象的生成,仅创建一次网格并通过顶点着色器使其变形。
使用蒙皮和变形技术对角色的身体和面部进行动画处理。
程序变形,例如旗帜,布料或水的运动。
通过在管线上发送简并的(无区域的)网格来创建粒子,并根据需要为其分配区域。
镜头变形,热雾,水波纹,页面卷曲等其他效果靠使用整个帧缓冲区的内容作为纹理堆对屏幕对齐的网格进行程序变形。
通过使用顶点纹理获取来应用地形高度场
使用顶点着色器完成的一些变形如图3.8所示。
图3.8左图是一个普通的茶壶。由顶点着色器程序执行的简单剪切操作将生成中间图像。右图中噪声函数创建了一个场使模型变形(图像由FX Composer 2制作,NVIDIA Corporation提供)
顶点着色器的输出可以通过几种不同的方式使用。通常的路径是对每个实例的图元(例如三角形)进行生成和栅格化,生成的各个像素片段将被发送到像素着色器程序以继续进行处理。在某些GPU上,数据也可以发送到曲面细分着色器或几何着色器或存储在内存中。这些可选阶段将在以下各节中讨论。
3.6 曲面细分着色器
细分阶段允许我们渲染曲面。 GPU的指令是获取每个表面描述,并将其转变为一组代表性的三角形。此阶段是GPU的可选功能,该功能首先在DirectX11中可用(并且是DirectX11必需的)。 OpenGL 4.0和OpenGL ES 3.2也支持它。
使用细分阶段有几个优点。曲面描述通常比提供相应的三角形本身更紧凑。除了节省内存外,此功能还可以防止CPU和GPU之间的总线(bus)成为形状或动画帧改变的动画角色或对象的瓶颈。通过为给定视图生成适当数量的三角形,可以有效地渲染表面。例如,如果一个球远离相机,则仅需要几个三角形。近距离观察时,最好用数千个三角形来表示。控制细节级别(level of detail)的能力还可以使应用程序控制其性能,例如,在较弱的GPU上使用较低质量的网格以保持帧速率。通常用平坦表面表示的模型可以转换为三角形的细网格,然后根据需要进行变形,或者可以对其进行细分,以便更低频率地执行昂贵的阴影计算。
细分阶段始终由三个元素组成。使用DirectX的术语,它们是壳着色器(hull shader),细分器(tessellator) 和域着色器(domain shader)。在OpenGL中,壳着色器 被称为细分控制着色器(tessellation control shader),而域着色器 被称为细分评估着色器(tesselation evaluation shader),虽然较为冗长,但更具描述性。固定功能细分器在OpenGL中称为图元生成器(primitive generator),可以看出,这确实是它的作用。
在第17章中详细讨论了如何指定和细分曲线和曲面。在此,我们简要概述了每个细分阶段的目的。首先,壳着色器的输入是一个特殊的面片(patch)图元。它由几个控制点组成,这些控制点定义了细分表面,贝塞尔面片或其他类型的弯曲的元素。壳着色器具有两个功能。首先,它告诉细分器应生成多少个三角形以及以何种配置生成。其次,它对每个控制点执行处理。同样,壳着色器具有可以修改传入的补丁说明,根据需要添加或删除控制点的可选功能。 壳着色器将其控制点集以及细分控制数据输出到域着色器。 见图3.9。
图3.9 细分阶段。壳着色器采用由控制点定义的面片。它将细分因子(TFs)和类型附加到固定功能的细分器上。控制点集由壳着色器根据需要进行转换,并与TF和相关的面片常量一起发送到域着色器。细分器将创建一组顶点及其重心坐标。然后由域着色器对其进行处理,生成三角形网格(显示控制点以供参考)
细分是管线中的固定功能阶段,仅与曲面细分着色器一起使用。它的任务是为要处理的域着色器添加几个新的顶点。壳着色器向细分器发送有关所需细分曲面类型的信息:三角形,四边形或等值线。等值线是线带的集合,有时用于毛发渲染。壳着色器发送的其他重要值是曲面细分因子(OpenGL中的曲面细分级别)。这里有两种类型:内边缘和外边缘。这两个内部因素决定了三角形或四边形内部发生了多少细分。外部因素决定了每个外部边缘被分割了多少(第17.6节)。图3.10显示了增加镶嵌细分因子的示例。通过允许单独的控制,无论内部如何细分,我们都可以使相邻曲面的边缘在细分中匹配。匹配的边缘可以避免在面片相连之处产生裂纹或其他阴影伪影。顶点被分配了重心坐标(第22.8节),这些值指定了所需表面上每个点的相对位置。
图3.10改变细分因子产生的影响。*犹他州茶壶*由32个小块组成。内部和外部细分因子从左到右分别为1、2、4和8(Rideout和Van Gelder 演示生成的图像)
壳着色器始终输出面片和一组控制点位置,但是可以通过向细分器发送零或更少(或无值,NaN)的外部细分级别来发出信号,表示将丢弃该贴片。 否则,细分器将生成网格并将其发送到域着色器。 域着色器的每次调用都使用来自壳着色器的曲面的控制点来计算每个顶点的输出值。 域着色器具有类似于顶点着色器的数据流模式,其中处理来自细分器的每个输入顶点并生成相应的输出顶点。 然后将形成的三角形沿管线向下一阶段传递。
尽管此系统听起来很复杂,但以这种方式进行结构化以提高效率,使得每个着色器都可以相当简单。传递到壳着色器中的面片通常很少或不做修改。该着色器还可以使用面片的估计距离或在屏幕大小来动态计算细分因子,例如在执行地形渲染的时候。或者,壳着色器可以简单地为应用程序计算和提供的所有面片传递一组固定的值。细分器执行一个复杂的(involved)但固定功能的过程,以生成顶点,为其指定位置并指定它们形成的三角形或直线。此数据放大步骤是在着色器外部执行的,以提高计算效率。域着色器将获取为每个点生成的重心坐标,并将其用于面片的求值方程(patch's evaluation equation)中以生成所需的位置,法线,纹理坐标和其他顶点信息。有关例,请参见图3.11。
图3.11。左图是大约6000个三角形的基础网格。在右图,每个三角形都使用PN三角形细分进行细分和置换。(图像来自NVIDIA SDK 11 样本,由NVIDIA Corporation提供,4A Games的Metro 2033模型提供。)
(按出现顺序,不重复标记)
Cook, Robert L., “Shade Trees,”Computer Graphics (SIGGRAPH ’84 Proceedings), vol. 18,no. 3, pp. 223–231, July 1984.Cited on p. 37, 765
Apodaca,AnthonyA.,andLarryGritz,Advanced RenderMan: Creating CGI for MotionPictures, Morgan Kaufmann, 1999.Cited on p. 37, 909
Upstill,S.,The RenderMan Companion:A Programmer’s Guide to Realistic ComputerGraphics, Addison-Wesley, 1990.Cited on p. 37
Lindholm, Erik, Mark Kilgard, and Henry Moreton, “A User-Programmable Vertex Engine,”inSIGGRAPH ’01 Proceedings of the 28th Annual Conference on Computer Graphics andInteractive Techniques, ACM, pp. 149–158, Aug. 2001.Cited on p. 15, 38
Peercy,MarkS.,MarcOlano,JohnAirey,andP.JeffreyUngar,“InteractiveMulti-PassProgrammable Shading,” inSIGGRAPH ’00: Proceedings of the 27th Annual Conference onComputer Graphics and Interactive Techniques, ACM Press/Addison-Wesley Publishing Co.,pp. 425–432, July 2000.Cited on p. 38
Kessenich, John, Graham Sellers, and Dave Shreiner,OpenGL Programming Guide: The Of-ficial Guide to Learning OpenGL, Version 4.5 with SPIR-V, Ninth Edition, Addison-Wesley,2016.Cited on p. 27, 39, 41, 55, 96, 173, 174
Blythe, David, “The Direct3D 10 System,”ACM Transactions on Graphics, vol. 25, no. 3,pp. 724–734, July 2006.Cited on p. 29, 39, 42, 47, 48, 50, 249
Kubisch,Christoph,“TransitioningfromOpenGLtoVulkan,”NVIDIA GameWorksblog,Feb. 11, 2016.Cited on p. 40, 41, 796, 814
Chajdas, Matth aus G., “D3D12 and Vulkan: Lessons Learned,”Game Developers Conference,Mar. 2016.Cited on p. 40, 806, 814
Hector, Tobias, “Vulkan:High Efficiency on Mobile,”Imagination Blog, Nov. 5, 2015.Citedon p. 40, 794, 814
Pranckeviˇcius, Aras, “Porting Unity to New APIs,”SIGGRAPH An Overview of Next Gen-eration APIs course, Aug. 2015.Cited on p. 40, 806, 814
Cabello, Ricardo, et al.,Three.js source code, Release r89, Dec. 2017.Cited on p. 41, 50, 115,189, 201, 407, 485, 552, 628
Giesen,Fabian,“ATripthroughtheGraphicsPipeline2011,”The ryg blog,July9,2011.Cited on p. 32, 42, 46, 47, 48, 49, 52, 53, 54, 55, 141, 247, 684,701, 784, 1040
Microsoft, “Direct3D 11 Graphics,”Windows Dev Center.Cited on p. 42, 233, 525
Isidoro, John, Alex Vlachos, and Chris Brennan, “Rendering Ocean Water,” in Wolfgang En-gel, ed.,Direct3D ShaderX: Vertex & Pixel Shader Tips and Techniques, Wordware, pp. 347–356, May 2002.Cited on p. 43
Kryachko, Yuri, “Using Vertex Texture Displacement for Realistic Water Rendering,” in MattPharr, ed.,GPU Gems 2, Addison-Wesley, pp. 283–294, 2005.Cited on p. 43
Andersson, Johan, “Terrain Rendering in Frostbite Using Procedural Shader Splatting,”SIG-GRAPH Advanced Real-Time Rendering in 3D Graphics and Games course, Aug. 2007.Citedon p. 43, 175, 218, 877, 878
Mittring, Martin, “Finding Next Gen—CryEngine 2,”SIGGRAPH Advanced Real-Time Ren-dering in 3D Graphics and Games course, Aug. 2007.Cited on p. 43, 195, 239, 242, 255, 457,476, 559, 856, 860, 861
Rideout, Philip, and Dirk Van Gelder, “An Introduction to Tessellation Shaders,” in PatrickCozzi & Christophe Riccio, eds.,OpenGL Insights, CRC Press, pp. 87–104, 2012.Cited onp. 44, 46
Cantlay, Iain, and Andrei Tatarinov, “From Terrain to Godrays:Better Use of DX11,”GameDevelopers Conference, Mar. 2014.Cited on p. 44, 569
Yuksel, Cem, and Sara Tariq,SIGGRAPH Advanced Techniques in Real-Time Hair Renderingand Simulation course, July 2010.Cited on p. 45, 642, 646, 649
Fernandes, Ant onio Ramires, and Bruno Oliveira, “GPU Tessellation: We Still Have a LOD ofTerrain to Cover,” in Patrick Cozzi & Christophe Riccio, eds.,OpenGL Insights, CRC Press,pp. 145–161, 2012.Cited on p. 46, 879
Giesen,Fabian,“ATripthroughtheGraphicsPipeline2011,”The ryg blog,July9,2011.Cited on p. 32, 42, 46, 47, 48, 49, 52, 53, 54, 55, 141, 247, 684,701, 784, 1040
NVIDIA SDK 11,https://developer.nvidia.com/dx11-samples.Cited on p. 46, 55, 150
页:
[1]