找回密码
 立即注册
查看: 390|回复: 0

第三章 图形处理器(中)

[复制链接]
发表于 2021-8-10 14:06 | 显示全部楼层 |阅读模式
3.4 可编程着色和API的演化
可编程着色框架的构想可以追溯到1984年库克(Cook)的着色树。一个简单的着色器和它相关的着色树如果3.4所示。1980年后期RenderMan着色语言基于此想法开发出来。它在今天仍旧被用于电影产品渲染,跟其它演化的规范一起,例如开放着色语言(OSL)项目。
消费级的图形硬件由3dfx互娱在1996年10月1日第一次成功引入。如图3.5从这一年开始的时间轴。他们的Voodoo图形卡有能力以高品质和高性能渲染游戏雷神之锤(Quake)使得它能快被采用。这个硬件彻底地实现了一个固定功能管道。在GPU原生支持可编程着色器之前,这里有一些尝试,通过多渲染通道实现实时可编程着色操作。雷神之锤3(Quake|||):在1999年竞技场脚本语言是这个领域第一个获得广泛的商业成功。就像本章一开始提到的,NVIDIA GeForce256是第一个被称作GPU的硬件,但它不可编程。然而,它是可配置的。
在2001年早期,NVIDIA GeForce3是第一个支持顶点着色器的GPU,通过DirectX8.0和OpenGL扩展暴露。这些着色器通过类似汇编的需要编写,被驱动程序即时地转换为微代码。DirectX8.0同样也包含像素着色器,但是像素着色器缺乏真正的可编程性,受限制的“编程”支持被驱动程序转换为纹理混合状态,纹理混合状态轮流连接硬件“寄存器合并器”(register combiner)。这些程序不仅长度受限(12条指令或更少)而且缺少重要的功能。从属纹理读取(dependent texture reads)和浮点数被Peercy等人标识为真正可编程性的关键,从他们对RenderMan的研究。
此时的着色器不允许流程控制(分支),因此条件通过计算两个分支并且选择或在结果中差值来模拟。DirectX定义概念着色器模型(SM)来区分不同着色器能力的硬件。2002年DirectX9.0的发布包含着色器模型2.0,它的功能包含真正的可编程顶点和像素着色器。类似的功能OpenGL使用不同的扩展暴露出来。支持任意从属纹理读取和16位浮点数的存储被加进来,最终Peercy等人指出的一组需要。着色器资源,例如指令,纹理,寄存器的限制被增加,因此着色器能胜任更复杂的效果。流程控制的支持也被加进来。着色器长度和复杂度的增长使得汇编程序模型越来越复杂。幸运的是,DirectX9.0也包含HLSL。这个着色需要由微软和NVIDIA合作开发。大约在同时,OpenGL ARB(体系结构评审委员会)发行了GLSL,为OpenGL提供的非常类似的语言。这些语言的语法和设计哲学都深受C编程语言的影响并且包含来自RenderMan着色语言的元素。
着色器模型3.0在2004年被引入,增加了动态流程控制,使得着色器明显的更加强大。它把可选的功能变为必须的,进一步增加了资源限制,并且在顶点着色器中增加了对纹理读取支持的限制。当新一代的游戏控制台在2005年后期(微软的Xbox360)和2006年后期(索尼的PLAYSTATION 3系统)上市,它们装备了支持着色器模型3.0的GPU。任天堂Wii控制台是最后的著名的固定管道GPU之一,最初发行与2006年后期。现在纯粹的固定功能GPU已经走了很久了。着色器语言已经演化到可以有多种不同的工具用于创建和管理它们。一个这样的工具的截图,使用Cook的着色树概念,如图3.6所示。
可编程性的下一个一大步同样来自2006年末。着色器模型4.0,包含在DirectX 10.0中,介绍了几个主要的功能,例如几何着色和流输出。着色器模型4.0包含为所有着色器(顶点,像素,和几何)提供的统一编程模型,统一着色器的设计在之前描述过。资源限制进一步增加,并且加入对整数数据类型的支持(包括位运算)。OpenGL3.3中的GLSL 3.3提供了类似的着色器模型。
在2009年DirectX11和着色器模型5.0发行,新增曲面细分(tessellation)阶段着色器和计算着色器(compute shader),也叫做直接计算(DirectCompute)。这个发型也聚焦与更有效地支持CPU的多处理(multiprocessing),一个主题在18.5节讨论。OpenGL新增曲面细分在4.0版,计算着色器(compute shaders)在4.3版。DirectX和OpenGL的发展不同。都为一个特定的版本发行设置了一定级别的硬件支持。微软控制着DirectX API因此直接和独立的硬件供应商(IHVs)合作,比如AMD,NVIDIA,和Intel,以及游戏开发商和计算机辅助设计软件公司,以确定要公开(expose)的功能。OpenGL由硬件联盟和软件供应商开发,由非营利组织Khronos Group管理。因为涉及到的公司数量很多,API的功能通常在引入到DirectX一段时间后才出现在OpenGL的发行版本中。然而,OpenGL允许扩展,供应商特定的或者更一般的,允许在官方在发行版中支持前使用最新的GPU功能。
API的下一个重大变化是AMD在2013年推出的地幔(Mantle)API。跟视频游戏开发商DICE合作开发,Mantle的想法是去除大部分图形驱动程序的开销,并且把控制直接交给开发者。除了这种重构,进一步支持了有效CPU多处理。这类新的API致力于极大地减少CPU花在驱动程序上的时间,以及更高效的CPU多处理(multiprocessor)支持(18章)。这个在Mantle中率先提出的想法被微软采纳并在2015年作为DirectX 12发行。注意DirectX 12并不专注于公开新的GPU功能,DirectX 11.3公开了同样的硬件功能。两个API都可以用于发送图形到虚拟现实系统,比如Oculus Rift和HTC Vive。但是,DirectX 12是对API的彻底重新设计,可以更好地映射到现代GPU架构。低开销的驱动程序对CPU驱动程序开销会导致瓶颈的应用程序时有用的,或者为图形使用更多的CPU处理器可以提升性能。从较早的API移植可能很困难,天真的(naive)实现会导致性能降低。
苹果在2014年发行了它自己的低开销API叫做Metal。Metal起初在移动设备上可用,例如iPhone 5S和iPad Air,以及较新的Macintoshes(苹果电脑)一年后可以通过OS X El Capitan获得访问权限。除了效率,减少CPU使用率可以节省功耗,这是移动设备上的重要因素。这个API有它自己的着色语言,适用于图形和GPU计算程序。
AMD把它的Mantle捐赠给Khronos Group,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 ES 1.1。 2007年OpenGL ES 2.0规范发布,提供了可编程的着色(shading)。它基于OpenGL 2.0,但没有固定功能组件,因此不向后兼容OpenGL ES 1.1。 OpenGL ES 3.0于2012年发布,提供的功能有多个渲染目标,纹理压缩,变换反馈,实例化以及更广泛的纹理格式和模式,还有着色器语言的改进。 OpenGL ES 3.1添加了计算着色器,而3.2除了其他功能外,还添加了几何形状和镶嵌着色器。第23章讨论移动设备架构的更多细节。
OpenGL ES的一个分支是基于浏览器的API WebGL,通过JavaScript调用。 该API的第一版发布于2011年,可在大多数移动设备上使用,因为它的功能等效于OpenGL ES 2.0。 与OpenGL一样,扩展程序可以访问更高级的GPU功能。 WebGL 2假定OpenGL ES 3.0支持。
WebGL特别适合尝试功能或在课堂中使用:
  它是跨平台的,可以用于所有的个人电脑和几乎所有的移动设备。
  驱动程序批准由浏览器处理。 即使一个浏览器不支持特定的GPU或扩展程序,通常另一个浏览器也可以。
  代码被解释而不是编译,并且只需要一个文本编辑器即可进行开发。
  大多数浏览器都内置了调试器,并且在任何网站上运行的代码都可以被检查。
  可以通过将程序上传到网站来部署,或者Github例如。
更高级的场景图和效果库如three. js为许多更复杂的效果提供了容易访问的代码,这些效果如阴影算法,后处理,基于物理的着色,和延迟渲染。
3.5 顶点着色器
顶点着色器是图3.2所示功能管道中的第一阶段。虽然这是直接在程序员控制下的第一阶段,但值得注意的是在此阶段之前会进行一些数据操作。在DirectX所谓的输入装配器(The input assembler),可以将几个数据流编织在一起以形成沿管道发送的一组顶点和图元。例如,一个物体可以由一组位置和一组颜色表示。输入装配器将通过创建有位置和颜色的顶来创建此对象的三角形(或直线或点)。第二个对象可以使用相同的位置数组(以及不同的模型转换矩阵)及其不同的颜色数组来表示。数据表示将在16.4.5节中详细讨论。这里也支持在输入装配器执行实例化。这允许一个对象被绘制几次,每个实例具有一些不同的数据,所有只需一次绘制调用。第18.4.2节介绍了实例化的使用。
三角形网格由一组顶点表示,每个顶点都与一个特定的在模型表面上的位置。除了位置,还有其他可选属性与每个顶点相关联,例如颜色或纹理坐标。表面法线也定义在网格顶点上,这似乎是一个奇怪的选择。数学上每个三角形都有一个定义明确的表面法线,直接使用三角形的法线进行着色似乎更有意义。然而,渲染时,三角形网格通常用于代表潜在的曲面,顶点法线用于表示此表面(曲面)的方向,而不是三角形网格本身的方向。 16.3.4节将讨论计算顶点法线的方法。图3.7显示两个代表曲面的三角形网格的侧视图,一个平滑,一个有明显的折痕。
顶点着色器是处理三角形网格的第一步。描述顶点形成的三角形的数据不可用于顶点着色器。顾名思义,它专门处理传入的顶点。顶点着色器提供了一种修改,创建或忽略与每个三角形的顶点相关的值的方法,例如颜色,法线,纹理坐标和位置。通常,顶点着色器程序将顶点从模型空间转换为齐次的修剪空间(第4.7节)。顶点着色器至少必须始终输出此位置。
顶点着色器与前面描述的统一着色器几乎相同。每一个传入的顶点由顶点着色器程序处理,然后输出在三角形或直线上插值的许多值。顶点着色器既不能创建也不可以销毁顶点,并且一个顶点生成的结果不能传递给另一个顶点。由于每个顶点都是独立处理的,因此GPU上任何数量的着色器处理器可以并行地应用于传入的顶点流。
输入装配通常表示为发生在顶点着色器被执行前的一个过程。这是物理模型通常区别与逻辑模型的一个例子。从物理上讲,获取数据以创建顶点的过程可能会发生在顶点着色器,驱动程序将在每个着色器前面适当地添加指令,这些指令程序员不可见。
随后的章节介绍了几种顶点着色器效果,例如用于动画关节的顶点混合和轮廓渲染。顶点着色器的其他用途包括:
  通过仅创建一次网格生成物体,并通过顶点着色器使其变形。
  使用蒙皮和变形技术对角色的身体和面部进行动画处理。
  程序变形,例如旗帜,布料或水的移动。
  通过发送退化(无区域)的网格沿管道生成粒子,并根据需要为这些退化的网格提供一个区域。
  镜头变形,热雾,水波纹,页面卷曲,和其它效果,通过把整个帧缓冲的内容作为一张纹理用在屏幕对齐的网格上,并对该网格运用程序变形。
  通过使用顶点纹理获取来应用地形高度场。
使用顶点着色器完成的一些变形如图3.8所示。
顶点着色器的输出可以通过几种不同的方式消耗。常规路径是为每个实例的图元(如三角形)进行生成和栅格化,每个生成的像素片段发往像素着色器进行后续的处理。对于某些GPU数据也可发往曲面细分阶段或几何处理阶段或被存储在内存中。这些可选的阶段在接下来的各节中讨论。
3.6 曲面细分阶段(The Tessellation Stage )
曲面细分阶段允许我们渲染曲面。 GPU的任务是获取每个表面描述,然后将其转变成代表性的一组三角形。这个阶段是可选的GPU功能,最早在DirectX 11中可用(并且在DirectX 11中是必需的)。OpenGL 4.0和OpenGL ES 3.2也支持。
使用曲面细分阶段有几个优势。曲面描述通常比提供相应的三角形本身更加紧凑。除了节省内存外,此功能还可以避免CPU和GPU之间的总线称为瓶颈,如对于一个动画角色或每帧外形都发生变化的物体。通过为给定的视图生成适当数量的三角形表面可以被高效的渲染。例如,如果一个球离相机很远,只需要几个三角形。近距离来看,用数千个三角形来代表或许看起来效果最好。控制细节等级的能力也可以允许应用程序控制自己的性能,例如在较弱的GPU上使用较低质量的网格,以便保持帧率。通常以平面表示的模型可以转换为三角形适中的网格,然后根据需要变形,或者它们可以被细分(tessellated)为了不那么频繁地执行昂贵的阴影计算。
细分阶段始终由三个元素组成。使用DirectX的术语,它们是hull shader,tessellator和domain shader。在OpenGL中,hull shader是tessellation control shader ,而domain shader是tessellation evaluation shader,虽然冗长,但更具描述性。在OpenGL中fixed-function tessellator被叫做primitive generator,就像将会看到的,它就是做这个的。
在第17章中详细讨论了如何指定和细分(tessellate)曲面和曲线。在此,我们简要概述了每个细分阶段(tessellation stage)的目的。 开始,hull shader的输入是一个特殊的patch图元。 patch图元由几个控制点组成定义了一个细分面(subdivision surface),Bezier patch,或其他类型的弯曲元素。hull shader具有两个功能。 首先,它告诉tessellator应该生成多少个三角形,以什么配置。 其次,它对每个控制点执行处理。另外,可选的,hull shader 可以修改传入的patch描述,根据需要添加或删除控制点。 hull shader输出它的一组控制点,和细分(tessellation)控制数据,到domain shader。参见图3.9。
图3.9 曲面细分阶段。hull shader接收由控制点定义的一个patch。hull shader发送细分因子(tessellation factors)(TFs)和类型到固定功能(fixed-function)tessellator。hull shader根据需要变换控制点集并且发送给domain shader,和TFs还有相关的patch常量一起。tessellator创建点集和这些点集的重心坐标。然后由domain shader处理这些,生成三角形网格(显示控制点供参考)。
tessellator是管道中的固定功能阶段,仅与tessellation 着色器一起使用。它的任务有添加一些新顶点以供domain shader处理。hull shader将向tessellator发送期望的tessellation surface类型:三角形,四边形或isoline。isoline是线带的集合,有时用于头发渲染。hull shader发送的其他重要值是镶嵌因子(tessellation factors)(在OpenGL中是细分等级)。这分为两个类型:内边缘和外边缘。这两个内部因子决定了在三角形或四边形内部发生多少细分(tessellation)。外部因子决定每个外部边缘被分割多少次(第17.6节)。增加细分因子的示例如图3.10所示。通过允许单独的控制,我们可以使相邻的曲面曲面的边缘在细分(tessellation)时匹配,而无论内部如何细分。匹配的边缘可避免在补丁相遇之处出现裂缝或其他阴影瑕疵。顶点分配了重心坐标(第22.8节),这些值指定了在期望的表面上每个顶点的相对位置。
hull shader总是输出一个patch,一组控制点的位置。然而,它通过发送给tessellator一个小于或等于0(或者不是一个数,NaN)的外部细分等级(outer tessellation level)来标识一个面片将被丢弃。否则,tessellator会生成一个网格并将其发送到domain shader。来自hull shader曲面的控制点被domain shader的每次调用所使用,为了计算每个顶点的输出值。domain shader有一个数据流模式就像一个顶点着色器,来自tessellator的每个输入的顶点被处理并且生成一个相应的输出顶点。形成的三角形然后沿着管道继续传递。
虽然这个系统听起来很复杂,但为了提高效率而采用这种结构,每个着色器可能非常简单。 传递到hull shader中的patch通常会经历很小的修改或者没有修改。此着色器还可以使用patch的估计距离或者屏幕尺寸动态地计算细分因子(tessellation factors),如地形渲染。交替地,hull shader或者简单地为所有patches传递一组固定值,这些值由应用程序计算并提供。tessellator执行一个复杂的但固定功能的处理生成顶点,给它们位置,并且指定它们形成的三角形或线。这个数据丰富的步骤为了计算高效在着色器外执行。domain shader获取为每个顶点生成的质心坐标并且在patch的求值方程式中使用,来生成位置,法线,纹理坐标,和其它期望的顶点信息。有关示例,请参见图3.11。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 04:33 , Processed in 0.065222 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表