The Graphics Rendering Pipeline

"《Real-Time Rendering 4th》笔记——第二章 图形渲染管线"

Posted by Ciel on March 25, 2019

The Graphics Rendering Pipeline

渲染管线基本结构包括四个阶段:应用、几何处理、光栅化、像素处理。图形渲染管线的主要功能就是决定在给定虚拟相机、三维物体、光源、照明模式,以及纹理等诸多条件的情况下,生成或绘制一幅二维图形的过程。

\img\in-post\real-time-rendering\2-1

1 应用程序阶段

  • 应用程序阶段将需要绘制的图元输入到几何阶段,以及实现一些软件方式来实现的方法。主要方法有碰撞检测、加速算法、输入检测、动画、力反馈、纹理动画、变幻仿真、几何变形,以及一些不在其他阶段执行的计算,如层次视锥裁剪等加速算法。

  • 对于被渲染的每一帧,应用程序阶段将摄像机位置,光照和模型的图元输出到几何阶段。

2 几何处理阶段

几何阶段主要负责大部分多边形操作和顶点操作,还可以分为以下几个阶段:

\img\in-post\real-time-rendering\2-2

  1. Vertex Shading 顶点着色

    • 顶点着色器有两个主要任务:计算顶点位置和计算程序想要的任何顶点输出数据,如法线和纹理坐标。

    • 模型的顶点和法线是通过模型变换得到的。对象的坐标称为模型坐标,当模型转换应用于这些坐标后,模型就位于世界坐标或世界空间下。当模型各自进行模型转换后,就存在于同一空间中。

    • 仅对相机或视点可以看到的模型进行绘制。相机在世界空间中有一个位置方向用来放置和校准相机。

    • 为了便于投影和裁剪,要对相机和所有模型进行视点变换。变换的目的是把相机放到原点进行视点校准,使其朝向Z轴负方向,Y轴指向上方,X轴指向右边。视点变换后,实际位置和方向依赖于当前API。称上述空间为相机空间或观察空间。

    • 确定材质上的光照效果的这种操作被称为着色(shading)。着色过程涉及在对象上的各个点处计算着色方程(shading equation)。通常,这些计算中的一些在几何阶段期间在模型的顶点上执行(vertex shading),而其他计算可以在每像素光栅化(per-pixel rasterization)期间执行。可以在每个顶点处存储各种材料数据,诸如点的位置,法线,颜色或计算着色方程所需的任何其它数字信息。顶点着色的结果(其可以是颜色,向量,纹理坐标或任何其他种类的阴着色数据)计算完成后,会被发送到光栅化阶段以进行插值操作。

    • 通常,着色计算通常认为是在世界空间中进行的。在实践中,有时需要将相关实体(诸如相机和光源)转换到一些其它空间(诸如模型或观察空间)并在那里执行计算,也可以得到正确的结果。

    • 每个管道都有顶点处理,一旦这个过程完成有几个可选的阶段:曲面细分,几何着色,流输出。它们的使用取决于硬件和程序员的要求。它们彼此独立,不常用,在第三章中会分别介绍。

    【总结】顶点着色阶段的目的在于确定模型上顶点处材质的光照效果。

  2. Projection 投影变换

    \img\in-post\real-time-rendering\2-3

    • 在光照处理之后,渲染系统就开始进行投影操作,即将视体变换到一个对角顶点分别是(-1,-1,-1)和(1,1,1)单位立方体(unit cube)内,这个单位立方体通常也被称为规范立方体(Canonical View Volume,CVV)。

    • 目前主要有两种投影方法:正交投影(orthographic projection,或称 parallel projection)、透视投影( perspective projection)。两种投影方式的主要异同点:

      • 正交投影的可视体通常是一个矩形,可以把视体变换为单位立方体。主要特性是平行线在变换之后彼此仍然平行,这种变换是平移与缩放的组合。

      • 透视投影中,越远离相机的物体,在投影后看起来越小。更进一步来说,平行线将在地平线处会聚。透视投影的变换其实就是模拟人类感知物体的方式。

      • 正交投影和透视都可以通过4X4的矩阵来实现,在任何一种变换之后都可以认为模型位于归一化处理之后的设备系统中。

    • 虽然这些矩阵变换是从一个可视体变换到另一个,但它们仍被称为投影,因为在完成显示后,Z坐标将不会再保存于的得到的投影图片中。通过这样的投影方法,就将模型从三维空间投影到了二维的空间中。

    【总结】投影阶段就是将模型从三维空间投射到了二维空间中的过程。

  3. Clipping 裁剪

    • 只有当图元完全或部分存在于视体内部时,才需要将其发送到光栅化阶段,这个阶段可以把这些图元在屏幕上绘制出来。
    • 一个图元相对于视体内部分为三种情况:
      • 完全位于内部,直接进行下一阶段
      • 完全位于外部,直接丢弃不进入下一阶段
      • 部分位于内部,需要对那些位于视体内部的图元进行裁剪处理

    \img\in-post\real-time-rendering\2-4

    原书图2-6,投影变换后,只对单位立方体内的图元继续进行处理。因此,将单位立方体之外的图元剔除掉,保留单位立方体内部的图元,同时沿着单位立方体将与单位立方体相交的图元裁减掉。因此,会产生新的图元,同时舍弃旧图元。

    【总结】裁剪阶段的目的,就是对部分位于视体内部的图元进行裁剪操作。

  4. Screen Mapping 屏幕映射

    • 只有在视体内部经过裁剪的图元,以及之前完全位于视体内部的图元,才可以进入到屏幕映射阶段。进入到这个阶段时,坐标仍然是三维的(但显示状态在经过投影阶段后已经成了二维),每个图元的x和y坐标变换到了屏幕坐标系中,屏幕坐标系连同z坐标一起称为窗口坐标系。
    • 假定在一个窗口里对场景进行绘制,窗口的最小坐标为(x1,y1),最大坐标为(x2,y2),其中x1<x2,y1<y2。屏幕映射首先进行平移,随后进行缩放,在映射过程中z坐标不受影响。新的x和y坐标称为屏幕坐标系,与z坐标一起(-1≦ z ≦ 1)进入光栅化阶段。

    \img\in-post\real-time-rendering\2-5

    原书图2.7,经过投影变换,图元全部位于单位立方体之内,而屏幕映射主要目的就是找到屏幕上对应的坐标。

    • 屏幕映射阶段的一个常见困惑是整型和浮点型的值如何与像素坐标(或纹理坐标)进行关联。给定一个水平像素数组,并使用笛卡尔坐标,则在浮点坐标中,最左像素的左边缘为0:0。OpenGL一直使用了这个方案,DirectX 10及其后续版本也使用了这个方案。这个像素的中心是为0.5。因此,像素范围[0,9]涵盖了[0.0,10.0)的范围。转换公式如下:

    \img\in-post\real-time-rendering\2-6

    其中d是离散的像素数(整数),c是连续的像素值(浮点数)。

    • 虽然所有API都具有从左到右递增的像素位置值,但是在某些情况下,上下边缘的零点位置不一致。(0;0)位于OpenGL中图像的左下角,而在DirectX中则是左上角,从一个API迁移到另一个API时,必须考虑这些不同。

    【总结】屏幕映射阶段的主要目的,是将之前步骤得到的坐标映射到对应的屏幕坐标系上。

3 光栅化阶段

给定已转换和投影的顶点及其关联的着色数据,下一阶段的目标是找到原始元素(例如正在呈现的三角形)中的所有像素。这一过程称为栅格化,也称为扫描转换。这一阶段会将屏幕空间中具有z值(深度值)和各种顶点信息的二维顶点转换为屏幕上的像素。栅格化也可以被认为是几何处理和像素处理之间的一个同步点,因为正是在这里,三角形由三个顶点组成,最终发送到像素处理阶段。

\img\in-post\real-time-rendering\2-7

左:栅格化分为两个功能阶段,称为三角形设置和三角形遍历。右:像素处理分为两个功能阶段,即像素处理和合并。

  1. Triangle Setup 三角形设置

    • 在这一阶段中,对三角形的微分、边方程和其他数据进行了计算。这些数据可用于三角形遍历,也可用于插值几何阶段生成的各种着色数据。该过程在专门为其设计的硬件上执行。
  2. Triangle Traversal 三角形遍历

    • 在三角形遍历阶段将进行逐像素检查操作,检查该像素处的像素中心是否由三角形覆盖,而对于有三角形部分重合的像素,将在其重合部分生成片段(fragment)。
    • 每个三角形片段的属性均由三个三角形顶点的数据插值而生成(第五章会有讲解)。这些属性包括片段的深度,以及来自几何阶段的着色数据。

    【总结】找到哪些采样点或像素在三角形中的过程通常叫三角形遍历(TriangleTraversal)。

4 像素处理阶段

像素处理是对图元中的像素或样本执行每个像素或每个样本的计算和操作的阶段。

  1. Pixel Shading 像素着色

    • 所有逐像素的着色计算都在像素着色阶段进行,使用插值得来的着色数据作为输入,输出结果为一种或多种将被传送到下一阶段的颜色信息。纹理贴图操作就是在这阶段进行的。

    • 像素着色阶段是在可编程GPU内执行的,在这一阶段有大量的技术可以使用,其中最常见,最重要的技术之一就是纹理贴图(Texturing)。纹理贴图在书的第六章会详细讲到。简单来说,纹理贴图就是将指定图片“贴”到指定物体上的过程。而指定的图片可以是一维,二维,或者三维的,其中,自然是二维图片最为常见。如下图所示:

    \img\in-post\real-time-rendering\2-8

    原书图2.9 左上角为一没有纹理贴图的飞龙模型。左下角为一贴上图像纹理的飞龙。右图为所用的纹理贴图。

    【总结】像素着色阶段的主要目的是计算所有需逐像素操作的过程。

  2. Merging 合并

    • 每个像素的信息都储存在颜色缓冲器(Color buffer)中,颜色缓冲器是一个颜色的矩阵列(每种颜色包含红、绿、蓝三个分量)。合并阶段的主要任务是合成当前储存于缓冲器中的由之前的像素着色阶段产生的片段颜色。通常运行该阶段的GPU子单元并非完全可编程的,但其高度可配置,可支持多种特效。

    • 此阶段还负责解决可见性问题,这是通过z-buffer算法实现的。Z缓冲器和颜色缓冲器形状大小一样,每个像素都存储着一个z值,这个z值是从相机到最近图元之间的距离。每次将一个图元绘制为相应像素时,需要计算像素位置处图元的z值,并与同一像素处的z缓冲器内容进行比较。如果新计算出的z值,远远小于z缓冲器中的z值,那么说明即将绘制的图元与相机的距离比原来距离相机最近的图元还要近。这样,像素的z值和颜色就由当前图元对应的值和颜色进行更新。反之,若计算出的z值远远大于z缓冲器中的z值,那么z缓冲器和颜色缓冲器中的值就无需改变。需要注意的是,z-buffer在屏幕上的每个点只存储一个深度,因此不能用于部分透明图元。这些图元必须在所有不透明图元之后按前后顺序呈现,或者使用独立于顺序的算法(见5.5节)。透明度是z-buffer的主要弱点之一。

    • alpha 通道(alpha channel)和颜色缓冲器联系在一起可以存储个与每像素相关的不透明值。在较早的api中,alpha通道还用于通过alpha测试特性选择性地丢弃像素。现在,丢弃操作可以插入到像素着色器程序中,任何类型的计算都可以用来触发丢弃操作。这种类型的测试可以用来确保完全透明的片元不影响z-buffer。(见6.6节)

    • 模板缓冲器(stencil buffer)是用于记录所呈现图元位置的离屏缓冲。每个像素通常与占8个位。图 元可使用各种方法渲染到模板缓冲器中,而缓冲器中的内容可以控制颜色缓存和Z缓存的渲染。举个例子,假设在模版缓冲器中绘制出了一个实心圆形那么可以使用系列操作符来将后续的图元仅圆形所出现的像素处绘制,类似一个 mask的操作。模板缓冲器是制特效强大工具。而在管线末端的所有这些功能都叫做光栅操作(raster operations ,ROP)或混合操作( blend operations)。可以将当前在颜色缓冲区中的颜色与正在三角形中处理的像素颜色混合。这可以实现一些效果,如透明度或颜色样本的积累。如前所述,混合通常可以使用API进行配置,而不是完全可编程。然而,一些api支持光栅顺序视图,也称为像素着色器顺序,它支持可编程混合功能。

    • 帧缓冲器(frame buffer)通常包含一个系统所具有的缓冲器,但时也可以认为是颜色和z缓冲器的组合。

    • 而当图元通过光栅化阶段之后,从相机视点处看到的东西就可以在荧幕上显示出来。为了避免观察者体验到对图元进行处理并发送到屏幕的过程,图形系统一般使用了双缓冲(double buffering)机制,这意味着屏幕绘制是在一个后置缓冲器(backbuffer)中以离屏的方式进行的。一旦屏幕已在后置缓冲器中绘制,后置缓冲器中的内容就不断与已经在屏幕上显示过的前置缓冲器中的内容进行交换。注意,只有当不影响显示的时候,才进行交换。

    【总结】合并阶段的主要任务是合成当前储存于缓冲器中的由之前的像素着色阶段产生的片段颜色。此外,合并阶段还负责可见性问题(Z缓冲相关)的处理。

    5 纵览渲染管线

    在概念上可以将图形渲染管线分为四个阶段:应用程序阶段、几何处理阶段、光栅化阶段、像素处理阶段。

    这条管线是数十年来面向实时渲染应用程序的API和图形硬件发展的结果。需要注意的是,这不是惟一可能的渲染管线。离线渲染管线(offline rendering pipelines)也是另一种进化路径。电影制作的渲染通常使用微多边形管道,但是光线跟踪和路径跟踪最近开始流行。这些技术也可用于建筑和设计的可视化。

    多年来,应用程序开发人员使用这里描述的流程的唯一方法是通过使用图形API提供的固定功能管线。固定功能管线之所以如此命名,是因为实现它的图形硬件无法灵活编程。另一方面,可编程的gpu能够准确地确定在整个管线的各个子阶段应用了哪些操作。对于本书的第四版,我们假设所有的开发都是使用可编程的gpu完成的。