图形渲染管线 (chapter 2 graphics rendering pipeline)
Real-Time Rendering 4th 中文翻译 chapter 2目录
2.0 渲染管线概述
2.1 渲染管线架构
2.2 应用阶段 (The Application Stage)
2.3 几何阶段 (Geometry Processing)
2.4 光栅化阶段 (Rasterization)
2.5 像素(片元)处理阶段 (Pixel Processing)
2.6 纵览渲染管线以及深入学习资料
渲染管线概述
在深入探讨渲染管线时我们先要对渲染管线有一个大概的认识。渲染管线的主要功能是决定在给定虚拟相机、三维物体、光源、照明模式,以及纹理等诸多条件的情况下,生成或绘制一幅二维图像的过程。对于实时渲染来说,渲染管线就是基础。因此,我们可以说,渲染管线是实时渲染的底层工具。
2.1
2.1 展示了使用渲染管线步骤。渲染出的图像的位置、形状是由它们的几何形状,环境特性,摄像机位置决定的。而物体的外观由材质特性,光源,纹理和着色模型确定。
2.1 Architecture
2.1.1 流水线以及渲染流水线相关概念概述
在物理世界中,管线的概念有许多不同的形式表现出来,广义上来说工厂装配线到快餐厨房,又或者是图形渲染都试用管线的概念。
理想情况下,一个非流水线系统然后被分成 n 个步骤并按照流水线模式执行可以提供 n 倍的加速。渲染管线正如其名他的的执行过程是线性进行,每一个阶段的输入都依赖于上的输出。
2.2
这种流水线结构也存在于实时计算机图形环境中如上图2.2
图形渲染管线:
渲染管线的基本构建,由四个阶段组成:应用阶段,几何阶段、光栅化阶段和像素处理阶段。 这些阶段中的每一个阶段都可以是一个流水线本身,如几何阶段下面的小插图所示,或者一个阶段可以(部分)并行化,在像素处理阶段下方。 在此图中,应用程序阶段是单个进程,但是这个阶段也可以流水线化或并行化。
渲染性能的描述:
渲染速度可以用每秒帧数 (FPS) 来表示,即每秒渲染的图像数。 也可以用赫兹(Hz)来表示,这只是 1/秒的表示法,即更新频率。 也是通常只说明渲染图像所需的时间,以毫秒 (ms) 为单位。生成图像的时间通常会有所不同,具体取决于图像的复杂程度在每一帧期间执行的计算。 每秒帧数用来表示特定帧的速率,或一段时间内的平均性能使用。 赫兹用于设置为固定速率的硬件,例如显示器。
2.1.2 实时渲染综述:
实时渲染流水线分4个主要阶段,应用、几何处理、光栅化、逐片元(像素)处理
顾名思义,应用程序阶段由应用程序驱动,因此通常在运行于cpu 上的软件中实现。由于cpu多核架构,使 cpu 能够有效地运行应用程序阶段所负责的各种各样的任务。传统上在 CPU 上执行的一些任务包括碰撞侦测、全局加速算法、动画、物理模拟等等, 这取决于应用程序的类型。
下一个主要阶段是几何处理阶段,变换、投影和所有其他类型的几何处理。这个阶段计算要绘制什么,应该如何绘制,以及应该在哪里绘制。几何级通常在一个包含许多可编程内核和固定操作硬件的图形处理器处理器(GPU)上执行。
光栅化阶段通常将三个顶点作为输入,形成一个三角形,并找到三角形内的所有像素,然后将这些像素转发到下一个阶段。
最后,像素处理阶段按每个像素执行程序以确定其颜色,并可执行深度测试以查看其是否可见。它还可以执行逐像素操作,例如将新计算的颜色与以前的颜色混合。光栅化和像素处理阶段也完全在 GPU 上处理。
所有这些阶段及其内部管道将在接下来的四个部分中讨论。图形处理器如何处理这些阶段的更多细节在。
2.2 The Application Stage
开发人员可以完全控制在应用程序阶段发生的事情,因为它通常在 CPU 上执 行。因此,开发人员可以完全确定实现,并在以后修改它以提高性能。这里 的更改也会影响后续阶段的性能。例如,一个应用程序阶段算法或设置可以减少绘制的三角形数量。
所有这些都表明,一些应用程序的工作可以通过 GPU 来完成,使用一个单独的compute shader。这种模式将 GPU 视为高度并行的通用处理器,而忽略了其专门用于渲染图形的特殊功能。
在应用阶段结束时,将渲染的几何图形提供给几何处理阶段。这些是渲染原始数据(rendering primitives其实我这块的翻译更应该是渲染所需的图元数据),例如,点、线和三角形,它们最终可能出现在屏幕上(或者使用任何输 出设备)。这是应用阶段最重要的任务。
应用阶段基于软件的实现导致该阶段不再细分为子阶段。然而,为了提高性能,这个阶段经常在几个处理器核心上并行执行。在 CPU 设计中,这被称为超标量结构,因为它能够在同一阶段同时执行多个进程。(第 18.5 节介绍了使用多个处理器核心的各种方法)
在这个阶段通常实现的一个过程是碰撞侦测。在检测到两个物体之间的碰撞之后,可以生成响应并发送回碰撞物体以及力反馈装置。应用阶段也是处 理来自其他来源的输入的地方,比如键盘、鼠标或者头戴式显示器。根据这 个输入,可以采取几种不同的操作。加速算法,例如特定的剔除算法(第 19 章),也在这里实现,以及其他管道无法处理的算法。
2.3 Geometry Processing
图形处理器上的几何处理阶段负责大部分的逐三角形和逐顶点操作。这个阶段进一步划分为以下几个功能阶段:顶点着色、MVP变换、裁剪和屏幕映射。
2.3
2.3.1 Vertex Shading,transform混讲
顶点着色有两个主要任务,即计算顶点位置并评估编程人员可能希望作为顶点输出的数据内容,例如法线坐标和纹理坐标。(局部光照模型)传统上大部分的阴影通过将灯光与每个顶点的位置和顶点法线来计算。仅在顶点存储结果颜色。 然后对三个顶点所形成的三角形内部进行插值。出于这个原因,这个可编程的顶点处理单元是命名顶点着色器。
随着现代 GPU 的出现,以及一些或者全部像素发生的所有着色,程序员将决定这个阶段是否完成着色,又或者是放到片元着色器中。顶点着色器现在是一个更通用的单元,专门用于设置数据与每个顶点的联系。 例如,顶点着色器可以为一个对象设置动画(顶点动画使用第 4.4 和 4.5 节中的方法)。
在模型到达屏幕的过程中,模型被转换成几个不同的空间或坐标系。最初,模型驻留在它自己的模型空间中,这仅仅意味着它根本没有被转换。每个模型都可以与一个model transform相关联,这样它就可以被定位和定向。可以将多个模型转换与单个模型关联起来。这允许同一模型的多个副本(称为实例)在同一场景中具有不同的位置、方向和大小,而不需要复制基本的几何图形。
通过模型变换转换的是模型的顶点和法线。一个物体的坐标称为模型坐标, 在将模型变换应用于这些坐标之后,模型被称为位于世界坐标或世界空间中。 世界空间是独一无二的,在模型经过各自的模型变换后,所有的模型都存在于同一空间中。
如前所述,只有相机(或观察者)看到的模型是呈现并最终渲染在屏幕上。相机在世界空间中有一个位置和一个方向,用于放置并瞄准相机。为了便于投影和剪辑,相机和所有模型通过视图变换进行变换。视图转换(2.4图中的view transform)的目的就是把相机放在原点对准它,让它看向目标的方向负 z 轴,y 轴指向上方,x 轴指向右侧。我们使用 -z 轴约定;有些文本更喜欢向下看 +z 轴。这差异主要是语义上的,因为两者之间的转换很简单。这应用视图变换后的实际位置和方向是相关的,在底层应用程序编程接口 (API) 上。如此划定的空间称为相机空间,或更常见的是,视图空间或眼睛空间。一个例子视图变换影响相机和模型的方式显示在图 2.4。模型变换和视图变换都可以实现为4×4 矩阵,这是第 4 章的主题。然而,重要的是要认识到程序员可以用任何方式计算顶点的位置和法线.
2.4
这种确定光对材质影响的操作称为着色,它涉及计算物体上不同点的着色方程。 通常,其中一些计算是在模型的几何处理过程中执行的。每个顶点可以存储各种材质数据,例如点的位置、法线、颜色、或着色方程所需的任何其他数值信息。顶点着色结果(可以是颜色、向量、纹理坐标,以及任何其他类型的着色数据)然后被发送到光栅化和像素处理阶段,插值并用于计算表面阴影。
顶点着色器在GPU的形式会在本书后续更深刻的讨论,尤其是第 3 章和第 5 章。作为顶点着色的一部分,渲染系统执行投影然后裁剪,将视图体积转换为一个单位立方体,其极值点位于(-1, -1, -1) 和 (1, 1, 1)。例如,0 ≤ z ≤ 1。单位立方体称为标准视体积。先做投影,在GPU上由顶点着色器完成。 有两种常用的投影方法,即正交投影和透视投影(一些投影矩阵的细节不再过多叙述)。
Image
2.Optional Vertex Processing
透视投影有点复杂。 在这种类型的投影中,物体离相机越远,投影后它看起来越小。此外,相互平行线可能会在地平线上会相交(看起来)。 透视变换模仿了我们感知物体大小的方式。 同时在几何上,视椎体称为截锥体,是一个具有矩形底面的截头金字塔。
正交变换和透视变换都可以用 4 × 4 矩阵构造(第 4 章),并且在任一变换之后,模型顶点信息在裁剪坐标中是定义为齐次坐标,这将在第 4 章中讨论,所以这发生在除以 w 之前。 GPU 的顶点着色器必须总是输出这种类型的坐标,以便下一个功能阶段,裁剪才能正常工作。
虽然这些矩阵将一个体积转换为另一个体积,但它们被称为投影,因为在显示之后,z 坐标不会存储在生成的图像中,而是存储在 z 缓冲区中,如第 2.5 节所述。 这样,模型被投影从三个维度到两个维度。
2.3.2 Optional Vertex Processing
每个管道都有刚才描述的顶点处理。一旦这个处理完成,有一些可选的阶段, 可以发生在 GPU 上。
按照这样的顺序:曲面细分,geometry shading,和stream output。
总结:
tessellation: 根据物体距离像机的远近自动生成曲线表面。
geometry shading: 用于自动生成新的顶点,通过用于生成粒子。
stream output: 将数据通过数组发送给基础处理单元,这些数据可以被CPU,或GPU在之后的阶段使用,通过用于粒子发射器。
1.Tessellation Shader
第一个任选阶段是曲面细分。想象你有一个球对象。无论您使用多少三角形来表示它,都会遇到质量或性能方面的问题。你的球在 5 米远的地方看起来可能很好看,但是靠近一些单独的三角形,特是沿着轮廓,就可以明显的看到菱角。如果你使用更多的三角形来提高画面质量,你可能会浪费大量的处理时间和内存,因为球离得很远,而且只覆盖了屏幕上的几个像素。通过曲面细分,一个曲面可以生成适当数量的三角形。
我们已经谈了一些关于三角形的内容,但到目前为止,我们只是处理了顶点。顶点可以用来表示点、线、三角形或其他对象。 顶点可用于描述曲面,例如球。这样的曲面可以通过一组patches来指定,每个patches由一组顶点组成。曲面细分阶段由一系列阶段组成,包括hull shader、tessellator和domain shader,这些阶段通常会将这些顶点集转换成更大的顶点集,然后用这些顶点集生成新的三角形集。场景的摄像头可以用来确定生成了多少个三角形:patch关闭时生成了很多个三角形(不理解),远离时生成了很少的三角形。
每个火球都可以用一个点来表示,一个顶点。几何着色器可以将每个点转换成一个正方形(由两个三角形组成),面向查看者并覆盖几个像素 所以为我们提供了一个更有说服力的原始阴影。
2.Geometry Shader
下一个可选阶段是几何着色器。该着色器早于曲面细分着色器出现,因此在 GPU 上更常见。它就像曲面细分着色器一样可以接受各种类型的图元并可以产生新的顶点。这是一个简单得多的阶段,因为这个创建的范围非常有限,而且输出原语的类型也非常有限。几何着色器有几种用途,其中最流行的是粒子生成,模拟一场烟花爆炸。每个火球都可以用一个点,一个顶点来表示。几何着色器可以将每个点变成一个正方形(由两个三角形组成),面向观察者并覆盖多个像素,从而为我们提供更有说服力的图元进行着色。
2.3.stream output
最后一个可选的阶段被称为流输出。这个阶段让我们使用GPU作为一个几何引擎。而不是将我们处理过的顶点发送到管道的其余部分,以呈现到scr 甚至,在这一点上,我们可以选择性地输出这些到一个数组,以进行进一步的处理。这些数据可以被CPU或GPU本身在稍后过程中使用。这个阶段通常用于粒子模拟,比如我们的烟花的例子。这三个阶段按照这个顺序执行:tessellation, geometry shading,and stream output,每个阶段都是可选的。如果我们继续沿着管道前进,我们将有一组具有齐次坐标的顶点,我们将检查摄像机是否查看它们。
2.3.3 Clipping
只有全部或部分在视锥体内的物体的图元数据需要传递到光栅化阶段(以及后续的像素 处理阶段),然后在屏幕上绘制它们。完全位于视锥体中的图元数据将按原样传递到下一个阶段。因为完全在视锥体之外的物体不会呈现。所提它们的图元数据不会进一步传递,需要裁剪的是部分位于视锥体内的图元。例如,有一个顶点在外面,一 个在视图体积内的直线应该根据视图体积来裁剪,这样外面的顶点就会被位 于直线和视图体积之间的交点上的新顶点所替换。使用投影矩阵意味着将变 换后的primitives裁剪到单位立方体上。在裁剪之前执行视图转换和投影的优点是 它使裁剪问题保持一致;总是将图元裁剪到单位立方体上。
Image
2.3.2 Screen Mapping
只有视锥体积中的图元数据才被传递到屏幕映射阶段,(换句话说就是只有在)进入这个阶段时坐标仍然是三维的(在数据表示中其实是四维的齐次坐标)。每个基元的 x 坐标和 y 坐标被转换为屏幕坐标。屏幕坐标和 z 坐标也称为窗口坐标。假设场景应该呈现在一个窗口中,最小角位于 (x1,y1),最大角位于(x2,y2),其中 x1 < x2 和 y1 < y2。然后,屏幕映射是一个平移,接着是一个缩放操作。新的 x 坐标和 y 坐标称为屏幕坐标。Z-coordinate([-1,+1]forOpenGLand forDirectX)也映射到。这步构造的坐标也就是NDC,这些都可以通过 API 进行修改。窗口坐标和重新绘制 的 z 值一起传递到光栅化阶段。屏幕映射过程见下图。
Image
我们将描述整数和浮点值与像素(和纹理坐标)之间的关系。给定一个像素水平排列使用笛卡尔坐标表示,最左边缘的像素在浮点坐标中为 0.0。 Opengl 一直使用这种方案(而directx虽然最左边缘的像素在浮点坐标中为 0.0,但与opengl有略微不同)。这个像素的中心位于 0.5。因此,一个像素范围覆盖了从的范围。这个转换过程 很简单
d = floor(c)
c = d + 0.5
PS: 其中 d 是像素的离散(整数)索引,c 是像素内的连续(浮点)值。
虽然所有 api 都有从左向右增加的像素位置值,但是在某些情况下, OpenGL 和 DirectX 之间的顶部和底部边缘的零位置是不一致的。OpenGL支持笛卡尔坐标系统,将左下角视为最低值元素,而 DirectX 将左上角定义为最低值元素。每个问题都有其逻辑性,不同的问题没有正确的答案。 例如,(0,0)在 OpenGL 中位于图像的左下角,而在 DirectX 中位于左上角。在 从一个 API 转移到另一个 API 时,必须考虑到这种差异。
注:“Direct3D”是 DirectX 的三维图形 API 组件。Directx 包括其他 API 元素,比 如输入和音频控件。在讨论这个特定 API 时,我们没有区分在指定特定发行 版时编写“DirectX”和在讨论这个特定 API 时编写“Direct3D”,而是遵循通常的用法,自始至终编写“DirectX”。
2.4 Rasterization
Image
左图:光栅化分为两个功能阶段,称为三角形设置和三角形遍历。右图:像素处理分为两个功能阶段,即像素处理和合并(混和)。
如何设置三角形重叠的像素取决于你如何设置 GPU 的管道。例如,您可以使用点采样来确定“insideness”最简单的情况是在每个像素的中心使用一个点样本,因此如果中心点在三角形内部,那么相应的像素也在三角形内部。您还可以使用抗锯齿中的msaa,对每个像素使用多个采样。还有一种方法是使用conservative rasterization,其定义是,如果像素至少有一部分与三角形重叠,那么像素就在三 角形的“内部”(第 23.1.2 节)。
2.4.1 Triangle Setup
在这一阶段,对顶点传输的三角形点的数据进行计算。这些数据可用于三角形遍历,以及插值由几何阶段产生的各种shading数据。此任务使用固定功能硬件。
2.4.2 Triangle Traversal
将检查三角形覆盖其中心(或样本)的每个像素,并为重叠于三角形的 像素部分生成片段。更详细的抽样方法可以在(5.4节)。找到三角形内部的样本或像 素通常被称为三角形遍历。每个三角形片段的属性是使用插值在三个三角形 顶点(Cahpter 5)中的数据生成的。这些属性包括碎片的深度,以及来自几何阶段的任何shading数据。McCormack等人提供了更多关于三角形穿越的信息。在三角形上执行透视校正插值(第 23.1.1 节)。然后将primitives中的所有像素 或样本发送到下一步描述的像素处理阶段。
2.5 Pixel Processing
此时,作为前面所有阶段组合的结果,已经找到了在三角形或其他primitives中考 虑的所有像素。这个像素处理阶段分为像素着色和合并。如图2.8右图所示。 像素处理是对图元内的像素或样本执行逐像素或逐样本计算和操作的阶段。
2.5.1 Pixel Shading
在此阶段使用插值的着色数据作为输入,执行逐像素的着色。将检查三角形覆盖其中心(或样本)的每个像素,最终的结果是一个或多个颜色被传递到下一个阶段。不像三角形设置和遍历阶段,通常是硬件执行(不可编程),像素着色阶段是执行可编程的 GPU 核心。为此,程序员为像素着色器(在 OpenGL 中称为fragment shader)提供一个程序,它可以包含任何想要的计算。这里可以采用各种各样的技术,其中最重要的是纹理技术。纹理是在更详细的处理在Chapter 6。 图像可以是一维、二维或三维的,最常见的是二维图像。最简单地说,最终结果是每个片段的颜色值,这些颜色值被传递到下一个子阶段。
Image
2.5.2 Merging
每个像素的信息存储在颜色缓冲区中,该缓冲区是一个颜色矩形数组(对于每种颜色,一个红色值、一个绿色值和一个蓝色值)。合并阶段的职责是将由像素着色阶段产生的片段颜色与当前存储在缓冲区中的颜色结合起来。这个阶段也称为 ROP,代表“光栅操作(管道)”或“渲染输出单元”,这取决于您询问的对象。与shading阶段不同,执行这一阶段的 GPU 子单元通常不是完全可编程的。但是,它是高度可配置的,能够实现各种效果。
这个阶段还负责解决可见性问题。这意味着,当整个场景已经被渲染时,颜色缓冲区应该包含从照相机的角度来看场景中全部可见的图元数据,这是可行的。对于大多数甚至所有的图形硬件,这是通过z缓冲区(也称为深度缓冲区)算法来完成的。详细的z-buffer算法我就不再赘述了(可以参考games101中的知识)。
我们已经提到过,颜色缓冲区用于存储颜色,而深度缓冲区存储每个像素的 z 值。但是,还可以使用其他通道和缓冲区来过滤和捕获片段信息。Alpha 通道与颜色缓冲区相关联,并为每个像素存储相关的不透明度值。在旧的 api 中,alpha 通道也被用来通过 alpha 测试特性有选择地丢弃像素。现在,丢弃操作可以插入到像素着色程序中,任何类型的计算都可以用来触发丢弃。这 种类型的测试可以用来确保完全透明的片段不会影响 z 缓冲区。
模板缓冲区是一个离屏缓冲区,用于记录呈现primitives的位置。它通常每像素 包含 8 位。可以使用各种函数将primitives呈现到模板缓冲区中,然后可以使用缓冲区的内容控制渲染到颜色缓冲区和 z 缓冲区。例如,假设一个已填充的圆已经被绘制 到模具缓冲区中。这可以与一个操作符相结合,该操作符只允许将后续的primitives呈现到存在圆的颜色缓冲区中。模具缓冲区可以是一个强大的工具,生成 一些特殊的效果。管道末端的所有这些功能称为光栅操作(ROP)或混合操作。 可以将当前在颜色缓冲区中的颜色与正在三角形中处理的像素的颜色混合。 这可以使效果,如透明度或积累的颜色样本。如前所述,混合通常可以使用 API 进行配置,但不能完全编程。然而,有些 api 支持光栅顺序视图,也称为像素着色器顺序,它支持可编程混合功能
framebuffer通常由系统上的所有缓冲区组成。
当图元数据已经达到并通过光栅化阶段,那么他会直接被呈现在屏幕上吗?不会,如果直接呈现会有画面的撕裂感。为了避免让像素出现单个单个变化,通常会将一张画面的全部像素都渲染完成后再发送给屏幕。这个过程使用了双缓冲区(我不知道这个词翻译的对不对)(double buffer)。这意味着场景的渲染是在屏幕之外的back buffer中进行的。一旦场景在back buffer中渲染完毕,back buffer的内容就会与之前显示在屏幕上的front buffer的内容进行交换。交换通常发 生在vertical retrace期间,这个时间段是安全的。
2.6 纵览渲染管线以及深入学习资料
这个管道是几十年来面向实时渲染应用程序的API和图形硬件发展的结果。需要注意的是,这并不是唯一可能的渲染管道;离线渲染管道经历了不同的演化路径。胶片制作的渲染通常是用微多边形管道完成的,但光线追踪和路径跟踪是最近才出现的。这些第 11.2.2 节所述的技术,也可用于建筑和设计的预视化。
多年以前,程序开发人员使用这里描述的过程的唯一方法是通过由正在使用的图形API定义的固定函数管线。固定渲染管线之所以这样命名,是因为实现它的图形硬件由不能以灵活的方式编程的元素组成。最后一个固定渲染机器的例子是2006年任天堂推出的Wii。另一方面,可编程gpu可以准确地说明在整个管线的各个子阶段中应用的是哪些操作。对于这本书的第四版,对我们来说假设所有的开发都是使用可编程的gpu来完成的。
更多学习资料
Image
本文使用 Zhihu On VSCode 创作并发布
页:
[1]