“逐片元操作”这个名字非常直观说明了这个阶段的操作对象——每一个片元。但是这个阶段和片元着色器一样有另外一个名字——输出合并阶段(Output-Merger),这个名字说明了这个阶段最重要的操作方式——合并。合并是指的将片元的颜色与颜色缓冲区中对应位置的颜色信息进行合并,使片元成为真正意义上的像素。不过在此之前,我们还要对输入的片元进行测试。
尽管在不同的管线实现中,片元测试的内容和细节都很复杂且有很大的差异,但最基本的测试过程包括模板测试和深度测试。模板测试从模板缓冲区中读取片元对应位置的模板值,与使用读取掩码读取到的参考值进行比较。这个比较函数可以由编程人员指定,只要模板值没有通过与参考值的比较,那么该片元就会被丢弃。
接下来的深度测试与模板测试一样,是高度可配置的。深度测试开启后,会读取当前片元的深度值,并与深度缓存区中该片元对应位置的参考深度值进行比较。这里的比较函数也可以由编程人员指定,但在一般情况下,我们希望保留深度值小的片元,丢弃深度值大的片元,以达到现实世界中物体遮挡的视觉直观。如果开启了深度写入,通过了深度测试的片元的深度值就会替代原有的深度参考值,写入到深度缓冲区中。
通过了所有测试后的片元即将进入管线的结束阶段——合并。由于片元的颜色最终要写入到颜色缓冲区,而颜色缓存区中往往存储着上一次渲染结果的颜色值,我们应该直接将片元的颜色替换掉缓冲区的颜色,还是做一些其他操作?在这里大部分的管线实现提供了混合(Blend)选项。关闭混合选项,片元的颜色就会直接替换掉缓冲区的颜色。但为了实现一些特殊的渲染效果,如透明物体,我们就需要开启混合选项,指定一个混合函数,让片元的颜色值与缓冲区的颜色值进行混合,形成新的颜色值并写入到缓冲区中。
片元的颜色写入到颜色缓冲区后,它就只需要等待 GPU 将当前的颜色缓冲区与前台颜色缓冲区进行交换,使当前颜色缓冲区变成前台颜色缓冲区,便可以成为像素显示在屏幕上了。
6 图形 API
在构建完渲染管线的全貌后,我们貌似就可以直接可以动手开始实现属于我们自己的渲染管线了。然而事情并没有这么简单。同 CPU 一样,与 GPU 打交道同样需要与显存、寄存器和总线等硬件结构打交道。这对于想要开发一款 3D 应用程序的一般的编程人员而言,不仅这样的学习成本高昂,开发和维护的成本也难以让人承受。类似于使用 Linux 或 Windows 与 CPU 等硬件打交道,一些组织和企业制定了图形 API (Graphics Application Program Interface)标准。这些图形 API 抽象了对 GPU 硬件的复杂操作,同时提供了渲染管线的抽象实现,以便应用程序开发者能够以较低的成本与 GPU 进行交互。
目前主流的三大图形 API 分别为 OpenGL、DirectX 和 Vulkan。
6.1 OpenGL 与 OpenGL ES
OpenGL 诞生于 20 世纪 90 年代初,可谓是图形 API 的先行者。由图形行业龙头 Khronos 组织制定(其最初的制定者是 SGI 公司)的它由于拥有强大的可移植性与易于使用的硬件访问方式,受到了图形行业的广泛支持和应用。同时其子集 OpenGL ES 在移动嵌入式平台更是占据行业标准地位。
目前 OpenGL 的最新版本为 OpenGL 4.6,OpenGL ES 的最新版本为 OpenGL ES 3.2。
6.2 DirectX
DirectX 是由 Microsoft 制定的图形 API 标准——这样说其实并不准确。DirectX 实际上是一种多媒体 API 标准,它包含了 DirectSound(捕获和播放数字声音)、DirectMusic(数字音频处理)、DirectInput(人机交互设备处理)、DirectPlay(游戏网络通信)、DirectShow(捕获和播放多媒体) 和 Direct3D。Direct3D 才是真正意义上的图形 API 标准,但由于 Direct3D 部分在整个 DirectX 中的内容与地位逐渐提高,在图形领域里两者就基本上被画上了等号,因此使用 DirectX 指代 Direct3D 在通常情况下也是可以的。DirectX 是一个平台相关的图形 API 标准,它只能在 Mircosoft 公司推出或支持的平台上工作,例如 Mircosoft Windows 和 Mircosoft Xbox。不过得益于 Mircosoft 在个人计算机市场的高占有率,DirectX 的低移植性不仅没有限制 DirectX 的开发与应用,反而得到了越来越多的开发人员的应用与支持,充分发挥了其高效性的特点。
目前DirectX 的最新版本为 DirectX 12。
6.3 Vulkan
Vulkan 的前身是由 AMD 公司主导开发,并在 2013 年推出的 Mantle。Mantle 相对于当时的 OpenGL 和 DirectX 11,给予了开发人员对于 GPU 硬件更多的操作空间,使开发人员能够显著提升硬件利用率与 3D 应用性能。这一特性使得 Mantle 在诞生后的两年里为其他图形 API 提供了提升性能的参考方法,如 Microsoft 的 DirectX 12 和 Apple 的 Metal。但由于 AMD 缺乏对图形行业足够的领导力与影响力,AMD 在 2015 年宣布停止维护 Mantle,交给 Khronos 组织。
由于 GPU 的可编程性越来越强,越来越多的平台开始支持图形加速、多媒体编码和深度学习等技术。这使得图形 API 需要比以往更加的灵活和高效。而发展了数十年的 OpenGL 不仅越来越难以满足在 GPU 上的这种高可编程性的需求,而且也无法发挥现代 CPU 多核多线程的性能优势。因此 Khronos 组织在 Mantle 的基础上推出了 Vulkan,并力图让其成为新一代的图形 API 行业标准。
Vulkan 的表现的确非常亮眼。它把 GPU 的大部分控制权交给开发者,以往由实现 OpenGL 的 GPU 驱动做的工作(如API验证、显存管理和线程管理)现在都交给了应用开发者。虽然这样的代价是开发和维护难度的提升,但其带来的性能效益也十分巨大。同时 Vulkan 也充分利用了现代 CPU 多核多线程的优势,进一步提升应用程序的性能。最后 Vulkan 同它的前辈 OpenGL 一样,是一个平台无关的高移植性图形 API。
这么看来 Vulkan 已经可以完全替代 OpenGL,真正成为新一代的图形 API 行业标准了。但实际上 Vulkan 实现的开发与维护相当困难,目前的应用程序开发人员也并未充分利用 Vulkan 的优势。而 OpenGL 相对而言不仅开发和维护的成本较低,学习起来相对简单,而且在目前的主流平台上 OpenGL 仍然有着良好的表现,因此 OpenGL 至今仍然受到图形行业的广泛支持。Vulkan 要成为新一代图形 API 行业标准,还有很长的路要走。
目前 Vulkan 的最新版本为 Vulkan 1.3。
7 图形驱动
图形 API 抽象了 GPU 对渲染管线的实现,但真正需要与 GPU 在硬件层面打交道的是图形驱动。图形驱动与其他驱动程序一样,是沟通应用程序 API、操作系统和硬件的“中间人”。图形驱动尽管是与硬件平台强相关的,但通常可以分为两层:用户模式驱动(User-Mode Driver)和内核模式驱动(Kernel-Mode Driver),简称为 UMD 和 KMD。
UMD 的主要工作是检验图形 API 的调用,编译着色器和 API 命令,并将其送入命令缓冲区(Command Buffer)中。而 KMD 就负责管理命令缓冲区,并将命令缓冲区中的命令送入命令处理器中执行。此外 KMD 还负责 GPU 的初始化和显存管理等工作。
8 写在最后的一些碎碎念