着色管线

光栅化技术:

场景渲染过程:

  1. 放置一个虚拟摄像机在3D场景中的某个位置,并设置一个视锥体来表示3D世界中的可视区域,这片区域被映射到屏幕上一块2D的具有一定分辨率的窗口。
  2. 场景中每个物体锁包含的顶点数据以及绘制该物体所需要的所有环境贴图,阴影等被提交到Opengl开始绘制该物体,这些顶点所构成的图元被视锥体裁剪,处于视锥体外部的图元将被丢弃,与视锥体香蕉的图元将被裁剪,剩下的图元被光栅化到与窗口分辨率对应的像素位置,每个像素块称为一个片元。
  3. 片元着色器对每个片元使用一个着色运算公司进行计算(关于多个光源的辐射照度,入射光方向矢量,观察方向矢量,法线矢量,表面的漫反射折射率和高光反射折射率,高光模型中的高光扩散系数或者粗糙度的和“利用蒙特卡洛原理”得到的颜色值),并最终结果与帧缓存上对应像素位置上的深度值进行比较,如果通过深度测试则将该颜色值与帧缓存上的颜色进行混合。
  4. 当所有物体被遍历完后帧缓存上的颜色缓存被送到显示设备显示,或者其结果被读回到宿主程序。

 

优点:

结合图形接口深度测试和颜色混合的机制,传统的渲染管线可以很容易的实现对半透明物体的绘制。

MSAA被集成到渲染管线,它可以对每个片元的深度进行多次采样,而使用一次着色计算以实现反锯齿。

每个物体可以根据其图形特征使用独立的着色器或着色器组合。

 

缺点是:

过度绘制(overdraw)

过多的光源,导致同一个物体渲染多次,每次都要走完整管线流程。需要绘制之前对物体与光源执行包围盒比较。

 

所以就有了针对光源剔除和overdraw的解决方案,基于2D分块和3D分簇的延迟渲染技术。

 

延迟着色:

将着色计算延迟到深度测试之后,我们旧避免因为被深度测试丢弃片元产生的不必要的着色计算,这就是延迟着色(deferred shading)技术的原理。与之相反的就是前项着色。

每个深度测试通过的像素使用额外的方式记录下所有这些着色参数,然后使用一个单独的渲染通道来仅对这些可视的像素点进行着色计算,这个缓存对象称为几何缓存(g-buffer),当代图形处理器接口提供的MRT可以进行存储。

 

步骤:

  1. 选址所有不透明几何体,每个片元对应的法线矢量,漫反射折射率,以及高光扩散系数等存储到G-buffer中,需要输出多个颜色值,所以需要MRT特性,这是前向着色阶段。
  2. 分别计算每个光源对可见表面点的影响,此时我们应该关闭深度测试,因为我们只需要找到该光源影响的屏幕上的区域,同时每个光源包围几何体有两个面,我们应该根据情况绘制光源包围几何体的其中一面。前向着色阶段输出的G-buffer将作为纹理数据被读入,G-buffer中的深度值用来计算像素的3D位置,以此用来计算光源的距离递减函数以及查询阴影贴图。最后该光源对每个像素点的颜色计算结果被写入到一个累计缓存(accumulate buffer)。此过程称为延迟着色阶段。
  3. 按传统的渲染管线绘制所有半透明的物体。

 

牺牲内存解决过度绘制的问题。

 

延迟渲染的问题:

  1. 不支持半透明物体。由于g-buffer只保存每个像素点的一个表面电的值,所以它不能支持半透明物体。
  2. 巨大的帧缓存存储占用。G-buffer每个像素可以占用多达128bits甚至以上的内存占用,用多重采样就会占用巨大的内存。
  3. 对屏幕区域的像素点,而不是根据每个物体自身的类型进行着色计算,这使得我们很难针对每种物体使用自定义的着色器,因为各种类型的物体被混在一个屏幕区域,我们必须使用统一的着色器。
  4. 内存访问的高带宽占用。  

解决带宽的办法有:

  1. 光源分配
  2. 减少光源循环中读取数据的数量,即进一步将着色计算中的光照计算分离出来

 

延迟光照计算

渲染方程一般拆分成漫反射和高光反射,我们可以把漫反射率和高光反射率从着色方程中抽离出来,使得与光源相关的计算部分只剩下法线和高光扩展系数,而光源入射方向是可以从法线方向计算出来的,因此,我们可以只需要在g-buffer中存储法线和高光扩展系数,法线是一个rgb颜色值,高光扩散系数通常是一个很小的整数,因此整个g-buffer只需要一个32位的颜色缓存而不需要之前说的128位甚至更多的数据,这种情况下甚至不需要MRT。

所以延迟渲染可以简述为:

  1. 渲染场景中所有不透明的几何体,将法线矢量n和高光扩展系数m写入到帧缓存中。只需要一个4分量的颜色缓存即可。
  2. 逐个渲染每个光源包围盒所在的几何体,并对该光源覆盖的每个屏幕像素点进行光照计算;和延迟着色不一样的是,它只计算辐射照度而不是辐射亮度,并且最终输出的漫反射和高光反射的辐射照度被写入到两个累计缓存中。此阶段为延迟光照计算阶段。
  3. 重新渲染所有不透明几何体,但是此时并不需要对表面进行光照计算,它仅仅需要从前一个阶段的两个颜色累计缓存中读取值执行计算。并将最终的着色结果写入到帧缓存中。此阶段为第二次几何通道。
  4.  绘制所有半透明物体。

 

原始的6通道的颜色缓存能够准确反应反射物体的颜色,它的边沿上呈现红绿两种高光,而4通道的方案失去了反射环境的颜色,所以他仅仅保留了高光的亮度,而丢弃了高光的色度。

但是人眼对高光亮度更为敏感,所以一般不会有问题。

 

光源分配:

降低贷款占用的另一个方法是改变传统延迟着色中的循环结构,使像素的遍历处于外循环,而光源的遍历处于内循环。

 

分块着色:

让一个区域内相邻的表面点共享一个光源列表,这样做的原因是相邻的表面电一般拥有相同的光源列表。

分块着色将屏幕区分划分为由多个块组成的网格,每个块覆盖的像素区域为32x32,每个块有一个独立的光源列表,它表示该区域内所有像素点受影响的光源列表,块内所有像素点共享整个光源列表。

 

所有块存储为一个2D纹理,每个像素点通过屏幕区域的像素位置来计算块索引,每个块存储两个数据,偏移和尺寸。所有块的光源列表存储在一个大的全局光源索引列表中,每个块通过在全局光源索引列表中的偏移值和尺寸来选择该块对应的光源列表。

最简单的方法是将每个光源的包围盒矩形投射到屏幕上,然后在么给块中分别插入受影响的光源索引到全局光源索引列表中,并修改数据中的偏移和索引值。

但是它在一些块中插入了无效的光源,如点光源四个角的位置,表面点在垂直于屏幕方向上的距离光源位置很远,从而根本不会受到该光源影响,但是它同样处于该光源投射的区域。可以使用模板测试,类似阴影体积方法来精确的关联光源与其影响的块。也可以给表面点设定一个最近和最远受光源影响的距离。

块数据的建立可以发生在GPU中,也可以在CPU中处理。GPU处理块的方式是在SPU中执行块的数据处理,而且也做着色计算,这样可以减少一个块内光源列表的重复读取。

光源分配其实适用于前向着色和延迟着色。

光源分块的三个阶段:

  1. 对场景(不透明的物体)执行一次无光照的传统的渲染管线,以确定哪些像素是可见的,这通常就是延迟着色的前向着色阶段。
  2. 利用某种方法对上述可视的表面点执行光源分配,它输出一个针对每个可视表面点的光源列表。
  3. 利用光源列表执行每个可视表面点的光照计算。

 

分块着色还是存在很大的优化空间,由于分块着色将3D空间投射到2D的屏幕区域,因此它的每个块占据了深度方向上非常大的范围,如下图

分块1两个物体之间存在深度不连续,我们无法用简单的分块范围来剔除包含在中间的光源,。由于深度范围太多,使得该分块必须包含该深度范围内的所有光源。

使用2D分块的另一个问题是这些分块信息是与视点相关的,即一旦摄像机位置和方向发生移动,或者场景中某些物体位置发生改变,则整个分块表格的数据需要重新计算。

 

 

分簇着色:

分簇着色在分块着色的基础上将像素分组的划分从2D的屏幕空间延伸到3D的观察空间,每个3D的块称为一个簇,从而使每个光源真正做到仅影响其局部区域。

分簇着色步骤:

  1. 将几何场景渲染到g-buffer以获取有效可视表面点以及相关材质属性。
  2. 计算每个簇的索引键值,也称为簇分配。
  3. 找出唯一的簇集合。
  4. 分配光源到每个簇。
  5. 利用每个簇的光源列表对像素点进行着色计算。

 

簇分配:

传统的几何空间的划分可以使用如BVH这样的树状结构来忽略大部分空域,但是分簇着色使用的是一种特殊的子空间结构,并不能用简单的树状结构进行管理。它对每个可视像素点都做一个簇索引键值的计算。

分簇着色技术对视锥体进行划分,它以分块着色中2D屏幕上块的划分为基础,然后在深度方向上添加了一个细节维度。每个子空间称为一个簇。以指数的形式划分深度方向,更远的地方簇的尺寸更大。每个簇的尺寸更接近一个立方体。

所以给定一个像素点的坐标,我们便可以计算出像素点在簇的索引值,这个索引键值由(i,j,k)表示,i,j,分别占用8位,k占用10位。总共26位表示一个簇索引键值。

也可以通过为一个簇指定该簇内像素点的法线分布,从而可以有效的进行背面剔除。为此,需要增加6位用来表示法线分布,这样簇的索引键值变为32位。

如果入射光方向与法线锥形的中心轴之间的夹角大于π2+α+δ  ,则该光源应该被剔除。α是法线锥形的半角,δ是从光源发出的包围簇的锥形的半角。 

 

找出唯一的簇集合:

这时计算出来的是一个重复键值的簇索引键值列表,我们需要去除重复的部分,形成一个包含唯一簇索引键值的簇列表。由于2D屏幕空间相邻像素点所在的簇在3D观察空间上并不一定是连续的,所以这些像素点计算出的簇索引键值并不一定是连续的。最简单的方法就是首先对索引键值列表进行排序,然后去除掉一段重复的索引键值。

但是在gpu中进行排序是一件非常影响性能的事情,首先以2D屏幕空间的额块为单元进行本地排序,并且以32x32大小的像素分块,所以可以充分利用gpu的并行计算性能,每个块内的数据可以写入到本地共享缓存,而不是全局内存进行计算。

另一个方法是使用虚拟纹理,虚拟纹理用来存储需要巨大内存的稀疏数据它可以将一个地址映射到一片紧密的内存区,这样用来降低内存的占用,虚拟内存使用页表来映射索引到紧密的物理内存区。一般用动态分配页表的方法来节省更多的空间。

 

分配光源到簇:

【Engel,2016】中针对DirectX12的基于保守光栅化技术的光源分配技术,这是一种利用图形处理器的光栅化技术能够产生非常精确的簇光源分配的方法,这种方法仅适用于凸面光源形状,考虑到大多数光源如点光源和聚光灯光源的包围体都是凸面的,因此这种方法非常实用。

传统光栅化,再选择每个图元所覆盖的片元时,是以该图元所占的面积是否覆盖该片元的中心位置来决定该图元是否覆盖到该片元,这种方法会漏掉一部分光源对簇的影响。保守光栅化则就爱侣任何部分被图元面积占用的片元均为有效片元。

保守广泛光栅化簇光源分配方法的基本过程可以分为两步,这两步均针对每个光源类型为单位进行:

  1. 壳通道:此通道首先将每个光源的包围几何体利用光栅化技术渲染到2D屏幕上以块为基础单元的分辨率上,并记下每个块上的最大和最小深度值,。
  2. 填充通道:利用壳通道产生的最大最小深度值来填充簇的光源列表。

这里仅对每个类型的光源存储一个几何网格数据,并且每个顶点数据在x,y,z方向上分布被限制到-1到1的单位尺寸。保守光栅化光源分配方法中,每个通道都是以一个光源类型为单位的。

 

壳通道:

壳通道的主要任务是找出每个光源在屏幕上每个块内包围簇范围最大最小的深度。只要找出这两个值,则在后面的填充听到则很容易正确的分配每个光源到每个簇内。

由于每个通道是以光源类型为单位的,每个光源类型包含多个光源的实例,所以要求渲染目标是一个Textrue2DArray类型的数组纹理,其数组的数量为每个类型光源对应的光源类型,数组中每个图像的分辨率等于2D屏幕上块的分辨率。

 

片元着色器的每个实例对应于屏幕区域一个块,每个块实际上是一个以摄像机为原点,四个面分别穿过块四边的锥形。一单一个片元着色器实例被制定,说明三角形的至少一部分和该块相交,所以在片元着色器中每个三角形必须与该块对应的锥形进行相关计算。

 

填充通道:

同样以每个光源类型为单位,以块为分辨率,分别根据每个块内每个光源所占的最大最小深度值对其他介于最大最小深度之间的簇进行填充。

 

着色计算:

出簇着色和分块着色方法在着色计算方面美原油太多区别,它同样可以使用于前向或延迟渲染方法中。但由于大大减少了每个簇内光源额数量,因此渲染性能较分块着色得到很大提升。

我们可以看到分簇着色每个像素计算的光照数量非常低,从而能够轻松应付巨大的光源数量,因为不管光源数量多么巨大,分配到每个簇内的光源数量几乎没有太多变化。

 

分簇着色最重大的意义在于,通过这种光源的局部性特征,将场景复杂度完全从光源数量中解脱出来,从而不管场景变得多么复杂,它能时钟提供一个稳定的帧率,这是实时渲染技术最重要的权衡指标。

 

着色器管理:

延迟渲染中,每一次渲染是以屏幕上的一块区域为单位进行着色计算的,这块区域内的像素点可能分别来自不同类型的几何体,它们可能分别具有不同类型的材质参数,所以使用延迟着色,另一个挑战是怎样管理好各种类型物体的着色计算。大量游戏场景中的物体表面抗原用BRDF/BSDF模型来做,剩下仅有一些特殊的物体如毛发,皮肤等使用法国其他特殊的材质模型。

多种类型的几何体混在一起使用相同的着色器,就避免不了分支的处理。顽皮狗工作室在神秘海域4中使用的管理着色器方案如下:

g-buffer必须支持所有类型的材质参数,所以这就可能导致巨大的g-buffer,因为其中一些材质参数对另一些类型的物体并不适用。他们做的优化是:

首先对于特殊的材质,使用相互独立的材质参数,两个基本的g-buffer用来存储所有物体都需要的材质参数,如发现,位置,粗糙度等。第三个可选的g-buffer用来存储每种类型特殊的一届材质参数,包括织物,头发,皮肤以及丝绸等,这个特殊的g-buffer对每个物体只使用一种类型,例如一个表面不可能同时包含织物和皮肤的材质参数,这是由材质创作管线限制的。

其次:充分利用GCN架构内置的位解码方法来充分压缩每个参数的长度,每个g-buffer中每个像素仅占用16位的存储空间。GCN架构提供的解码方法可以对任何证书按位进行解码,这使得我们可以在帧缓存中存储任何长度的数据,并且这个操作可以在一个GPU时钟周期内完成。例如以下代码从一个整数中分别获取一个1,5,和11位的颜色分量值。

Int r = s & 0x1F;           //1 cycle

Int g = (s >> 5) & 0x3F; //1 cycle

Int b = (s >> 11) & 0x1F; //1 cycle

 

神秘海域4中的延迟着色阶段是使用计算着色器以块为单位进行渲染的,其中每个块包含16x16个像素点。分两步处理像素着色计算:

  1. 对每个块执行材质类型定义。
  2. 对每个材质类型分别执行一次计算着色器。

材质定义阶段的额目的是找出每个块使用的计算着色器类型。他们对每个不同材质类型的组合建立一个单独的计算着色器通道,所有可能得材质类型组合存储在一个查询表中,这样每个块通过其材质组合的索引值就能找到对应的计算着色器。第二个阶段对每个材质类型组合使用一个独立的计算着色器,仅包含少量甚至无分支计算。

他们会有个材质ID的组合。根据材质ID返回表示一个独立的计算着色器的索引值。

由于一个块包含16x16像素点,所以块内每个像素仍然可以有不同材质,也就有分支处理。他们会用一个无分支排列组合的查询表,也就是让相同分支的在一个块。

神秘海域4的着色器管理技术可以提升20%-30%的性能。

 

 

延迟着色中的反走样技术:

走样和反走样的基础概念:一个连续信号在被采样为离散信号时,只有在其对应的频率域上,使用能够覆盖2倍以以上的频率带宽的采用率时,离散信号才能被完美重建为原始连续信号,否则重建后的原始信号将会发生走样,由于一些高频信号被“削平”了,许多原本不同的信号看起来像相同的信号。由于集合物体的边缘周围的颜色频率域变化非常大,基本上一般的屏幕分辨率根本无法捕捉这些频率域的变化范围,所以走样几乎不可避免。

图形学中主要有两大类走样类型:其一是由于对空间与的信号采样不足导致走样,这些称为空间走样,包括几何走样,着色走样。另一种走样是由于频率的限制,导致对相邻时间内信号的采样不足导致的走样,称为时间走样。

其中一种对空间走样的黄金标准是全屏超采样技术,直接使用更高的分辨率对场景进行渲染,然后对这种高分辨率的渲染结果进行过滤以“缩放到”屏幕分辨率,这是超采样反走样(SSAA)货全屏反走样(FSAA)

多重采样反走样技术(MSAA)是将几何可见性和着色计算分开,它对几何可见性函数的采样使用类似超采样一样更高的分辨率进行采样,但是每个像素点内只做一次着色计算,并使用可见性的覆盖率作为比率来混合当前的颜色值。他不能处理着色走样。

MSAA不适用于延迟着色,因为它会自动将片元着色器输出的结果拷贝多次,这使得本身就非常大的g-buffer更要使用多几倍的存储空间,这又大大增加了延迟着色计算中的带宽占用。

 

形态反走样

上图中A的SSAA方式处理子像素信息,他对每个子像素的颜色输出。而B是MSAA的思路,是对像素下的子像素做平均值计算覆盖率。通常人眼对于物体形状以及颜色变化最敏感,人在渲染结果中这种变化通常来源于几何体的轮廓,要么是深度不连续或者是不同物体之间颜色不连续,所以即使不具备子像素特征,MSAA在游戏中运用仍然非常广泛,但他不适用于延迟渲染。

形态反走样(MLAA)的核心算法是找出物体的轮廓信息,并利用这些轮廓信息计算出处于轮廓上像素的覆盖率,然后使用一个后期处理阶段对轮廓上的像素与相邻像素的颜色进行混合。

  1. 根据一定的像素属性(如颜色,深度,法线,几何体ID等)找出图像中不连续的像素,并标记处这些像素中的哪些边处于轮廓边缘,这称为边缘检测。
  2. 利用这些边缘线段的几何特征计算每个像素与周围邻近用来进行混合的像素的权重值。
  3. 对周围邻近的像素按照混合权重进行混合计算求出轮廓上像素的颜色值。

这些边缘线段可以被划分为三种类型:L,Z,U。每种类型的交叉边缘只包含一个像素的长度,非交叉边缘可以具有任意长度,直接连接每个类型的交叉边缘的中点就构成一跳轮廓线。

当轮廓线段被连接起来后,它们将轮廓上的像素分割成两个梯形,其每个梯形的面积就代表了该像素与附近像素的颜色用来进行混合计算的权重。我们计算每个梯形的面积是用边缘线段中的长度D-left+D-right以及宽度(一个像素的大小)。C-old轮廓上像素原来的颜色,C-opp代表邻近像素的颜色,a代表轮廓上的像素的混合权重,则该轮廓上像素的新的颜色值为:

C-new=(1-a)*c-old+a*c-opp

MLAA各个阶段的视觉效果:

在不需要多重采样或超采样的基础下,几乎可以达到4倍于MSAA的效果。并且MLAA是一种后处理算法,可以灵活加入任何渲染器。包括SRAA和FXAA都是MLAA的一种。他的缺点是不能处理子像素级别的特征,由于采样不足,高光和着色走样也不容易处理。它几乎忽略掉了对角线的轮廓,因为对角线轮廓需要考虑更多像素,MLAA仅考虑轮廓上的像素混合。

 

子像素形态反走样:

SMAA是MLAA的增强算法,SMAA已经在CryEngine3中实现了。SMAA算法包含三个渲染通道(pass)

第一通道:首先边缘被检测出来,存储在一个边缘纹理中,纹理中颜色表示每个边缘在像素中的位置(上下左右),绿色是顶部,红色在左边,黄色表示在以上两个方向拥有边缘。在此通道,边缘像素的模板值被记录,以便后续的通道仅处理轮廓上的像素。

第二通道:计算每个轮廓上像素的混合权重。找出每条经过该像素左边和顶部边缘线段的模式,计算出该像素距离这些模式线段交叉边缘的距离,两个距离值作为纹理坐标到一个预计算纹理中进行采样,这个预计算的纹理是根据线段模式计算出来的权重分部值。

第三通道:根据上一步的权重值从周围的四个像素中取出颜色值进行混合计算。

SMAA算法较大特点是充分利用了GPU纹理过滤的功能以大大减少一些迭代。

边缘检测:

SMAA选择使用亮度值来进行边缘检测L=0.2126*R+0.7152*G+0.0722*B

对每个像素比较它与左边和上边的像素的亮度值,两个相邻像素之间的亮度差绝对值要大于一个阈值,比较的结果是一个枚举值,纹理的2通道RG为分别对应上边和左边是否处于边缘。但是相邻多个线段的渐进变化很容易被拆分成多个独立的线段模型,像下面这张图上中小图一样,但是我们希望得到的是上右小图的结果。

SMAA采用一种适用性双阈值的策略来克服这个问题如上图的下左所示,灰色点表示当前处理的像素点,黄色表示当前像素点可能的左边边缘,蓝色表示相邻像素的边缘。首先计算出所有蓝色边缘中亮度差之并取最大值C-max,然后根据C-2l>0.5*C-max是否为true决定左边缘C-l是否存在,用同样的思路计算上边缘。但实际上因为涉及大量的内存和读取,只会选择一部分执行,如上图右下。

 

计算权重值:

混合权重包括三个步骤:首先找出没饿像素中心距离其所在形状两端的距离,以及其中的d-left和d-right距离,找出该像素所在形状的交叉边缘,并连接两个交叉边缘中点构成一条轮廓线,最后根据这条轮廓线计算该像素的混合权重。

SMAA第一个通道输出的是一种包含边缘信息的纹理,纹理每个像素要么是1要么是0表示边缘或非边缘。找边缘就是找到像素值为0的为止。

SMAA利用硬件支持的纹理过滤功能,每次遍历向前步进两个像素单元。

每两个相邻像素重叠的为止(菱形的位置)对边缘纹理进行采样,使用双线性差值的过滤方式得到三种结果:

0:表示两个像素均不包含边缘。

0.5:其中一个像素包含边缘。

1.0:两个像素均包含边缘。

0,5时就停止步进,表示下一个像素不包含边缘信息。

由于它每次迭代仅在相邻两个像素的左边缘进行比较,容易忽略掉轮廓形状另一边的左边缘。

我们不仅需要对像素步进方向的像素左边缘进行比较,还需要对边缘形状另一边的左边缘进行比较。可以利用双线性插值来减少纹理读取,单需要移动位置使他采样上下左右的值。

当找到当前变迁形状两边的结束位置之后,我们需要找到具体的交叉边缘的为止,同样使用和距离计算一样的双线性插值可以避免多次纹理读取。为了避免条件判断,但做法是直接将它们的返回值编码为一个特定的值,然后用这些值代表的顺序生成后面使用的面积纹理。

这里对像素使用一个(0.0,-0.25)的偏移后再对边缘纹理进行双线性插值采样,四个值乘以四就称为0,1,3,4。就可以作为索引值了,不需要在着色器中做条件判断。

接下来利用这些值来计算边缘像素的覆盖面积。首先将交叉边缘的重点连接起来,然后计算每个像素内的梯形面积。保存在面积文理中。

面积纹理是由25个9x9的子纹理组成的纹面,每个子纹理表示一个边缘形状类型,每个形状类型可以由2个0到4的索引值查询,就是前面的索引值。

 

时间反走样:

SSAA的每个自采样点通常是不重合的,因此如果我们能将这些自采样点的空间分布,分散地分布到各个时间帧中,技能实现和SSAA几乎一样的效果。这就是时间反走样的制作原因TAA。

静态场景:

要使用TAA技术,在不同的帧当中相同的像素应该被使用一个抖动操作,以使当前帧的像素被移动到一个超采样中自采样点的位置。抖动点通常使用某种随机分布函数产生,随机分布应该具有低差异性,子采样点分布尽量铺满整个像素区域,ue4是用halton数列来产生子采样点的分布。

抖动操作发生在顶点着色器中,修改投影矩阵将采样点修正到偏移位置。

通过抖动,产生了和SSAA一样的空间域上的多个子采样点,然后子采样点进行过滤,最简单就是加权平均。

但这种方式会耗费巨大的内存,递归指数过滤器使用当前渲染的结果和之前所有帧混合的经过反走样处理的历史结果进行混合。这在主流游戏引擎大量使用。

TAA可以在延迟着色计算之后,对高动态范围HDR的图像进行时间反走样处理,然后将时间反走样之后的HDR图像传给渲染管线的后续阶段,例如色调映射及后处理阶段。

 

动态场景:

当场景是动态时,大量的几何体发生变化,这时TAA变得复杂。

动态场景的三个问题:

首先需要一个额外的屏幕区域大小的缓存来存储每个历史颜色的位置。存在G-buffer中。

其次需要在上一帧图像的离散的屏幕分辨率中找出一个位置来对历史颜色缓存进行采样,涉及重采样的问题,所以可能会变得模糊。

再次同一个位置的像素除了采样点位置外可能发生了其他变化,例如光源遮挡关系,像素颜色值发生变化,甚至像素本身是由着色器中程序产生的几何体,这样变化可能导致重影的瑕疵。

 

重投影:

我们需要找出当前帧中每个像素在上一帧当中的位置,通过重投影来实现。

重投影的概念是发生在发现一个事实,相邻两帧中大部分像素的颜色值是连贯的,持续跟踪这些没有发生太多变化的像素,缓存起来下一帧可以不用再计算该像素颜色因此要实现的实际是缓存系统。但是它也有模糊的问题。

为了计算当前像素的历史颜色缓存位置,需要计算出每个像素在上一帧相对于当前帧的偏移位置,这叫移动矢量。用额外的颜色缓存将偏移矢量直接输出到g-buffer中。在几何通道中输入:上一帧的摄像机信息和上一帧每个物体的本地到世界坐标的变化矩阵,输出相邻两帧的顶点坐标到片元着色器。

这些顶点坐标被光栅化处理后,就可以在片元着色器中计算每个像素的移动矢量。但这里不能用抖动的坐标,要用每个像素中心店位置的颜色值,那些抖动的子像素位置仅是用来进行不同的采样。

由于光栅化计算出的像素坐标是一个像素的中点,当摄像机或物体移动时,同样的点光栅化后的坐标并不是完全一样的,它可能存在小宇一个像素内的偏移,有时在不相关的位置,所以需要过滤处理,过滤会变得模糊,所以需要解决模糊问题。

 

模糊处理:

TAA的模糊来源是每次迭代对历史颜色纹理的采样使用双线性过滤,而且随着时间的增加,参与加权的像素的范围就越来越大。

式中σg2  是一个常数项,v表示像素中心距离每个子采样点额差值。影响重投影模糊有两个因素。第一减少每个子采样点到像素中心店额距离,有效的方法是提高分辨率,每个像素划分为4个子像素,不需要每一帧渲染4次,将四个像素独立存储为一个缓存对象,每帧更新一个子像素,然后合并。

另一个是混合因子a,ue4是当像素附近的颜色对比度比较低时增加混合因子,而对比度高时(薄面,边缘等频率变化比较高的区域)减少混合因子。

为了进一步减小模糊,可以使用一个单独的通道以后处理的方式对颜色缓存进行锐化处理,对颜色缓存使用一个锐化过滤器来实现

 

重影:

 

重影问题是TAA最著名的问题

重影的原因是由于像素的颜色值发生了改变,不再合理的颜色还是混合进当前帧的输出图像中,最终形成一个拖尾的效应。

这些改变原因可以分为两类:第一类是整个像素的值发生了变化,例如由原来的可见变为不可见,这个时候历史上可见的颜色值就会被混合进不可见的区域,,形成比较强烈的重影。第二类是一个像素内的局部颜色发生了改变,即某些子采样点的颜色发生了变化,一些地方在一个像素尺寸内的频率域变化很大(如一些超薄表面,草地等)使得某些子像素特性无法被渲染而被历史颜色所混合。崎岖不平的路面中具有频率变化范围较大的法线的范围,仍然被人物头像部分的历史颜色所混合。

为了排除这些失效的历史颜色,TAA需要在混合时判断历史颜色是否失效。

领域裁剪基于一个假设:即图像中的颜色是连续的,历史颜色缓存中的颜色应该位于当前时间帧内领域像素颜色的范围内。普遍的方法是使用一个AABB包围盒在颜色空间内将当前像素的周围3x3个像素的颜色包围住,AABB形成一个颜色范围,然后判断对历史颜色缓存的采样是否落在这个颜色范围内。

颜色空间是一个色度学中的概念,在色度学中,一个颜色由RGB三个原色混合而成,三个原色的混合比例的和是1,3D空间颜色可以表述为2D的颜色范围,这个范围表现为一个三角形,在每个颜色空间内,每个颜色值到三角形每个顶点的比例之和为1。

不同颜色区间可以相互转化,但可能存在一定的颜色损失。为了保证亮度高的颜色范围能够被保留,通常颜色之间的转换不是线性的,如伽马矫正。

要表示当前像素周围3x3个像素的颜色范围,我们只需要在颜色空间上使用一个2D的多边形包围住这9个像素的颜色值。

如果历史颜色落在多边形包围盒内,则认为像素的颜色没有发生太大变化,可以与历史颜色混合。否则则不混合进当前颜色中。但是这样因为抛弃之前的像素导致出现走样,考虑到历史颜色由有效变为无效的过程可以认为是一个线性的过程,可以找出在这个变化过程中,最接近当前多边形包围盒范围的颜色,这个颜色可以混合进当前像素的颜色中。

AABB盒因为会包含很多无效历史颜色,还是会有重影效果,但是多边形判断也会造成GPU压力,因此有一种改进的AABB包围盒颜色范围。

这种新的裁剪技术称为方差裁剪,它首先求出所有3X3个领域像素颜色的方差,然后使用方差围绕期望值的变化范围建立一个AABB方差包围盒。

 

聚集几何缓存反走样:

TAA不能有效的处理子像素特征,如草地,动物毛发等超薄超细的表面,这些表面中,每个像素可能包括多达七八个三角形图元,因此TAA往往要结合SSAA来处理子像素特征。

但是SSAA会占用很大的带宽所以基本不能在延迟渲染中用。

AGAA是聚集几何缓存反走样技术,他类似预过滤的方式,低分辨率的纹理通过从高一级分辨率的纹理中提前过滤出来,高分辨率的子采样点几何数据被使用预过滤器提前过滤为一个称为几何聚集的更小分辨率几何数据,每个几何聚集对应一个像素内一部分可见图元的覆盖率,深度,法线等相关表面属性,经过预过滤的G-buffer称为几何聚集缓存AG-buffer。

分为4步执行:

  • 深度前向通道使用高密度的采样率对每个像素的可见性进行采样,这一步仅输出深度,法线几何数据到g-buffer。(高密度可见性采样,使用多重采样技术保证几何细节)
  • 聚集定义,按照子采样点的深度和法线特征,将该像素内的所有子采样点分成c个聚集,每个聚集包含多个子采样点。聚集定义仅标明每个子采样点数据属于哪个聚集,除此之外不做任何处理。(使用簇分配算法分配像素到聚集中,AGAA使用基于距离的分簇算法,因为可以假设在空间局部范围内,子像素之间的可见性和方向倾向于一致,每个聚集存储一个与所有子采样点的映射关系。)
  • AG-buffer,使用第二个光栅化通道对几何场景进行渲染,开启早期深度测试,设置深度比较为等于,只有那些处于第一步生成深度值的像素才被计算,每个子像素的几何数据被累计到根据聚集定义阶段定义的聚集当中,输出AG-buffer。(关键步骤,多级纹理的过滤技术是提前将多个纹素按照一定的权重加权成一个纹素,然后供给低分辨率的着色方程使用。这里也是类似这个做法,将几何数据进行过滤,按一定的权重求其加权平均值。聚集几何缓存数据生成阶段的主要目的是将n个子采样点过滤到c个聚集中(c<n),需要用到目标无关光栅化技术,它可以对深度测试使用更高的采样率,而对输出颜色目标使用更低的分辨率。但法线比较特殊,因为加权平均的法线并不正确,需要考虑变化的期望和方差来计算。)
  • 延迟着色,使用AG-buffer进行着色计算。

单AGAA还处于早起使用阶段,还需要大量的验证。AGAA一些比较常见的问题可以通过提高聚集数量来改善。但因为所有预过滤的几何参数依赖一个相同的光照模型,它仅适用于比较统一的光照模型,这是这个技术最大的弱点。

 

 

参考:全局光照技术:从离线到实时渲染

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值