Advertisement

《Real-Time Rendering 4th Edition》读书笔记--简单粗糙翻译 第三章 图形处理单元 The Graphics Processing Unit

阅读量:

写在前面的话:由于英语基础较为薄弱,在阅读过程中显得较为缓慢。因此不如就按照自己的理解进行粗略的翻译尝试,并将其当作个人学习心得记录下来。其中存在的不足之处也颇多,在未来深入理解后会再回头进行进一步修改和完善。相信投入的时间和精力都是值得付出的。

————————————————————————————————

“计算机大部分时候就是你所看到的”--黄仁勋

最初的应用集中在对三角形进行像素插值计算,在这一过程中将每个像素的数据绘制到显示屏幕上。通过纹理访问的方式将图像数据映射至表面并完成相关运算后,在后续阶段又增加了对深度值(z-depths)的插值计算以及深度比较步骤。由于频繁调用的需求,在渲染流程中这些处理被移交给专用硬件系统来优化以提升整体性能水平。现代渲染管线通常通过逐步迭代的方式来实现复杂的视觉效果。值得注意的是,在图形处理领域中专用加速单元仅能提供显著的速度提升优势,在当前计算架构中这是其最大的价值所在

在过去的二十年间,图形硬件的发展速度非常快。NVIDIA于1999年推出了第一款具备定点处理功能的商业用图形芯片GeV380,并将其命名为GPU(graphics processing unit)。为了区别之前的光栅化架构GeForce256 GPU这一术语便被广泛采用至此之后便统一称为GPU了随后几年中 GPU逐渐发展出允许开发者自行编排的架构从基于固定渲染管线上升到允许开发者自行编排的架构多种多样的可编程着色器构成了控制 GPU 的核心方案为了提高效率 部分管线仍然保持着不可编程的状态 但整体趋势是向着高度可编程性和灵活性迈进

因为GPU被设计为执行大量并行任务的原因在于其强大的计算能力,在这种架构下它不仅能够以极快的速度运行各种图形处理指令而且还具备优化资源以满足高性能需求的特点

第3.3节阐述了着色器的工作原理。目前而言,则只需了解着色器的核心功能是一个小型处理器即可完成相关工作。它主要负责执行一些相互独立的任务,并且具体包括将顶点从模型坐标转换为世界坐标、计算每个像素的颜色值等功能。因为每一帧都需要向屏幕发送数以万计的三角形图形,并且这些图形在每一秒都会触发数亿次 shader invocations 来完成绘制过程。

处理设备普遍面临的一个问题是延迟问题。获取数据的过程往往需要一定的时间消耗。当数据信息传输至处理器所需的时间越长时,延迟也会随之增大。有关这一问题的详细讨论可参考第23.3节的内容。相较于从寄存器中读取数据而言,从存储器中获取数据所耗费的时间更为漫长。第18.4.1节对内存操作进行了深入探讨与说明。在等待检索所需的数据时,系统的性能表现会受到一定的影响。

3.1 数据并行结构

旨在提升效率的各种处理器系统中每种都会采取特定策略以适应不同的任务需求

GPU运用独特的计算策略。 majority GPU chips are built with thousands of shaders. GPU functions as a data pipeline, processing similar streams sequentially. This inherent similarity enables GPU to efficiently handle massive parallel tasks. Moreover, these operations are designed to be as independent as possible, minimizing the need for data sharing or shared memory. However, this independence is occasionally compromised to implement new functionalities at the expense of performance efficiency. In such cases, the system may need to wait for another processing unit to complete its task before proceeding.

GPU通过吞吐量(throughput)来衡量其最高数据吞吐速率。然而这种快速处理带来了性能上的牺牲。受限于芯片区域有限的数量只能缓存内存或控制逻辑每个着色器往往都会比CPU处理器带来更高的延迟。

GPU通过吞吐量(throughput)来衡量其最高数据吞吐速率。然而这种快速处理带来了性能上的牺牲。受限于芯片区域有限的数量只能缓存内存或控制逻辑每个着色器往往都会比CPU处理器带来更高的延迟。

当一个Mesh经过光栅化后会产生2000个片元供后续处理。相应的像素着色程序必须执行2000次以完成这些计算任务。想象一下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况下这种情况将会发生:如果仅有一个着色器来完成这些工作那么其性能表现将非常糟糕因为每个片元都需要在寄存器中执行一系列数学运算而其中寄存器属于局部存储空间并运行速度非常快因此在访问这些数据时非常迅速假如这一过程还要求该着色器从外部纹理资源获取信息以确定该网格上对应像素的颜色那么这就会带来额外的问题因为纹理资源并不是直接可及的而是作为一个独立资源存在这使得对 texture 的读取过程变得相对复杂这对于 GPU 来说是一项耗时的操作因为 GPU 忙于处理当前任务期间无法立即响应其他请求这时该着色器就会出现阻塞状态等待必要的 texture 数据传输完毕才能继续正常工作

为了提升性能落后的GPU设备,在现代显卡架构中通常会对每个流处理器(即图形处理单元中的计算单元)进行优化设计

在这个架构中, GPU经历了从等待阻塞到处理接下来的片元的过程. GPU进一步设计将逻辑指令从数据中分离开,并称为单指令多数据结构(SIMD).这种结构会在固定数量的着色器以lock-step形式下执行相同的指令. 与使用单独的逻辑或调度单元器处理每个程序相比,SIMD的优势在于能够用更少的硬件来处理数据以及交互数据. 将两千个片元处理案例应用到当前GPU上,每个像素着色时所对应的单一操作被定义为一个线程(thread). 这个线程与其他CPU中的线程不同,在着色器执行过程中使用的寄存器都需要一定的内存空间来存储输入值. 每一个线程都被映射到一个SIMD lane中,每一个SIMD lane负责完成8至64个GPU着色器所对应的SIMD运算过程. 每个warp或wavefront包含多个这样的SIMD lane,共同完成复杂的计算任务.

假设我们有2000个片元需要处理,在NVIDIA架构中存在32个独立运行的线程。根据计算可知每个线程将拥有62.5个warps(即工作队列),其中最后一个仅有一半参与实际运算。这些工作队列(warp)的处理流程与单个GPU的处理流程相似,在同一个lock-step阶段同时执行指令时会出现多线程并行的情况:当一个处理器执行内存获取操作时,其他处理器也会立即执行相同指令以同步操作。如果某个工作队列在进行内存获取时出现阻塞,则后续的工作队列需要等待该队列的数据完成才能继续运行以避免数据丢失。为了实现快速切换至新的工作队列(即跳转),我们可以将后续的操作任务重新分配给另一个空闲的工作队列——这种切换速度与单线程处理相当,并且不会产生额外开销:每个工作队列都有自己的独立寄存器用于记录当前正在进行的操作指令信息,并且跳转到新的工作队列只需要将一组计算单元映射至另一组计算单元即可完成——这种跳转操作具有极小开销并能够保证整个系统的高效运行直至所有任务完成(如图3.1所示)。

图3.1展示了简化版的着色器处理示例。一个三角形的所有片元被划分为一组warps(warps)。每个warp包含4个线程实例,在这种情况下总共有32个线程。该着色程序包含5条指令。GPU着色器从第一个"warp"开始执行这些指令,并在遇到"txr"指令时暂时阻塞以获取所需数据。随后第二个"warp"切换进来,在此期间该程序的前三个指令将被提交给第二个"warp"并继续执行直至再次出现阻塞现象。接着第三个"warp"会切换进来,在其遇到阻塞问题后会立即返回到第一个"warp"继续执行任务。如果此时"txr"指令的数据仍未被获取,则会进入真正的阻塞状态直至所有相关数据都被成功加载完毕。随后每个"w warp"依次完成各自的任务

在上述简单的示例中,在warp切换上存在一定的开销(尽管其开销虽小)。然而,在现有技术的基础上进一步提升执行效率并非易事。尽管如此,在GPU上实现最低延迟的主要手段依然是warp切换(warp-swapping)。此外,在整个处理流程中还存在几个关键因素会影响最终的整体效率——例如参与计算的具体线程数目以及能够被调度的最大数量的warps。

着色程序本身的架构在其运行效率中同样扮演着关键角色。其显著的影响因素之一即每个线程所需的寄存器数量。例如,在上述案例中, 我们假设显卡一次能够处理数千个工作项. 如果单个线程所需使用的寄存器数量越多, 显卡能够维持的有效工作流数量及warp数目都会相应减少. 一旦work-group数目减少后, 相应地, work-group之间的切换机会也会随之减少. 在这种情况下, 每个work-group都必须保持高度活跃状态. 同时, 活动的工作流数目与最大数目之比值即为occupancy率(显卡利用率). 这一数值越高就意味着系统资源利用越充分. 当该数值较低时, 显著会影响系统的整体性能表现. 最后值得注意的是, 内存访问频率的变化也会对系统的总延迟产生重要影响

另外一个重要因素可能影响整体效率的是动态分支(dynamic branching)。这种现象主要由程序中的条件判断语句(if statements)以及循环结构所引起。例如,在着色程序中遇到条件判断语句时,在所有线程都位于同一路径的情况下运行则不会有其他路径被考虑。然而,在某些情况下甚至只有一个线程触发了条件判断后就必须同时执行两个不同的路径,并且通过特定的机制来丢弃不必要的结果这一问题被称为**线程散度(thread divergence)**现象。具体来说,在一个warps内部仅有少数几条指令涉及循环迭代或者条件判断选择不同的路径而其余大部分warps则遵循相同的指令流运行的情况下就会出现大量不必要的warps处于空闲状态的情况

在后续章节中, 我们将会深入探讨 GPU 如何支持构建渲染管线, 可编程着色器将如何运作. 每个 GPU 阶段的功能与扩展.

3.2 GPU渲染管线概述

图形处理器主要包含几何处理单元、光栅化单元以及像素处理单元三个主要部分。而这些又被划分为不同程度的可配置或可编程子单元。如图3.2所示,在各个处理单元中采用了不同的颜色标记来区分其是否具有配置或编程能力。需要注意的是,在本章中所采用的物理划分方式与上一章有所不同。

图形处理器(GPU)渲染流水线由多个组成环节构成。根据颜色划分的各个阶段,在设计上是否具备编程灵活性与配置多样性?其中绿色部分具有完整的参数化能力;而虚线框表示为可选模块;黄区则仅支持配置而非参数化设置;例如,在混合模式中可以实现多种效果结合。

该GPU的核心架构设计采用API接口的形式向开发者展示其计算能力。如前所述,在第18章及第23章中详细讨论过,在芯片设计领域已有诸多 vendor 提供相应的 hardware-software co-design solutions。其中一些固定功能模块可在相邻可编程流水线段间通过多线程并行机制完成同样地,在图形处理流水线中同一时间段内同一图像数据可能被多个不同的 processing units 处理以确保高效并行运算。值得注意的是尽管这些技术细节有助于评估影响系统性能的关键因素但它们不应被视为构建GPU核心架构的具体实现方案

顶点着色属于几何处理阶段的一部分,并且具有高度的可编程性特点。在这一过程中用于处理每个图元的所有顶点信息,并支持对每个图元进行操作功能如销毁或创建新的图形元素。曲面细分与几何着色两种功能均为可选阶段,在当前主流的GPU架构中通常不具备这些功能(尤其是在移动设备上)。

裁剪、三角形设置以及三角形遍历均属于硬件固定的子系统。窗口与视口配置决定了屏幕映射关系。pixel processing stage is fully programmable. 然而, 融合过程不具备程序性能力, 但其配置非常灵活, 可通过一系列参数进行详细调节. 该过程涉及修改颜色值、深度缓存、混合运算、模板测试以及其他辅助操作等. pixel coloring与融合过程共同构成pixel processing stage

随着时间的推移, GPU PIPELINE 在执行 硬编码操作时, 已逐渐转变为 逐渐变得更加灵活且可控 的状态. 其中 可编程阶段 打扮着关键角色. 在下一节中将深入探讨 各种 可编程 阶段 的 特征 .

随着时间的推移, GPU PIPELINE 在执行 硬编码操作时, 已逐渐转变为 逐渐变得更加灵活且可控 的状态. 其中 可编程阶段 打扮着关键角色. 在下一节中将深入探讨 各种 可编程 阶段 的 特征 .

3.3 可编程着色阶段

现代着色程序采用了统一的设计方案

阐述整个着色器编程模型超出了本书讨论的范围。已有大量文档、书籍和网站进行了这项操作。着色器编程采用了一种基于类C++的标准类型系统(STS)的语言来实现效果显著的技术——高级着色语言(HLSL)。其中DIrectX使用的就是这一技术,并将其称为高级着色语言(HLSL, High-Level Shading Language);而OpenGL则采用了另一种类似的技术——GLSL(OpenGL Shading Language)。值得注意的是,在这种情况下,HLSL代码被转换为中间机器码字节码,并被命名为中间表示(IL...intermediate language, ILs), 以确保了硬件独立性。通过这种中间表示,程序可以在离线阶段完成编译并存储起来,随后又被驱动转换成特定GPU指令体系结构ISA所需的二进制指令序列.控制台编程通常会直接使用ISA指令集而不涉及中间表示步骤.

单精度的浮点值标量和向量的核心数据类型是32位,在图形着色器编程中常用到向量时较多却过去未见硬件支持。如今GPU已集成32位整数与64位浮点运算功能。 浮点向量分量通常包括xyzw坐标、法线方向、矩阵行、颜色通道 rgba以及纹理坐标 uvwq参数等;整数值则主要用于计数值、指数或二进制掩码等计算需求;集合数据类型如结构体、数组与矩阵等也同样得到了广泛的支持

在计算机图形学中,绘图指令(Draw Call)被用来触发图形API以绘制一组几何体。在这一过程中,默认情况下,默认情况下默认情况下默认情况下默认情况下默认情况下默认情况下默认情况下默认情况下默认情况下,默认情况

底层虚拟机为不同类型的输入输出分配了特定的寄存器。均匀类型的可用常量寄存器数量远超varying输入与varying输出所需的数量。这是因为对于每个顶点或像素而言,在varying类型输入和输出中需单独存储数据的缘故。而均匀输入数据一次性存储,在单个draw call过程中可被所有顶点及像素复用。虚拟机还拥有多种临时寄存器用于暂存空间信息。所有类型寄存器均可利用临时寄存器中的整数值进行索引操作(如图3.3所示)。

图3.3展示了Shader Model 4.0中的统一虚拟机架构及其寄存器配置情况。每个图形元素旁边都标注了其最大支持的数量。这些数量是以斜杠分隔开的三个数字表示的:从左到右依次为顶点寄存器、几何寄存器和像素寄存器的最大容量。

在现代GPU上运行的各种图形学算法已经达到了很高的效率水平。这些算法主要包括使用算术符号进行的简单运算以及一些基础函数如反正切、平方根以及对数运算。其中加减乘除等基本运算可分别用*、+等符号表示,并包含一些更为复杂的操作包括向量化的法线处理、反射向量计算、点积运算以及矩阵变换等。

控制流(flow control)是利用分支指令来改变代码的执行流程 。在HLSL中的控制流指令有“if”“case”语句及各种循环类型。着色器支持两种控制流。静态控制流(static flow control) 是基于uniform 输入,这意味着控制流代码在本次draw call 过程中是常量。静态控制流的主要好处就是在不同情况下(例如不同数量的光源情况下)可以使用相同的着色器。不存在线程散度,因为所有的调用都是用的相同的代码路径。动态控制流(Dynamic flow control) 是基于varying 输入的,意味着每个片元执行的代码可以不同。这比静态控制流更有用,但是性能消耗更多,特别在代码流在着色器调用中不定时改变流向。

3.4 可编程着色的环境和API

程序化的色彩方案源自1984年Cook理论模型。
图3.4呈现了一个简单的色彩生成程序及其相应的颜色树结构。
在20世纪90年代初,《基于Cook理论的色彩生成技术逐步演进》。
这一技术至今仍广泛应用于影视后期制作领域。

图3.4 一个简单的铜材质着色器和他对应的着色语言程序。

第两款商用级别的图形硬件由3dfx于1996年10月1日发布。图3.5呈现了图形硬件发展的时间轴。3dfx开发的Voodoo系列图形处理器生成的游戏Quake以其卓越的画面质量和高性能而广为人知,并迅速得到了广泛应用。这一技术支持了固定渲染流水线技术。在可编程着色成为主流之前,在早期提到的NVIDIA GeForce 256被认为是第一款被公认为 GPU 加工器的产品。虽然不具备可编程性但具备高度可配置性。

图3.5 图形硬件版本和API的发展时间轴

2001年份期间,NVIDIA推出Ge-force系列中的首款支持顶点着色的图形处理器

此时运行的着色器引擎并不支持控制流指令,在进行图形处理时必须依靠软件模拟获取控制条件。DirectX官方定义了 Shader Model 2.0 以区分不同硬件平台的着色能力。2002年版的 directX 9.0 标准首次引入了 Shader Model 2.0,并实现了真正的顶点和像素可编程着色功能。与此同时,OpenGL也推出了完全相同的功能模块。通过增强着色资源库的支持程度,《使》着色器能够实现更为复杂的图形效果。随着对复杂控制流的支持需求不断增加,《导致》着色器程序长度和复杂度显著提升,《使得》可编程模型逐渐变得愈发繁琐。幸运的是《是》,自 directX 9.0 推出后开始采用 HLSL 编程语言——这一由微软与英伟达共同主导开发的语言体系,《同时》OpenGL也发布了 GLSL 图形化编程语言《风格》类似C语言的特点明显可见。

在2

图形3.6 专为色彩设计而设的可视化色彩系统。左侧的功能盒封装了多种功能,在选中后右侧可见各项可调节参数。每个功能块与其相连的部分只需通过简单的连线即可完成连接。

在2006年底时,Shader Model 4.0与DirectX 10版本正式推出,并增添了新的功能如几何着色与流输出等特性。Shader Model 4.0中所有的着色器均支持uniform输入参数。为了进一步提升性能与灵活性,在资源管理方面做了重要改进,并增强了对整数类型数据结构的支持度。此外,在图形处理器接口(OpenGL)领域,开放图形状态机器(GLSL)已升级至3.3版本以适应最新技术需求。

在2009年份时,《DirectX 11》与《Shader Model 5.0》正式发布,并新增了《曲面细分着色器(tessellation shader)》以及《计算着色器(compute shader, 同时也被称为通用计算 DirectCompute)》功能。这一版本中的CPU多处理器配置更为高效。与此同时,《OpenGL》也在4.0版本中首次引入了《曲面细分着色器》,并在4.3版本中加入了《计算着色器》的支持。由于《DirectX》与《OpenGL》的发展路径存在差异,《DirectX》各个版本所需要的功能都必须依赖特定硬件支持才能实现。微软作为《DirectX API》提供者,《Windows DirectX》能够直接与AMD、NVIDIA、Intel等硬件厂商展开合作,并与游戏开发者及计算机辅助设计公司共同推动各项特色功能的发展工作。另一方面,《OpenGL》是由一系列硬件与软件供应商共同组成的开放平台所开发而成,并由非盈利组织《Khronos Group》进行管理维护。由于涉及多方协作,《DirectX》某些核心API功能会在相应后续版本中逐步向《OpenGL》兼容版本迁移;而相比之下,《OpenGL》由于具备更强的扩展性以及支持更多元化的设备平台,在功能实现上往往能够提前满足开发者的需求需求。

于2013年推出的是AMD开发的变革性API Mantle。DICE公司的游戏开发中采用Mantle API去除了许多图形驱动程序所需的计算资源,并将执行这些计算的任务直接分配给开发者。优化了多核处理器的性能。通过减少CPU驱动过程中的计算开销使这一新方案更具效率。随后于2015年微软发布了DirectX 12标准这一设计思路与Mantle API相似但 DirectX 12并未专注于提升GPU性能而是专注于其他方面两种技术均可将图像发送至虚拟现实系统例如Oculus Rift 和 HTC Vive等设备但 DirectX 对API进行了重新设计使其更适合现代GPU架构这一改进降低了CPU运行图形处理任务时所消耗的能量从而减少了CPU成为系统瓶颈的可能性同时也提升了多处理器环境下的图形处理效率然而旧版本API可能面临移植上的挑战

苹果于2014年推出了其低成本API——Metal 。这一技术不仅提高了性能、减少了CPU的消耗量、还能有效延长电池续航时间(对移动设备而言)意义重大。此外,Metal还支持独特的着色系统

AMD随后将AMD的Mantle工作转移给Khronos组织, Khronos于随后于2016年推出新版后将名称更改为Vulkan . 同如OpenGL一样,Vulkan同样具备跨平台的支持. 它采用了一种称为SPIR-V的新高级中间语言,能够在多个平台上部署, 并且能够同时被着色器和GPU架构利用. 它还能适用于无需图形界面的GPU计算场景. 和者类低功耗API的不同之处在于,它不仅支持跨平台,还能够覆盖从工作站到移动设备的各种应用场景

在移动设备上使用的OpenGL ES(其中"ES"代表嵌入式系统),因为该API专为移动设备设计。然而,在标准的OpenGL中对某些设备而言计算量较大且功能有限。2003年时发布的第一代版本OpenGL ES 1.0是对OpenGL 1.3的一个精简版本,在支持固定渲染管线方面做了简化。尽管DirectX当时发展迅速但在移动设备上的应用较为受限。例如第一代iPad于2010年发布时采用的是OpenGL ES 1.1版本而在2007年就已经发布了 OpenGL ES 2.0版本支持可编程着色功能。随后于2012年发布的是 OpenGL ES 3.0版本首次引入了多渲染目标(multiple render target)纹理压缩变换反馈(transform feedback)以及实例化(instancing)等功能增强了着色能力支持更多类型纹理和模式并改进了着色编程语言。之后如2013年的 OpenGL ES 3.1版加入了计算着色功能而到了2014年的 OpenGL ES 3.2版则增加了几何着色和曲面细分着色等多种新特性这些内容将在后续章节中进行详细探讨

属于 OpenGL ES 的一个分支 WebGL 被应用于浏览器。
于2011年首次发布,并被广泛应用于多数移动设备。
其功能类似于 OpenGL ES 2.0。
而 WebGL 2 则对应于 OpenGL 3.0

WebGL比较适合在课堂上教学使用:

1、它是跨平台的,可以在所有的个人电脑和大部分移动设备上使用。

2、基于浏览器的支持。可能某些浏览器不支持特定的GPU功能,而有些其他浏览器则可能具备这种功能。

3、 代码是interpreted,不需要编译,开发环境只需要一个文本编辑器。

4、大部分浏览器是支持Debug的,代码可以在任何网站上。

5、 程序可以通过上传到网站或者GitHub来部署。

高级场景图和效果库中包含丰富的场景图与效果集合,在线资源中可便捷获取各种复杂的渲染效果代码。其中three.js是一个极具代表性的开发平台,在此框架下方便获取各种复杂的渲染效果代码包括阴影算法、后期处理、物理基模的渲染以及延迟渲染技术等。

3.5 顶点着色

如图3-2所示,在现代图形处理器(GPU)渲染流程中占据首要环节的是顶点着色阶段。值得注意的是,在这一关键步骤之前还有其他操作需要完成。具体而言,在DirectX技术框架下这一过程被称为输入汇编(input assembler ,它负责将多个数据流整合为发送给渲染管线的完整顶点信息与图形元素信息。例如,在三维建模时一个物体可以用一组坐标值和一组颜色值来表示;通过输入汇编系统则能够生成这些坐标与颜色对应的完整顶点集合,并利用这些顶点构建三角形、线条或单个点等几何元素。此外该系统还支持批量实例化处理:在一次Draw Call周期内同一个模型可以被多次复制并应用不同的属性参数以实现动态效果

一个三角形网格可以通过一系列指定位置的顶点来表示一组模型表面。除了坐标位置以外,还有其他的和顶点相关的其他参数,例如颜色值或纹理坐标。网格表面处的法线同样被定义在每个顶点上 。从数学角度来看,每个独立存在的三角形都具有明确唯一的表面法线,并基于其自身的法线进行阴影计算往往更为有效。然而,在渲染过程中,通常会将三角形网格视为隐式曲面进行处理,而并非直接采用其自身的几何属性来表现表面的方向性。16.3.4节将详细阐述如何计算这些顶点处的法线向量方法。图3.7展示了两个不同侧视图中的三角形网格:一个是光滑呈现形状,另一个则显示出明显的折痕特征曲线

图3.7展示了三角形网格在侧视图中的表现形式,在该视图中黑色线条代表各个顶点处的方向向量(即法线),而红色区域则显示出了整个表面的大致形状。左侧区域体现出了光滑曲面上各顶点处的方向向量(即法线),而右侧区域显示出了带有折痕影响后的各顶点处的方向向量(即折痕处出现异常方向向量)。在中间位置上我们观察到了一个额外增加的部分——该位置上我们不仅复制了一份原始数据,并且为每个新增的数据分配了一个对应的位置方向向量(即新的方向向量)。

处理三角形网格的第一步是进行顶点上色不适用这种用于描述构成多边形数据的方法来实现对三角形的上色操作。根据名称可知,在上色过程中可以通过修改或忽略附加于每个三角形的各个顶点数据, 包括颜色值、法线、纹理坐标及位置等信息, 来达到预期效果。在执行上色时, 程序通常会将模型空间中的各个顶点转换为齐次裁剪空间, 并根据需要计算相应的坐标值

在图形渲染 pipeline 中,默认情况下我们通常会采用一种称为统一着色器(Unified Shading Program)的方式来进行表面着色操作。该过程接收输入 vertex 数据后进行插值计算得到 output data。在进行 vertex 着色时既不允许新增也不允许销毁任何 vertex 节点,并且每个节点生成的结果仅限于自身使用。因为每个处理单元独立完成各自的任务,在 GPU 架构下可同时处理大量并行的数据流

在执行顶点着色之前时态发生了变化,在此之前的阶段中,在输入进入系统之前就已经进行了必要的预处理步骤。模型通常分为两种类型:物理模型和逻辑模型;驱动在为每个顶点准备时会自动添加相关的指令这些指令是隐藏起来的不影响开发者的工作流程

接下来章节阐述了顶点着色效果,如动态关节的顶点绑定和轮廓绘制.此外:

· 生成新的对象。用顶点着色变换只会创建一次的mesh。

·利用Skining(蒙皮)和morphing(变形)技术制作人物动画和面部。

· 程序化变形,例如旗帜、衣服和水。

生成粒子。首先将没有渲染面积(no area)的mesh对象传递给管线进行处理;接着为网格添加相应的渲染区域。

光栅形变、温度弯曲、水面波动以及翻页效果等都是常见的视觉效果表现方式。为了实现这些效果,在图形渲染 pipeline中将帧缓存区的数据视为一个按屏幕网格排列且与显示区域对齐的纹理图,并对其进行程序化地进行形变处理以达到预期的效果展示。

· 地形高度场。

图3.8展示了顶点着色的一些变形例子。

图3.8 左边部分呈现的是一个正常的茶壶模型。中间部分展示了通过顶点着色进行简单剪切处理后的茶壶模型。右边部分则展示了受到噪声影响而发生形变的茶壶

其输出可采用多种方式进行应用,在计算机图形学领域具有重要价值。通常用于生成相应的几何体(如三角形),随后经过光栅化处理以确定哪些像素将被进行像素着色。在某些高性能图形处理器(GPU)中,则可能进一步执行曲面细分、几何涂色或数据存储等操作。这些技术在后续章节中将会有更加详细的探讨和技术实现路径研究

3.6 曲面细分阶段

该阶段通过支持实现对曲面的呈现。
GPU负责将每个表面表示为一组适当的三角形。
该阶段首次引入于DirectX 11版本,并在之后的OpenGL 4.0 和OpenGL ES 3.2版本中得到了相应的支持。

采用曲面细分技术具有显著的优势。采用曲线表型法来模拟表面结构并简化复杂几何体,在节省内存之外,在GPU处理过程中通过减少细节层次避免成为性能瓶颈。在特定视口中设定适当数量的三角形网格用于表面建模,并根据距离自动调节细节层次以平衡视觉效果与计算效率。例如,在球体距离摄像机较远时仅需少量三角形即可近似呈现其形状;当球体靠近摄像头时则需要密集的三角形网格才能准确捕捉细节。LOD技术的应用同样有助于提升整体渲染效率,并通过不同层次细节切换实现资源优化配置。为了维持渲染效率,在高性能不足的情况下优先采用低精度模型进行渲染以确保流畅运行速度的同时保证视觉质量不会因过多细节而影响帧率表现。模型通常采用高精度多边形来模拟平面与复杂几何体从而实现高效的图形处理效果

曲面细分分为三个阶段,在DirectX中被称作表面分割辅助程序( hull processor)、分割执行单元( tessellator)、顶点属性渲染程序( vertex attribute processor);而在OpenGL中则分别为 表面分割控制单元( tessellation control unit)、表面分割评估单元( tessellation evaluation unit);名称虽显冗长但更具 specificity;而固定功能细分设备通常被称为图元生成程序。

针对曲面细分的各个阶段, 我们进行简要总结。首先, 壳着色器接受一种特殊的控制点(patch )作为输入。其次, 壳着色器具有两大功能: 其一, 在不同配置下要求细分器生成特定数量的三角形; 其二, 则负责处理每个控制点。通过壳着色器可以灵活设置patch类型, 并增删或调整控制点位置。最后, 壳着色器输出的一组新的控制点集后传递给域处理器进行处理。参考图3.9

在图3.9所示的曲面细分阶段中,在线将多个patch依次传递至壳着色器,并由壳着色器接收并传输包含细分因素(TFs) 以及类型信息的数据包至固定功能细分器。随后根据需求对控制点进行调整或优化处理,并整合后一并传递至域着色器。接着基于重心坐标计算生成一批新的顶点位置,并将这些新顶点位置与域着色器沟通以完成最终的三角形Mesh输出。

在几何处理流程中,细分器(tessellator)是一个固定功能模块,其工作由细分着色器负责。它的主要职责是向域着色器注入新的顶点信息。壳着色器应通知细分器曲面的细分类型:三角形、四边形或等值线。这些类型的划分对于后续图形渲染具有重要意义。另一个关键的概念是** subdivision factors ** ,其在 OpenGL 中被称为 ** tessellation levels ** ,并分为 ** internal edges 和 external edges 两种类型:前者处理内部区域的精细程度后者则控制外部边缘划分的程度。图3.10展示了当 subdivision factors 增加时的效果示例。通过独立调节 internal 和 external factors 我可以确保相邻曲面之间的边界细化适中并不需过多关注内部区域的具体细节问题。为了实现这一目标重心坐标体系确定了表面各点相对于整体的位置信息因此生成的新顶点按照重心坐标分布在整个表面区域

图3.10展示了不同细分因素的表现位置。(patches)茶壶包含32个控制点,并通过内部和外部细分分别设置为1, 2, 4, 8个元素.

壳着色器通过patch进行变换处理后创造出一组新的控制��;细分器将新生成的网格数据传递给域着色器;域着色器利用曲面上的控制点信息来计算每个对应顶点的位置坐标;其数据处理流程与vertex coloring方式具有相似性:首先采用细分器产生的所有vertices作为输入数据源,在经过合理计算后产生相应的output vertices并通过连接相邻的output vertices形成三角形结构,并将其传递到下一个处理阶段

尽管这个系统听起来有些复杂性很高,在这种架构下实现了高效的性能。尽管每个着色器都相对简单但在特定设计下仍能发挥出色效。在经过壳着色器的patch提交后并未发生任何变化这表明该过程并未引入任何修改内容。壳着色器能够根据相邻patch之间的距离以及屏幕尺寸来计算出一个细分因子这个因子直接关系到整体图形细节的表现效果。细分器则通过计算出新的顶点位置并指示所需类型的几何体模型从而为后续渲染过程提供必要的几何信息基础。域着色器采用重心坐标系的方法确定每个采样点的位置并基于此生成完整的法线贴图以及其他必要的顶点属性数据为最终渲染奠定基础如图3.11所示展示了这一系列操作的具体实施过程。

图3-11展示了左侧模型约有6千多个三角形数量及其通过PN细分处理后的情况。

3.7 几何着色

在DirectX 10版本中增加了几何着色功能,在渲染管线中紧随细分曲面着色器完成工作流程,并作为可选项引入;该技术要求使用Shader mode 4.0,在早期版本中无法应用;同样地,在OpenGL 3.2及OpenGL ES 3.2等较新版本中得到了实现

几何着色的主要输入是一个单独的对象及其所有顶点 。这些对象通常涉及三角形、线段或零散的点,并通过几何学方法进行定义与操作扩展图形元素。特别地,在与原始三角形不直接相连的位置还可以引入辅助顶点,并且还可以借助邻近于折线端点的那个额外顶点来完成某些操作(如图3.12所示)。这两种渲染技术均支持最多32个控制参数,并建议在进行曲面细分阶段时优先考虑分块贴图生成

图3.12 几何着色法所处理的对象包括几个基本元素:如点、线段和三角形。在右侧第二个图形元素中包含了一条线段及其相连的两个端点;而位于其外侧的那个图形元素则是一个由三个不在同一平面上的顶点构成的三角形.

顶点着色器被开发用来调整输入数据并执行有限复制操作。例如,在一个立方体中将每个面扩展为6个子面并重新渲染以实现立方体表面效果。同时还可以用于生成高级联机阴影贴图来提升渲染质量。如图3.13所示。

案例展示了基于几何着色(GS)的应用情况。具体而言,在左方区域中展示了通过 GS 实现元球体等值面细分的操作过程;中间部分则呈现了结合 GS 和 流输出 对 线段 的 分形细分过程,并通过 GS 生成 公告板 展示 闪电效果;而右方展示的是采用顶点着色与几何着色相结合,并借助流输出模拟布料形态的技术过程。

DirectX 11采用了基于几何贴图的实例化技术,在这一技术下可以在任意图形元素上重复应用其效果,并在 OpenGL 4.0 中通过指定调用次数来实现控制。该技术最多能够生成四个不同的数据序列之一来供后续流程使用;每个数据序列都有选择性地被传递到渲染目标以完成显示工作

几何着色过程中的输出顺序遵循输入序列 ,由于同时处理多个几何着色核心时需要确保输出结果具有一定的有序性。

在DrawCall过程中, 仅限于光栅化. 曲面细分阶段以及几何着色阶段是可以生成新对象的. 基于内存与资源限制, 几何着色阶段是最难以预测的, 由于其高度可编程性. 实践中很少使用该功能, 因此在其优势无法与其匹配的情况下才得以存在. 在许多移动设备上采用软件实现该功能是不被推荐的.

3.7.1 流输出

在标准GPU管道中,数据依次从顶点着色阶段出发,在经过光栅化阶段后交给像素着色阶段处理。以前的数据通过整个渲染管线流动的过程中无法被直接访问到。因此,在Shader Model 4.0版本中提出了流输出的概念。在顶点着色阶段完成所有顶点计算后会生成结果数据集(例如一个按顺序排列好的队列)。实际上可以通过关闭栅格化功能并使整个渲染管道专注于非图形化流处理器的任务来优化性能。通过这种方式处理的数据能够通过整个管道返回,并支持迭代式的数据处理流程。这种操作特别适用于模拟水和其他粒子效果等场景,并且也可以用来实现物体蒙皮效果以提高复用性。

流输出仅采用浮点数形式返回数据信息,并由此产生明显的内存消耗。流输出的作用对象是图元而非单个顶点 。当网格被导入到管道中时,在处理过程中每个三角形会自动生成独立的顶点集合,并且原有的共享顶点将消失。为了实现这一目标,在流水线处理阶段通常会将这些独立生成的图元发送给后续处理环节。在OpenGL渲染流程中,在流水线阶段称为变换回馈(transform feedback),该过程用于完成变换操作并将其结果反馈回来以便后续使用。值得注意的是,在流水线操作完成后,所有参与变换操作后的各组数据需要维持其原有的顺序关系以确保系统的正常运行

3.8 像素着色

光栅化阶段是一个较为固定的处理流程,并具备配置能力。为每个三角形进行扫描以识别其覆盖的像素范围的同时,并估算该三角形对这些像素区域的影响程度。则称这些被完全或部分遮挡的像素为片元(fragment)

三角形顶点的值包括z缓冲区中的z值,在三角形表面的每个像素执行内插计算;这些计算结果随后发送到片元着色阶段进行处理。在OpenGL中将该过程称为片元着色(fragment shader),这一名称可能更为准确。

在大多数场景中,我们采用透视校正插值技术来实现像素表面位置之间世界空间距离随物体远离观察者的退后而增大的效果。例如,在渲染轨道时,请注意随着观察者与铁轨的距离增加(即铁轨越远),相邻枕木之间的间距逐渐缩小。另外还有一些插值方法可选,在DirectX 11中,默认情况下采用的是屏幕空间插值技术

伴随着GPU技术的进步和发展,在许多情况下使用的输入数量也在不断增加。例如,在判断一个多边形是否可见时所依据的一个重要标准就是它所在的平面。在单个pass过程中将同一个三角形的一面设置为一种材质而另一面设置为另一种材质这样的操作对于图形渲染的质量提升具有重要意义。

通常,在像素着色过程中会计算并输出每个片元的颜色。
它不仅能够生成不透明度,并且还可以选择性地调整其z-depth。
在融合(merge)阶段中,默认情况下,默认不可编辑的数据通常指的是模板缓冲中的数值。
经过光栅化(fragment)处理得到的深度信息同样可以在该阶段进行调整。
但这些信息会被传递至融合(merge)阶段处理。
DirectX 11.3版本之后首次引入了这种功能。
根据上述描述,在Shader Model 4.0标准下,默认情况下,默认不可编辑的数据通常指的是模板缓冲中的数值。

在像素着色阶段可以丢弃片元;比如没有输出(如图3.14所示)。 原来的裁剪平面功能曾经位于固定功能管道中的一个可配置组件(现移至顶点着色器)。 借助于裁剪平面的丢弃能力,在像素着色阶段都可以灵活地实现这一功能。

图3.14展示了定制化的切割平面设置。左边的图表展示了平切面切割后的呈现结果;中间的图像呈现了一个嵌套球体通过这三个自定义的裁剪平面切割后得到的结果;右边的图表显示只有当球体表面位于这三个自定义的切割平面前沿之外时才会进行截断处理后的结果。

最初阶段,在像素着色技术发展初期时, 输出结果仅限于供合并阶段使用, 其用途仅局限于这一特定环节的应用场景。然而, 随着像素着色技术的不断进步, 一颗像素着色器能够处理的指令数量经历显著提升, 这一变化促使多渲染目标(MRT)概念的诞生。值得注意的是, 与传统方法不同, 每个片元不再局限于向单一的颜色缓冲区域与深度缓存区域发送数据, 而是能够生成多个数据集合并分别存储在不同的存储区域里, 每个独立的数据存储区域都被称作一个'渲染目标'(Render Target)。此外, 可用的'渲染目标'数量因所使用的GPU型号而异, 具体范围通常在4至8个之间。

尽管受限于诸多因素, 但MRT功能却展现出了强大的高效性能. 仅需一次渲染流程, 即可在同一目标生成颜色值图像, 并在同一过程中获取物体形态数据. 进而于第三个目标获取空间距离数据. 这一技术衍生出一种新型渲染管道模式——延迟渲染架构, 其显著特征是可以分别执行可见性和着色阶段. 首先, 在第一个pass阶段记录物体位置数据及像素材质参数, 紧接着完成光照效果或其他效果模拟.

在进行像素着色时会遇到一些约束:首先,在相邻 pixels之间不能直接传输计算结果;其次还不能查看其他 pixels 的变化信息;只有当前 pixel 的计算结果才能影响自身显示效果。然而这些约束也没有想象中的那么令人担忧;通过在一个 pass 周期内生成一张临时图像可以在后续 pass 周期中进行处理以解决这一问题

这种规则同样面临一个问题:像素着色无法直接或间接影响相邻像素的处理结果。然而,在许多情况下这一限制并不成立:例如,在现代图形处理器中,默认会将四个相邻的片元组合在一起处理成一个称为"quad"的结构(见图3.15)。每个这样的quad实际上由4个独立但高度相关的片元组成(每个片元对应一个图像坐标点),这样 GPU 就能够高效地执行相关计算任务。

如图3.15所示,在quad区域(由2×2个像素组成)中进行光栅化。右图展示了这些被标记的像素及其梯度值计算过程。右图详细列出了四个像素各自的梯度值。值得注意的是,在三角形之外的三个像素虽然未直接参与该区域的绘制过程,但仍需由GPU对其梯度值进行计算。左下角位置处的像素在其x和y方向上的梯度值则基于其相邻的两个四邻像素进行计算。

在 DirectX 11版本中引入了一种称为Unordered Access View (UAV) 的缓冲机制用于直接访问(direct access)任何存储位置。最初仅限于像素渲染器和几何 shaders 的应用,在 DirectX 11.1版本起,默认情况下所有着色器均可支持UAV技术。与 OpenGL 4.3标准中的相关功能相对应的是Shader Storage Buffer Object (SSBO) 。这两个术语各有其特定描述。

如果两个像素着色器同时试图使用同一个索引值,则会出现问题。 它们都会在本地进行修改操作,并且最终只保留一个结果。 GPU借助专业的并行计算中的原子操作机制来解决这个问题。 但这种机制会导致某些着色器必须等待执行。

虽然原子能够防止数据通病,并且许多算法都需要按照特定顺序进行处理。举个例子,在DirectX 11.3中引入了Rasterizer Order Views (ROVs) ,这些机制能够确保操作按正确顺序执行。与无序访问机制类似的是UAVs(unordered),它们在读写方式上相同。这显著提高了着色器对缓存单元的访问能力。当检测到无序操作时,像素着色器可能会等待直至先前绘制完成。

3.9 合并阶段

如前所述,在2.5.2节中提到,在这一阶段中,每个片元会处理其各种深度值以及对应的颜色值,并将其与当前帧缓冲区的内容进行结合处理。DirectX将这一过程称为输出混合(output merger) ,而OpenGL则将其命名为每个样本操作(per-sample operations) 。在传统渲染管线架构中这一过程主要涉及模板缓冲与深度缓冲的操作环节。当片元可见时,在进行着色混合之前需要执行颜色混合操作;然而对于不可见的透明表面而言,则无需进行真实的颜色混合运算,因为它们的颜色会被当前帧缓冲区中的颜色直接覆盖掉。实际上,在进行半透明材质与合成操作时会发生这样的颜色值存储过程:无论是哪种情况,在完成着色运算后都会生成最终的颜色结果并将其存储到相应的目标缓存区域中

想象一下,在经过光栅化生成的片元进行深度测试时被先前的片元遮挡的情况。在这种情况下,在像素着色阶段所执行的所有操作均为无意义的操作。为了减少这种浪费现象的发生,许多图形处理器(GPU)会在像素着色之前实施一些合并测试(merge testing) 。这些测试将基于片元的z值来进行可见性判断,并且会将被遮挡的片元予以剔除。这个功能统称为早期深度比较(early-z) 。在某些情况下,在对一个像素进行着色时可能会修改片元的深度值或者完全舍弃该片元信息。如果在一个特定的像素着色程序中检测到此类行为,则必须关闭早期深度比较功能以避免影响渲染效率。DirectX 11标准以及OpenGL 4.2版本都提供了强制执行早期深度比较功能的机制,尽管这一机制伴随有诸多限制条件

尽管合并阶段不具备程序性特征,但它仍然高度可配置,能够适应多种场景需求。在颜色混合领域,提供了多种可供选择的操作方式,其中最常见的便是对颜色值与alpha通道进行乘法、加法以及减法等基本算术运算,当然还有取最大值、取最小值等高级操作,此外还包括按位逻辑运算等多种可能性。值得指出的是,DIRECTX 10版本支持帧缓冲技术以及两种颜色通道的混合操作,称为DUAL源色混合(Dual source-color blending),这一功能仅限于非多渲染目标(MRT)场景应用;而到了DIRECTX 10.1版本中,则实现了对多渲染目标(MRT)的支持:每个独立的颜色缓冲单元都能够执行不同的混合操作方案

通过 DirectX 11.3 的 Row Operations and Vertex Shaders(ROVs)实现混合编程。尽管存在一定的性能开销。ROVs 和合并阶段确保了正确的绘制顺序。不论像素着色器输出结果的生成顺序如何,在此过程中 API 都规定了这些数据必须被存储下来,并且按照输入序列传递至合并阶段。

3.10 计算着色阶段

传统图形功能中使用GPU实现的越来越普遍,并且还延伸到了多个其他领域,在金融衍生品定价等非图形领域也有广泛应用。比如在计算股票期权估值时以及用于训练深度学习神经网络的过程中都会用到这些技术手段。基于其强大的硬件计算能力被称为GPU computing

在Directx 11标准中定义的计算着色器是一种不在渲染管线中的着色器。这一类着色器之所以与图形渲染相关联的原因是因为它们被图形API所调用。这些着色器与位于渲染管线中的统一着色处理器处于同一个池中。与其他类型的着色体一样,它们都有输入端口可以访问缓冲区资源。在计算着色体中采用 warp 和 thread 等指令更为常见。 Directx 11标准引入了线程组(thread group)的概念 ,每个线程组可包含从一个到多个的线程 。为了便于在程序中引用这些线程体,在计算着色体内部使用 xyz 坐标来唯一标识每个工作单元 。每个线程组共享一个小内存,在Directx 11标准中该内存容量设定为32KB. 每个工作单元内部都是并发执行的

计算着色的一个重要优势是利用GPU生成的数据 。传输至CPU端存在微小延迟,在将数据的保存与处理移至GPU端完成,则能显著提升性能。这种技术经常应用于后期处理过程。共享内存机制允许相邻线程共用采样后的中间结果。通过应用计算着色技术来优化光照分布和平均亮度,在效率上相比单像素着色方法而言,在性能上提升了约两倍

计算着色不仅可以在**粒子系统( particle systems )中实现 ,同样也可以应用于网格处理( mesh processing )中 。例如面部动画( facial animation ) 以及裁剪( culling ) 、图像过滤( image filtering ) 、提升深度精度( improving depth precision ) 、阴影( shadows )以及深度领域( depth of field ) 等等 。如图3.16所示 。

图3.16 展示了计算着色方案。左幅图像展示了风吹动头发的效果,发丝采用了基于曲面细分技术的设计。中幅图像呈现了快速模糊效果,右幅图像展示了海浪般的运动感.

全部评论 (0)

还没有任何评论哟~