Desc:
在看实时渲染中做的记录,根据QianMo翻译后的文章进行学习,链接引用, https://github.com/QianMo/Real-Time-Rendering-3rd-CN-Summary-Ebook
图形渲染管线
分为四个阶段:
应用阶段、几何阶段、光栅化阶段、像素处理阶段(RealTimeRenderer4th中的提出)
应用阶段
通过软件的方式实现的阶段,开发者能够对该阶段发送的情况完全控制,改变实现方法来改变实际性能。其他阶段,全部或部分建立在硬件基础上,改变实现过程会非常困难
虽然不能分为若干个子阶段,但是可以在几个并行处理器上同时执行。
超标量体系结构,可以在同一阶段实现不同的几件事
通常实现的方法:
碰撞检测、加速算法、输入检测、动画、力反馈以及纹理动画,变换仿真、几何变形,以及一些不在其他阶段执行的计算(层次视锥裁剪等加速算法)
主要任务:
末端,将需要在屏幕上显示出来绘制的集合体(图元:点、线、矩形)输入到绘制管线的下一个阶段
将摄像机位置、光照、模型的图元输出到管线的下一个阶段
几何阶段
细分的功能阶段管线:
+ 模型视点变换:
+ 顶点着色:
+ 投影:
+ 裁剪:
+ 屏幕映射:
执行计算量非常高的任务,在只有一个光源的情况下,每个顶点大约需要100次左右的精准浮点运算操作
模型和视图变换:
模型和视图变换阶段分为模型变换和视图变换。
模型变换的目的是将模型变换到适 合渲染的空间当中
视图变换的目的是将摄像机放置于坐标原点,方便后续步骤的操作顶点着色:
- 对于对象上各个点计算着色方程。片元上的颜色在像素光栅化期间执行。
- 在每个顶点处存储各种材料数据,如:点的位置、法线、颜色或计算着色方程需要的其他数字信息。顶点着色结果计算完成后,会发送到光栅化阶段进行插值操作
阶段目的:在于确定模型上顶点处材质的光照效果
- 投影:
- 光照处理完之后,进行投影操作。把视体变换到一个对角顶点分别是(-1,-1,-1)和(1,1,1)单位立方体内,这个单位立方体通常也被成为规范立方体(cvv)
a. 投影方法:
i. 正交投影:
ii. 透视投影:
都可以用4x4的矩形来实现,在任何一种变换之后,都可以认为模型位于归一化处理之后的设备坐标系中
虽然这些矩形变化是从一个可视体变换到另一个,但是他们仍被成为投影,因为在完成显示后,Z坐标不再保存于得到的投影图片中。将模型从三维投影到二维的空间中
阶段目的:将模型从三维空间投影到二维的空间中
- 裁剪:
- 只有当图元完全或者部分存在于视体中时,才需要将其发送到光栅化阶段,这个阶段可以把这些图元在屏幕中绘制出来
- 部分存在于视体内部的图元需要进行裁剪操作
阶段目的:对部分处于视体内部的图元进行裁剪操作
- 屏幕映射:
- 此时每个图元的x、y坐标变换到屏幕坐标系中,屏幕坐标系连同Z坐标一起成为窗口坐标系
阶段目的:将之前步骤得到的坐标映射到对应的屏幕坐标系中
光栅化阶段
给定经过变换和投影之后的顶点,颜色以及纹理贴图(均来自于几何阶段),给每个像素正确配色,以便正确绘制整幅图像。这个过程叫光栅化或扫描变换,即从二维顶点所处的屏幕空间(所有顶点都包含Z值即深度值,及各种与相关的着色信息)到屏幕上的像素的转换
功能阶段:
1. 三角形设定阶段:
2. 三角形遍历阶段:
3. 像素着色阶段:
4. 融合阶段:
- 三角形设定 Triangle Setup :
- 主要用来计算三角形表面的差异和三角新表面的其他相关数据。该数据主要用于扫描转换,以及由几何阶段处理的各种着色数据的插值操作所用。该过程在专门为其设计的硬件上执行
- 三角形遍历 Triangle Traversal:
- 逐像素检查操作,检查该像素处的像素中心是否由三角形覆盖,而对于有三角形部分重合的像素,将在其重合部分生成片段
- 找到哪些采样点或像素在三角形中的过程通常叫三角形遍历或扫描转换。每个三角形片段的属性由三个三角形顶点的数据插值而生成。这些属性包括片段的深度,以及来自几何阶段的着色数据
像素检查
- 计算像素点相对于三个顶点的位置权重,根据叉乘计算三个三角形的面积
- 根据重心坐标的定义,当一个点位于三角形内部时,其重心坐标的值满足以下条件:
- 重心坐标的每个分量(例如 alpha、beta 和 gamma)都在 [0, 1] 的范围内。
- 三个重心坐标的和等于 1,即 alpha + beta + gamma = 1。
公式
1 |
|
1 |
|
判断像素是否在三角形中,通过叉乘的性质
1 |
|
当图元通过光栅化阶段后,从相机视点处看到的东西就可以在屏幕上显示出来。为了避免观察者体验到对图元进行处理并发送屏幕的过程,图形系统一般使用了双缓存机制,屏幕绘制是在一个后置缓冲器中以离屏的方式进行。一旦屏幕已在后置缓冲器中绘制,后置缓冲器的内容就不断与已经在屏幕上显示过的前置缓冲器中的内容进行交换。只有当不影响显示的情况,才进行交换
阶段的主要任务是合成 (当前存储于缓冲器中的由之前的像素着色阶段产生的) 片段颜色。此外还负责可见性问题 (Z缓冲相关) 的处理
在光栅化阶段,所有图元会被光栅化,进而转换为屏幕上的像素。首先,计算三角形表面的 差异和三角形表面的其他相关数据(三角形设定阶段),然后,找到哪些采样点或像素在三 角形中(三角形遍历阶段),接着计算所有需逐像素计算操作(像素着色阶段),然后,合 成当前储存于缓冲器中的由之前的像素着色阶段产生的片段颜色,可见性问题可通过 Z 缓存 算法解决,随同的还有可选的 alpha 测试和模版测试(融合阶段)。所有对像依次处理,而 最后的图像显示在屏幕上
像素处理阶段(实时渲染四中,将这部分从光栅化中提出)
- 像素着色 Pixel Shading:
- 所有逐像素的着色计算都在像素着色阶段进行,使用插值得到着色数据作为输入,输出结果为一种或多种将被传送到下一个阶段的颜色信息
- 在可编程GPU内执行
- 主要目的是计算所有需要逐像素操作的过程
- 融合 Merging:
- 每个像素的信息都存储在颜色缓冲器中,而颜色缓冲器是一个颜色的矩阵列。
- 融合阶段的主要任务是合成当前存储于缓冲器中的 由之前的像素着色阶段产生的片段颜色。不像其他着色阶段,通常运行该阶段的GPU子单元并非完全可编程的,但其高度可配置,可支持多种特效
- 负责可见性问题的处理。这意味着当绘制完整场景的时候,颜色缓冲器中应该还包含从相机视点处可以观察到的场景图元。对于大多数图形硬件来说,这个过程是通过Z缓冲(深度缓冲)算法来实现的。Z缓冲算法非常简单,具有O(n)复杂度(n:像素数量),只要每个图元计算出相应的像素值,就可以使用这种方法,大概内容:Z缓冲器和颜色缓冲器形状大小一致,每个像素都存储一个Z值,这个z值是从相机到最近图元之间的距离。每次将一个图元绘制为相应像素时,需要计算像素位置处图元的z值,并和同一像素处z缓冲器内容进行比较。决定是否更新当前图元的颜色值
为什么半透明模型的渲染要使用深度测试而关闭深度写入?
- Alpha通道和颜色缓冲器联系在一起可以存储一个与每个像素相关的不透明值。可选的alpha通道可在深度测试执行前在传入片段上运行。
- 模板缓冲器是用于记录所呈现图元位置的离屏缓存。每个像素通常与占用8个位。图元可使用各种方法渲染到模板缓冲器中,而缓冲器中的内容可以控制颜色缓存和Z缓冲的渲染 (类似遮罩的操作) 模板缓冲器是制作特效的强大工具。而在管线末端的所有这些功能都叫做光栅操作或混合操作
- 帧缓冲器通常包含一个系统所具有的所有缓冲器,但有时也可以认为是颜色缓冲器和Z缓冲器的组合
- 累计缓冲器 对帧缓冲器的补充。可以用一组操作符对图形进行累计。例如,为了产生运动模糊,可以对一系列物体运动的图像进行累计和平均等 (景深、反走样、软阴影)
总结
图形渲染管线的主要功能是在给定虚拟相机、三维物体、光源、照明模式,以及纹理等诸多条件的情况下,生成或绘制一幅二维图像的过程。概念上可以划分为 应用、几何、光栅化阶段
延迟渲染
优缺
与正向渲染对比
伪代码
过程分析
1、几何处理阶段(Geometry Pass)。这个阶段中,我们获取对象的各种几何信息,并将第二步所需的各种数据储存(也就是渲染)到多个G-buffer中;
2、光照处理阶段(Lighting Pass)。在这个pass中,我们只需渲染出一个屏幕大小的二维矩形,使用第一步在G-buffer中存储的数据对此矩阵的每一个片段计算场景的光照;光照计算的过程还是和正向渲染以前一样,只是现在我们需要从对应的G-buffer而不是顶点着色器(和一些uniform变量)那里获取输入变量了
延迟渲染方法一个很大的好处就是能保证在G-buffer中的片段和在屏幕上呈现的像素所包含的片段信息是一样的,因为深度测试已经最终将这里的片段信息作为最顶层的片段。这样保证了对于在光照处理阶段中处理的每一个像素都只处理一次,所以我们能够省下很多无用的渲染调用。除此之外,延迟渲染还允许我们做更多的优化,从而渲染更多的光源。
在几何处理阶段中填充G-buffer非常高效,因为我们直接储存位置,颜色,法线等对象信息到帧缓冲中,这个过程几乎不消耗处理时间。 而在此基础上使用多渲染目标(Multiple Render Targets, MRT)技术,我们可以在一个Pass之内完成所有渲染工作
典型的Deferred Rendering 的渲染流程有两步:
《Real-Time Rendering 3rd》 提炼总结
- 几何处理阶段:渲染所有的几何/颜色数据到G-buffer
- 光照处理阶段:使用G-buffer计算场景的光照
几何缓冲区
主要用于存储每个像素对应的位置(Position),法线(Normal),漫反射颜色(Diffuse Color)以及其他有用材质参数。根据这些信息,就可以在像空间(二维空间)中对每个像素进行光照处理
非真实渲染
轮廓描边
视点方向的描边:
基于视点方向的描边方法,使用视点方向和表面法线之间的点乘结果得到轮廓线信息。轮廓线接近于零,可以判定这个表面极大概率侧向视线方向,可以视作轮廓边缘,进行描边
基于过程集合方法的描边
先渲染正向表面,再渲染背向表面
+ 一种方法是仅仅渲染出背向表面的边界线,使用偏置或者其他技术来确保这些线条恰好位于正向表面之前。这样可以将除轮廓线边缘之外的其他所有线条全部隐藏起来,通常会出现无法连接 独立线段的情况,从而造成明现的缝隙
+ 另一种方法是将背面表面本身渲染成黑色。但没有任何偏置操作,背向表面就会保持不可见,需要通过偏置将这些背向表面沿屏幕Z方向向前移动
基于图形处理的描边
通过在各种缓冲区上执行图像处理技术,来实现非真实渲染的方法。可以将其理解为一种后处理操作。通过寻找相邻Z缓冲数值的不连续性,就可以确定大多数轮廓线的位置。同样,借助邻接表面法线向量的不连续性,可以确定出分界线边缘的位置。
基于轮廓边缘检测的描边
将轮廓边缘理解为朝向相反的相邻三角形的交接。其中一个三角形是朝向视点,零一个三角形背向视点。
混合轮廓描边
结合了图像处理方法和几何要素方法,来渲染轮廓的方法
首先,找到一系列轮廓边缘的列表。其次,渲染出所有物体的三角
形和轮廓边缘,同时为他们指定一个不同的ID值(也就是说,赋予不同的颜色)。接着读取
该ID缓冲器并从中判断出可见的轮廓边缘,随之对这些可见线段进行重叠检测,并将它们连
接起来形成平滑的笔划路径。最后就可以对这些重建起来的路径进行风格化笔划渲染,其
中,这些笔划本身可以用很多方法来进行风格化处理,包括变细、火焰、摆动、淡化等效
果,同时还有深度和距离信息
纹理调色板
使用屏幕空间坐标来采样纹理,为了增强绘制效果,可以在屏幕空间上所有表面上运用纸纹理
色调艺术图
通过在纹理之间进行切换形成的硬着色效果和toon着色效果之间的一种混合嫁接
卡通渲染的具体实现细节
要素
- 锐利的阴影
- 少有或者没有高亮的点
- 对物体轮廓进行描边
渲染加速算法
空间数据结构
空间数据结构的组织通常是层次结构的。
宽泛地说,即最顶层包含它之下的层次,后者又包含更下层的层次,以此类推。
因此,这种结构具有嵌套和递归的特点。
用层次结构的实现方式对访问速度的提升很有帮助,复杂度可以从O(n)提升到O(log n)。
但同时,使用了层次结构的大多数空间数据结构的构造开销都比较大,虽然也可以在实时过程中进行渐进更新,但是通常需要作为一个预处理的过程来完成
裁剪技术
各种层次细节技术
GPU渲染管线与可编程着色器
绿色阶段都是完全可编程
黄色阶段可配置,但不可编程
蓝色的阶段完全固定
合并阶段
作为光栅化阶段名义上的最后一个阶段,合并阶段(The Merging Stage)是将像素着色器中生 成的各个片段的深度和颜色与帧缓冲结合在一起的地方。
这个阶段也就是进行模板缓冲 (Stencil-Buffer)和 Z 缓冲(Z-buffer)操作的地方。最常用于透明处理(Transparency)和合 成操作(Compositing)的颜色混合(Color Blending)操作也是在这个阶段进行的。
虽然合并阶段不可编程,但却是高度可配置的。在合并阶段可以设置颜色混合来执行大量不 同的操作。
最常见的是涉及颜色和 Alpha 值的乘法,加法,和减法的组合。其他操作也是可 能的,比如最大值,最小值以及按位逻辑运算
流输出
GPU 的管线的标准使用方式是发送数据到顶点着色器,然后对所得到的三角形进行光栅化处理,并在 像素着色器中处理它们。
数据总是通过管线传递,无法访问中间结果。流输出的想法在 Shader Model 4.0 中被引入。在顶点着色器(以及可选的几何着色器中)处理顶点之后,除了将数据发送到光栅化 阶段之外,也可以输出到流,也就是一个有序数组中进行处理。
事实上,可以完全关掉光栅化,然后 管线纯粹作为非图形流处理器来使用。以这种方式处理的数据可以通过管线回传,从而允许迭代处 理。如原书的第 10.7 节所述,这种操作特别适用于模拟流动的水或其他粒子特效
几何着色器
顶点和片段着色器之间的可选着色器
随着 2006 年底 DirectX 10 的发布被加入到硬件加速图形管线中。几何着色器作为 Shader Model 4.0 的一部分,不能在早期着 色模型(<= SM 3.0)中使用
几何着色器的输入是单个对象及对象相关的顶点,而对象通常是网格中的三角形,线段或简单的点。 另外,扩展的图元可以由几何着色器定义和处理
几何着色器需要图元作为输入,在处理过程中他可以将这个图元整个丢弃或者输出一个或更多的图元 (也就是说它可以产生比它得到的更多或更少的顶点)。这个能力被叫做几何增长(growing geometry)。如上所述,几何着色器输出的形式只能是点,折线和三角形条
顶点着色器
顶点着色器属于第一个阶段,可选是在GPU还是CPU上实现。CPU上实现的话,需要将输出数据发送到GPU进行光栅化。目前几乎所有的GPU都支持顶点着色
顶点着色阶段之前发生了一些数据操作。比如在 DirectX 中叫做输入装配 (Input Assembler)的阶段,会将一些数据流组织在一起,以形成顶点和基元的集合,发送到管线
传入的每个顶点由顶点着色器程序处理,然后输出一些在三角形或直线上进行插值后获得的值。 顶点着色器既不能创建也不能消除顶点,并且由一个顶点生成的结果不能传递到另一个顶点。由于每 个顶点都被独立处理,所以 GPU 上的任何数量的着色器处理器都可以并行地应用到传入的顶点流 上
可编程着色模型
在现代 GPU 上 ,图形运算中常见的运算操作执行速度非常快。通常情况下,最快的操作是标量和向 量的乘法和加法,以及他们的组合,如乘加运算(multiply-add)和点乘 (dot-product)运算。其他 操作,比如倒数(reciprocal), 平方根(square root),正弦(sine),余弦(cosine),指数 (exponentiation)、对数(logarithm)运算,往往会稍微更加昂贵,但依然相当快捷。
纹理操作非常高效,但他们的性能可能受到诸如等待检索结果的时间等因素的限制
流控制(flow control)是指使用分支指令来改变代码执行流程的操作。这些指令用于实现高级语言结 构,如“if”和“case”语句,以及各种类型的循环。
Shader 支持两种类型的流控制。
静态流控制 (Static flow control)是基于统一输入的值的。这意味着代码的流在调用时是恒定的。静态流控制的主要好处是允许在不同的情况下使用相同的着色器(例如,不同数量的光源)。
动态流控制(Dynamic flow control)基于不同的输入值。但动态流控制远比静态流量控制更强大但同时也需更高的开销,特别是在调用 shader 之间,代码流不规律改变的时候
评估一个 shader 的性 能,是评估其在一段时间内处理顶点或像素的个数。如果流对某些元素选择“if”分支,而对其他元 素选择“else”分支,这两个分支必须对所有元素进行评估(并且每个元素的未使用分支将被丢弃)
Shader 程序可以在程序加载或运行时离线编译。和任何编译器一样,有生成不同输出文件和使用不同 优化级别的选项。一个编译过的 Shader 作为字符串或者文本来存储,并通过驱动程序传递给 GPU
概述
GPU 实现了第二章中描述的几何和光栅化概念管线阶段。其被分为一些不同程度的可配置性和可编程性的硬件阶段
顶点着色器:
完全可编程,顶点着色器可以对每个顶点进行诸如变换和 变形在内的很多操作,提供了修改/创建/忽略顶点相关属性的功能,这些顶点属性包括颜色、法线、 纹理坐标和位置。顶点着色器的必须完成的任务是将顶点从模型空间转换到齐次裁剪空间
几何着色器:
允许 GPU 高效地创建和销毁几何图元。几 何着色器是可选的,完全可编程的阶段,主要对图元(点、线、三角形)的顶点进行操作。几何着色 器接收顶点着色器的输出作为输入,通过高效的几何运算,将数据输出,数据随后经过几何阶段和光 栅化阶段的其他处理后,会发送给片段着色器
裁剪:
属于可配置的功能阶段,在此阶段可选运行的裁剪方式,以及添加自定义的裁剪 面
屏幕映射(Screen Mapping)、三角形设置(Triangle Setup)和三角形遍历(Triangle Traversal)阶段是 固定功能阶段
像素着色器:
是完全可编程的阶段,主要作用是进行像素的处理,让复杂的着色方程 在每一个像素上执行
合并阶段:
处于完全可编程和固定功能之间,尽管不能编程,但是高度可配置,可 以进行一系列的操作。其除了进行合并操作,还分管颜色修改(Color Modifying),Z 缓冲(Zbuffer),混合(Blend),模板(Stencil)和相关缓存的处理
图形渲染与视觉外观
抗锯齿
AA:边缘柔化、消除混叠、抗图像折叠有损,反走样灯
高分辨率抗锯齿 HRAA
高分辨率抗锯齿方法(High Resolution Anti-Aliasing,简称 HRAA),也称 Quincunx 方法,也出 自 NVIDIA 公司。
“Quincunx”意思是 5 个物体的排列方式,其中 4 个在正方形角上,第五个 在正方形中心,也就是梅花形,很像六边模型上的五点图案模式。此方法中,采样模式是五点梅花状,其中四个样本在像素单元的角上,最后一个在中心
覆盖采样抗锯齿 CSAA
CSAA 就是在 MSAA 基础上更进一步的节省显存使用量及带宽,简单说 CSAA 就是将边缘多边形里需要取样的子像素坐标覆盖掉,把原像素坐标强制安置在硬件和驱动程序预先算好的坐标中。
这就好比取样标准统一的 MSAA,能够最高效率的执行边缘取样,效能提升非常的显著。比方说 16xCSAA 取样性能下降幅度仅比 4xMSAA 略高一点,处理效果却几乎和 8xMSAA 一样。8xCSAA 有着 4xMSAA 的处理效果,性能消耗却和 2xMSAA 相同
多重采样抗锯齿 MSAA
多重采样抗锯齿(Multi Sampling Anti-Aliasing,简称 MSAA),是一种特殊的超级采样抗锯齿 (SSAA)。
MSAA 首先来自于 OpenGL。具体是 MSAA 只对 Z 缓存(Z-Buffer)和模板缓存 (Stencil Buffer)中的数据进行超级采样抗锯齿的处理。
可以简单理解为只对多边形的边缘进行 抗锯齿处理。这样的话,相比 SSAA 对画面中所有数据进行处理,MSAA 对资源的消耗需求 大大减弱,不过在画质上可能稍有不如 SSAA
超级采样抗锯齿 SSAA
先把图像映射到缓存并把它放大,再用超级采样把放大后的像素进行采样,一般选取2个或4个邻近像素,把这些采样混合起来后,生成的最终像素
令每个像素拥有邻近像素的特诊,像素与像素之间的过渡色彩,就变得近似,令图形的边缘色彩过渡趋于平滑。
再把最终像素还原回原来大小的图像,并保存到帧缓存也就是显存中
这样就等于 把一幅模糊的大图,通过细腻化后再缩小成清晰的小图。如果每帧都进行抗锯齿处理,游戏 或视频中的所有画面都带有抗锯齿效果。 超级采样抗锯齿中使用的采样法一般有两种:
- OGSS,顺序栅格超级采样(Ordered Grid Super-Sampling,简称 OGSS),采样时选取 2 个邻近像素
- RGSS,旋转栅格超级采样(Rotated Grid Super-Sampling,简称 RGSS),采样时选取 4 个邻近像素
作为概念上最简单的一种超采样方法,全场景抗锯齿(Full-Scene Antialiasing,FSAA) 以较高的分辨率对场景进行绘制,然后对相邻的采样样本进行平均,从而生成一幅新的图像
曲面细分和三角形划分
细长三角形的光栅化效率很低,因为在对远处顶点进行插值时,可能会造成瑕疵
多边形划分为三角形,(解决算法只能用于凸三角形的情况)
- 耳切法,时间复杂度O(n^2)
着色
漫反射分量:
镜面反射分量:
着色处理方法:
- 平滑着色:
§ 一个三角面用同一个颜色。如果三角面的代表顶点恰好被光照成了白色,那么整个面都是白色
- 高洛德着色:
§ 每顶点求值后的线性插值结果通常成为高洛德着色。顶点着色器传递世界空间的顶点法线和位置到Shade()函数,然后将结果写入内插值。像素着色器将获取内插值并将其直接写入输出。可以为无光泽表面产生合理的结果,但是对于强高光反射的表面,可能会产生失真
- 冯氏着色:
§ 对着色方程进行完全的像素求值。顶点着色器将世界空间法线和位置写入内插值,此值通过像素着色器传递给Shade()函数.而将Shade()函数返回值写入到输出中。即使表面法线在顶点着色器中缩放为长度1,插值也可以改变其长度,因此可能需要在像素着色器中再次执行此归一化操作
冯氏着色可以说是三者中最接近真实的着色效果,当然开销也是最大的。因为高洛德着色是每个顶点(vertex)计算一次光照,冯氏着色是每个片元(fragment)或者说每像素计算一次光照,点的法向量是通过顶点的法向量插值得到的。所以说不会出现高洛德着色也许会遇到的失真问题