古墓丽影(2015)的崛起是古墓丽影(2013)精彩重启的续集。 我个人觉得这两个部分都很有趣,因为它们脱离了停滞的原始系列,并再次讲述了劳拉的故事。 在此游戏中,就像在前传中一样,情节占据了中心位置,它提供了迷人的制作,狩猎和攀爬/研究机制。
古墓丽影(Tomb Raider)使用了Crystal Dynamics开发的Crystal Engine,也用于
Deus Ex:Human Revolution 。 续集使用了一个名为Foundation的新引擎,该引擎先前是为Lara Croft和Temple of Osiris开发的(2014年)。 通常可以将其渲染描述为具有初步照明通过的瓦片引擎,稍后我们将了解其含义。 该引擎允许您在渲染器DX11和DX12之间进行选择。 我选择了后者,原因如下。 为了捕获框架,Geforce 980 Ti使用了
Renderdoc 1.2,该游戏包含所有功能和装饰。
分析框架
为了避免破坏,在这种情况下,坏人会追逐Lara,因为她正在寻找他们所寻找的工件。 没有武器就无法解决这种利益冲突。 劳拉在夜间潜入敌军基地。 我选择了带有大气和对比照明的镜架,引擎可以在其中展示自己。
深度提前
在这里,执行了许多游戏的常规优化-深度的一小部分初步传递(大约100次平局)。 游戏会渲染最大的对象(而不是占据屏幕上更多空间的对象),以利用Early-Z视频处理器功能。 在
英特尔文章中阅读有关它的更多信息。 简而言之,如果GPU可以确定像素着色器被先前的像素覆盖,则它们能够避免执行像素着色器。 这是相当便宜的过程,用深度值预填充Z缓冲区。
在这一点上,我发现了一种有趣的细节(LOD)技术,称为“模糊”或“棋盘格”。 这是逐渐显示或隐藏一定距离的对象的常用方法,以便以后可以用较低质量的网格替换它们,也可以完全隐藏它们。 看这辆卡车。 看起来它正在渲染两次,但实际上它是在同一位置以高LOD和低LOD进行渲染。 每个级别都渲染其他像素未渲染的那些像素。 第一个LOD有182,226个顶点,第二个LOD有47,250个顶点,在很远的距离上它们是无法区分的,但是其中一个的价格要便宜三倍。 在此帧中,LOD 0几乎消失,LOD 1几乎完全呈现。 LOD 0完全消失后,将仅呈现LOD 1。
0级1级伪随机纹理和概率系数使我们可以丢弃尚未通过阈值的像素。 此纹理用于ROTR。 有人可能想知道为什么不使用alpha混合。 与起泡淡化相比,Alpha混合具有许多缺点。
- 深度初步通过的便利性:由于渲染了带有孔的不透明物体,因此我们可以在初步通过中进行渲染并使用Early-z。 由于排序问题,在早期阶段具有alpha混合的对象将不会呈现到深度缓冲区中。
- 需要其他着色器 :如果使用延迟渲染器,则不透明对象的着色器不包含任何照明。 如果需要用透明对象替换不透明对象,则需要有照明的单独选项。 除了由于对所有不透明对象使用至少一个附加着色器而增加了所需的内存量和复杂性之外,它们还必须准确以避免对象向前移动。 这有很多原因,这很困难,但这全都归结为现在在不同的代码路径中完成渲染。
- 较大的重绘 :Alpha混合可以创建较大的重绘,并且在对象具有一定程度的复杂性的情况下,可能需要很大一部分带宽来遮蔽LOD。
- Z冲突 :当两个多边形以非常接近的深度渲染时,z冲突是一种闪烁效果 。 在这种情况下,浮点计算的不准确性迫使它们依次进行渲染。 如果我们渲染两个连续的LOD,逐渐隐藏一个LOD,然后显示第二个LOD,则它们会引起z冲突,因为它们彼此非常接近。 总有解决方法,例如,优先选择一个多边形而不是另一个多边形,但是这样的系统很复杂。
- Z缓冲区效果 :许多效果(如SSAO)仅使用深度缓冲区。 如果已经完成环境光遮挡的时候在管道的末端渲染了透明对象,则无法考虑到它。
该技术的缺点是它看起来比alpha混合更糟,但是良好的噪声模式,起毛后模糊或临时抗锯齿几乎可以将其完全隐藏。 在这方面,ROTR并没有做任何特别的事情。
普通通行证
Crystal Dynamics在其游戏中使用了一种非常不寻常的照明模式,我们将在照明走道中进行介绍。 现在,只需说引擎没有G缓冲区传递即可。 至少达到其他游戏所熟悉的程度。 在此通道上,对象仅将有关深度和法线的信息传输到输出。 法线以RGBA16_SNORM格式的渲染目标记录在世界空间中。 奇怪的是,该引擎使用Z-up方案,而不是Y-up(Z轴指向上方,而不是Y轴),这种方式在其他建模引擎/程序包中更常用。 alpha通道包含光泽度,然后将其解压缩为
exp2(glossiness * 12 + 1.0)
。 光泽度值也可以为负,因为该符号用作指示表面是否为金属的标记。 可以单独注意到这一点,因为alpha通道中的所有深色都与金属物体有关。
正常的光泽度/金属感出发优势还记得在“深度初步”部分中,我们谈到了节省像素成本吗? 我会回头再说明一下。 拍摄下图。 这会将山脉的详细部分渲染到普通缓冲区。 Renderdoc会用绿色突出显示通过深度测试的像素,而未通过红色突出显示的像素(不会渲染)。 如果没有此初步通过,将要渲染的像素总数大约等于104518(在Photoshop中计算)。 实际渲染的像素总数为23858(由Renderdoc计算)。 节省约77%! 如我们所见,通过明智地使用,此初步通过可以带来很大的收益,并且只需要大约100次抽奖。
记录多线程命令值得一提的是,我选择了DX12渲染器-记录多线程命令,这是一个有趣的方面。 在诸如DX11之类的早期API中,渲染通常在单个线程中执行。 图形驱动程序从游戏接收了渲染命令并不断传输GPU请求,但游戏不知道何时会发生。 这导致效率低下,因为驱动程序必须以某种方式猜测应用程序正在尝试执行的操作,并且无法扩展到多个线程。 诸如DX12之类的较新API将控制权移交给了开发人员,开发人员可以决定如何编写命令以及何时发送命令。 尽管Renderdoc无法显示录制的完成方式,但是您会看到有七个标记为Color Pass N的彩色通道,并且每个通道都包裹在一对ExecuteCommandList中:Reset / Close。 它标记了命令列表的开始和结束。 该列表约占100-200个抽奖电话。 这并不意味着它们是使用多个流进行记录的,而是对其进行了提示。
雪中的足迹如果您查看Lara,可以看到在屏幕截图前面移动时,她在雪中留下了痕迹。 在每帧中,都会执行计算着色器,该着色器会记录某些区域中的变形,并根据曲面的类型和高度来应用变形。 在这里,仅法线贴图应用于雪(即几何形状不变),但是在雪较厚的某些区域中,实际上是变形! 您还可以看到雪如何“落”到位并填充Lara留下的轨道。
GPU Pro 7中对此技术进行了更详细的描述。 雪翘曲纹理是一种高程图,可跟踪Lara的运动并粘贴在边缘周围,以便采样着色器可以利用此折叠功能。
阴影图集
创建阴影贴图时,使用了一种相当通用的方法-将尽可能多的阴影卡打包到一个通用的阴影纹理中。 这样的阴影图集实际上是一个巨大的16位纹理,大小为16384×8196。 这使您可以非常灵活地重用和缩放地图集中的阴影贴图。 在我们正在分析的框架中,地图集中记录了8张阴影图。 其中四个与定向照明的主要来源有关(月亮是因为它发生在夜间),因为它们使用级联阴影贴图-一种用于定向照明的长距离阴影的相当标准的技术,我已经在
前面进行了解释。 更有趣的是,该帧的捕获中还包括几个投影和聚光灯源。 在此帧中记录8个阴影图的事实并不意味着其中只有8个投射阴影光源。 游戏可以缓存阴影计算,也就是说,未更改光源位置或示波器中几何图形的照明不应更新其阴影贴图。
似乎阴影映射的渲染还受益于将多线程命令写入列表,在这种情况下,多达19个命令列表被写入以渲染阴影映射。
定向照明的阴影定向照明的阴影在照明通过之前进行计算,然后进行采样。 我不知道如果场景中有多个定向光源,会发生什么情况。
环境遮挡
对于环境光遮挡,ROTR允许您使用HBAO或HBAO +的变体(此技术最初由NVIDIA发布)。 该算法有多种变体,因此我将考虑在ROTR中找到的一种。 首先,深度缓冲区被划分为16个纹理,每个纹理包含所有深度值的1/16。 分离的方式如下图所示,每个纹理包含来自原始纹理的4×4块的一个值。 第一个纹理包含所有标记为红色(1)的值,第二个纹理包含标记为蓝色(2)的值,依此类推。 如果您想了解更多有关此技术的信息,这是
Louis Bavoil 的文章 ,他也是有关HBAO的文章的作者之一。
下一步将为每个纹理计算环境光遮挡,这将为我们提供16个AO纹理。 如下生成环境光遮挡:对深度缓冲区进行几次采样,重新创建位置并累积每个样本的计算结果。 每个环境光遮挡纹理都是使用不同的采样坐标来计算的,也就是说,在4x4像素块中,每个像素都讲述了故事的自身部分。 这样做是出于性能原因。 每个像素已经对深度缓冲区进行了32次采样,而完整的效果将需要16×32 = 512个采样,即使是功能最强大的GPU也是如此。 然后,它们重新组合为一个全屏纹理,这看起来非常嘈杂,因此在此之后要平滑结果,将执行全屏模糊处理。 我们在
《魔多阴影》中看到了非常相似的解决方案。
HBAO零件充满噪音的完整HBAO完整的HBAO水平模糊准备好HBAO瓷砖照明预通过
灯光预通过是一种非常不寻常的技术。 大多数开发团队结合使用延迟+直接照明计算(带有变化,例如,使用平铺,群集)或完全直接用于屏幕空间的某些效果。 预照明技术是如此罕见,以至于值得解释。 如果传统的延迟照明的概念是将材料的属性与照明分开,那么将照明与材料的属性分开的想法就是照明初步通过的基础。 尽管此措辞看起来有些愚蠢,但与传统的延迟照明的区别在于我们将所有材料属性(例如反照率,镜面反射的颜色,粗糙度,金属性,微遮挡性,发射性)存储在巨大的G缓冲区中,以后用作输入后续照明通行证的数据。 传统的延迟照明会给吞吐量带来很大的负担。 材料越复杂,G缓冲区中需要的信息和操作就越多。 但是,在初步光照过程中,我们首先使用最少的数据量分别累积所有光照,然后在后续过程中将其应用于材质。 在这种情况下,照明仅足以满足法线,粗糙度和金属感。 着色器(此处使用两遍)以三种渲染目标RGBA16F格式输出数据。 一个包含漫射照明,第二个包含镜面照明,第三个包含环境照明。 此时,所有阴影数据都已考虑在内。 奇怪的是,在全屏扫描的第一遍(漫反射+镜面照明)中,使用了两个三角形的四边形,而在其他效果中,则使用了一个全屏三角形(为什么如此重要,您可以在
此处找到)。 从这个角度来看,整个框架不是完整的。
漫射照明镜面照明环境照明瓷砖优化瓷砖照明是一种优化技术,旨在渲染大量光源。 ROTR将屏幕分成16×16的图块,然后保存有关哪些光源与每个图块相交的信息,即,仅对与这些图块相关的那些光源执行照明计算。 在帧的开始,将启动一系列计算着色器,这些着色器确定哪些源与图块相关。 在照明阶段,每个像素确定其位于哪个图块中并遍历图块中的每个光源,执行所有照明计算。 如果将源有效地链接到图块,则可以节省大量计算和大部分带宽,并提高生产率。
深度缩放基于深度的上采样是一种有趣的技术,可用于此过程和后续过程。 有时,无法以全分辨率呈现计算量大的算法,因此它们会以较低的分辨率呈现,然后按比例放大。 在我们的情况下,环境照明是以一半的分辨率计算的,也就是说,在计算之后,必须正确地重新创建照明。 以最简单的形式拍摄并内插4个低分辨率像素,以得到类似于原始图像的图像。 这样可以平滑过渡,但在不连续处效果不佳,因为我们混合了不相关的值,这些值可以在屏幕空间中相邻但在世界空间中彼此远离。 在解决该问题的方法中,通常会使用几个深度缓冲区样本,并将其与我们要重新创建的深度样本进行比较。 如果样本距离太远,则在重建时我们不会将其考虑在内。 这样的方案效果很好,但是这意味着休闲着色器非常占用带宽。
ROTR采取了一项棘手的措施,即尽早丢弃模板。 通过法线后,深度缓冲区将完全填满,因此引擎将执行全屏传递,并在模板缓冲区中标记所有中断的像素。 当需要重新创建环境照明缓冲区时,引擎将使用两个着色器:一个着色器对于没有深度间隙的区域非常简单,另一种着色器对于具有间隙的像素则更为复杂。 如果像素不属于相应区域,则早期模板将丢弃它们,即,仅在必要区域中存在成本。 以下图像更加清晰:
半分辨率环境照明缩放内部深度全分辨率环境照明,无肋骨扩大肋骨的深度准备好环境照明半分辨率视图重建图像的特写视图在初步通过照明之后,将几何图形转移到传送带,仅这次是每个对象都对照明纹理,环境光遮挡纹理以及我们从一开始就没有写入G缓冲区的材料的其他属性进行采样。 这样做很好,因为由于您无需读取一堆纹理将它们写入大的G缓冲区,然后再次读取/解码它们,因此可以大大节省带宽。 这种方法的明显缺点是,所有几何形状都需要重新传输,并且照明的初步通过本身的纹理本身对吞吐量造成了很大的负担。 我想知道为什么不使用较浅的格式(例如R11G11B10F)来进行初步的光照传递纹理,但是在alpha通道中有其他信息,因此这是不可能的。 尽管如此,这是一个有趣的技术解决方案。 至此,所有不透明的几何体均已渲染并照亮。 请注意,它包括诸如天空和笔记本电脑屏幕之类的发光物体。
感言
这个场景不是展示反射的好例子,所以我选择了另一个。 反射着色器是循环的相当复杂的组合,可以减少为两个部分:一个采样立方贴图,另一个执行SSR(屏幕空间反射-计算屏幕空间中的反射); 所有这些操作均一遍完成,最后将其混合,同时考虑确定SSR是否检测到反射的系数(该系数可能不是二进制的,而是区间[0,1]中的值)。 SSR在许多游戏中都以一种标准方式工作-它反复跟踪深度缓冲区,试图找到阴影表面反射的光线与屏幕上任何位置的另一个表面之间的最佳交点。 SSR与先前减小的当前HDR缓冲区的比例的mip链一起使用,而不与整个缓冲区一起使用。
还有一些校正因子,例如反射的亮度以及特殊的菲涅耳纹理,这些纹理是在此通过之前根据法线和粗糙度计算得出的。 我不太确定,但是在研究汇编代码之后,我觉得ROTR只能计算光滑表面的SSR。 在SSR阶段之后,该引擎没有模糊的Mip链(其他
引擎中也没有),甚至没有像使用射线追踪深度缓冲区之类的东西,该光线
根据粗糙度而
变化 。 通常,较粗糙的表面会接收来自三次方贴图的反射,或者根本不会接收到它们。 不过,考虑到SSR不会随时间累积并且不会对其进行空间模糊这一事实,在SSR工作的地方,其质量非常高且稳定。 Alpha数据还支持SSR(在某些寺庙中,您可以在水中看到非常漂亮的反射),这是您不经常看到的很好的补充。
反思反射缓冲区之后的思考点燃的雾
在我们的场景中,雾的表现很差,因为它会使背景变暗,因此是由粒子产生的,因此我们再次将示例与反射配合使用。 雾比较简单,但是很有效。 有两种模式:整体模式,雾的一般颜色以及从三次方贴图获得的向内散射的颜色。 也许是再次从三次反射贴图中提取了三次贴图,或者重新创建了一次。 在这两种模式下,雾的稀疏度都是从全局稀疏纹理中获取的,其中稀疏曲线被打包以产生多种效果。 在这样的方案中,值得注意的是,它给出了非常低成本的照明雾,即 散射空间中的向内变化,从而产生了雾与远处照明相互作用的幻觉。 这种方法也可以用于向天空附近的向内大气散射。
雾到雾后体积照明
在框架的早期阶段,将执行几项操作以准备体积照明。 两个缓冲区从CPU复制到GPU:光源索引和光源数据。 两者都由计算着色器读取,该着色器输出相机视图的40x23x16 3D纹理,其中包含穿过该区域的光源数量。 纹理为40×23,因为每个图块占用32×32像素(1280/32 = 40、720 / 32 = 22.5),而16是深度像素数。 纹理不包括所有光源,仅包括标记为大量的光源(在我们的场景中有三个)。 正如我们将在下面看到的那样,平面纹理还会产生其他虚假的体积效果。 显示的纹理具有更高的分辨率-160x90x64。 在确定每个图块的光源数量及其索引之后,依次执行三个计算着色器,执行以下操作:
- 第一遍以可见角金字塔的形式确定进入体积内单元的光量。 每个单元都会累积所有光源的影响,就好像它们具有对光起反应并将其一部分返回相机的悬浮粒子一样。
- 第二遍使半径小的照明模糊。 由于分辨率很低,可能有必要避免移动相机时出现闪烁。
- 第三遍从前到后绕过体积纹理,逐渐增加每个源的影响并提供完成的纹理。 实际上,它模拟了沿光束到给定距离的入射光的总量。由于每个单元都包含一部分被粒子反射回相机的光,因此在每个单元中,我们将收到所有先前通过的单元的共同贡献。此段落也确实模糊。
完成所有这些操作后,我们将获得3D纹理,该纹理报告特定位置相对于相机接收的光量。全屏通道中剩下要做的就是确定该位置,找到纹理的相应体素并将其添加到HDR缓冲区中。照明着色器本身非常简单,仅包含约16条指令。体积点亮之后的体积照明头发渲染
如果未启用PureHair功能,则将标准头发层叠加在一起。该解决方案仍然看起来不错,但我想重点介绍最新技术。如果启用此功能,则框架将从使用一系列计算着色器模拟Lara的头发开始。《古墓丽影》的第一部分使用了一种称为TressFX的技术,而续集《水晶动力》则实施了一项改进的技术。经过初步计算,我们获得了多达7个缓冲区。它们全部用于控制拉拉的头发。流程如下:- 启动计算着色器以根据先前和当前位置计算运动值(用于运动模糊)
- 1×1 ()
- 122 (Triangle Strip) ( — ). , . 7 , . , , . « ».
- / quad , , . , , .
- 4, ( « »)
如果您有兴趣了解更多有关此的信息,那么AMD有许多资源和演示文稿,因为它是该公司创建的公共图书馆。我对第1阶段之前的阶段感到困惑,在该阶段执行与第3阶段相同的绘制调用,据说它仅呈现深度值,但实际上未呈现内容,这很有趣;也许Renderdoc没有告诉我任何事情。我怀疑他可能试图执行条件渲染请求,但是我看不到任何预测调用。发起来可见的头发像素阴影的头发平铺渲染Alpha数据和粒子
透明对象再次使用针对瓦片初步照明过程计算的光源瓦片分类。每个透明物体在一次通过中就计算出自己的照明,也就是说,指令和循环的数量变得非常可怕(这就是为什么照明的初次传递用于不透明物体的原因)。如果透明对象被打开,它们甚至可以在屏幕空间中执行反射!将每个对象从后到前的排序顺序直接渲染到HDR缓冲区中,包括玻璃,火焰,车辙水等。当Lara聚焦于某个物体(例如,左侧盒子上有可燃混合物的瓶子)时,alpha通道还会使边缘突出显示。但是,粒子被渲染到半分辨率缓冲区中,以消除其重绘所产生的带宽上的巨大负载,尤其是当许多覆盖屏幕的大粒子用于产生雾,雾,火焰等时。因此,HDR缓冲区和深度缓冲区的每一侧减少一半,此后开始粒子渲染。粒子会产生大量的重绘,某些像素会被着色大约40次。热图显示了我的意思。由于以半分辨率渲染粒子,因此此处使用与环境照明相同的智能缩放技巧(在模具中标记间隙,第一遍渲染为内部像素,第二遍渲染边缘)。您可能会注意到,粒子还可以渲染其他一些alpha效果,例如火焰,发光等这是必需的,以便可以相对于例如烟雾正确地对alpha进行排序。您还可以注意到,这里出现了来自安全聚光灯的“体积”光线。它们是在此处添加的,而不是在体积照明阶段创建的。这是一种长距离创建它们的低成本但现实的方法。-123
-ROTR可以一次完成快门速度和色调校正。但是,尽管我们通常认为伽马校正与色调校正一起发生,但事实并非如此。正如我们在其他 游戏中所看到的,有很多方法可以实现曝光。 ROTR中的亮度计算非常有趣,几乎不需要任何中间数据或通过次数,因此我们将更详细地说明此过程。整个屏幕被划分为64×64的图块,此后,每个256个流的组(20、12、1)的计算就开始填充整个屏幕。每个线程基本上执行以下任务(下面显示了伪代码): for(int i = 0; i < 16; ++i) { uint2 iCoord = CalculateCoord(threadID, i, j);
每个组计算所有64个像素(256个线程,每个线程处理16个值)的对数和。它不存储平均值,而是保存实际处理的像素的总和和数量(并非所有组都精确地处理64×64像素,因为例如,它们可能超出屏幕边缘)。 Shader明智地使用本地线程存储来拆分总和。每个流首先使用16个水平值,然后单独的流垂直汇总所有这些值,最后,该组的控制流(流0)将结果相加并将其全部保存到缓冲区中。该缓冲区包含240个元素,实际上使我们可以看到屏幕许多区域的平均亮度。以下命令启动64个线程,这些线程循环所有这些值并将其相加,以获得最终的屏幕亮度。它还从对数返回线性单位。我对曝光技术没有太多经验,但是阅读Krzysztof Narkovic的这篇文章澄清了一些事情。保存到包含64个元素的数组以计算移动平均值是必要的,在移动平均值中,您可以查看以前的计算值并平滑曲线,以避免亮度非常急剧的变化,从而造成快门速度的急剧变化。这是一个非常复杂的着色器,我仍未弄清楚其所有细节,但最终结果是与当前帧相对应的快门速度值。找到合适的快门速度后,一遍将执行最终的快门速度加色调校正。ROTR似乎使用照相色调映射,它解释了对数方式的使用,而不是通常的方式。着色器(曝光后)中的色调校正公式可以扩展如下:在这里可以找到简要说明。我无法弄清楚为什么有必要用Lm进行除法,因为它消除了乘法的影响。无论如何,whitePoint是1.0,因此在此帧中该过程不会做太多事情,图像只会更改快门速度。LDR间隔的值甚至没有限制!当颜色多维数据集间接限制大于1.0的值时,它会在颜色分级期间发生。暴露于曝光后镜头光晕
镜头光晕以有趣的方式渲染。少量的初步通过计算出1xN的纹理(其中N是将被渲染为镜头光斑的眩光元素的总数,在我们的示例中为28)。该纹理包含粒子的alpha值和一些其他未使用的信息,但是引擎不是通过可见性请求或类似的东西来计算它,而是通过分析圆中粒子周围的深度缓冲区来计算它。为此,将有关顶点的信息存储在像素着色器可用的缓冲区中。然后,将每个元素渲染为从光源发出的简单的平面对齐平面。如果alpha值小于0.01,则将NaN值分配给该位置,以使该粒子不会栅格化。它们有点像绽放效果并添加光晕,但是此效果本身是稍后创建的。镜头耀斑镜头光晕元素镜头耀斑后布卢姆
Bloom使用一种标准方法:对HDR缓冲区进行下采样,将明亮的像素隔离,然后按比例增加其比例并增加模糊以扩大其影响范围。结果被放大到屏幕分辨率,并且合成叠加在其顶部。有几个有趣的观点值得探讨。整个过程使用7个计算着色器执行:2个用于下采样,1个用于简单模糊,4个用于放大。- target (mip 1). . , mip- , 0.02.
- mip mip 2, 3, 4 5.
- mip 5. , . , .
- — . 3 , mip N mip N + 1, , . bloom , .
- mip 1 HDR-, bloom.
BloomMIP 1 BloomMIP 2 BloomMIP 3 BloomMIP 4 BloomMIP 5 BloomMIP 5 BloomMIP 4 BloomMIP 3 BloomMIP 2 BloomMIP 1 Bloom花后好奇的方面是,缩小的比例的质地改变纵横比。为了直观起见,我对其进行了更正,并且只能猜测其原因。也许这样做是为了使纹理大小是16的倍数。另一个有趣的点:由于这些着色器通常在带宽上非常有限,因此组共享内存中存储的值将从float32转换为float16!这允许着色器交换数学运算以使可用内存和带宽加倍。为了解决这个问题,值的范围应该变得很大。Fxaa
ROTR支持各种不同的抗混叠技术,例如FXAA(快速近似AA)和SSAA(超级采样AA)。值得注意的是,缺少用于启用临时AA的选项,因为对于大多数现代AAA游戏而言,它已成为标准。尽管如此,FXAA可以出色地完成其任务,SSAA也可以很好地工作,如果游戏缺乏性能,这是一个相当“沉重”的选择。运动模糊
似乎运动模糊使用的方法与《暗影阴影》中的解决方案非常相似。渲染体积照明后,单独的渲染通道将动画对象的运动矢量输出到运动缓冲区。然后,将该缓冲区与相机引起的运动进行组合,最后的运动缓冲区将输入到模糊通道,从而在屏幕空间的运动矢量指示的方向上执行模糊。为了估计几遍的模糊半径,计算了缩小比例的运动矢量的纹理,以便每个像素大致了解附近有什么类型的运动。模糊过程是在几遍过程中以一半的分辨率执行的,正如我们所看到的,后来在模板的帮助下,它的比例在两遍过程中增加了。进行几次通过有两个原因:首先,以减少创建可能具有非常大半径的模糊所需的纹理读取次数,其次是因为执行了不同类型的模糊。这取决于动画角色是否在当前像素上。运动模糊到运动模糊速度运动模糊通行证1运动模糊通行证2运动模糊通行证3运动模糊通行证4运动模糊通行证5运动模糊通行证6运动模糊,放大和缩小运动模糊,边缘缩放附加功能和详细信息
还有更多值得一提的细节。- 相机冻结:在寒冷的天气中,相机会出现雪花和白霜
- 脏相机:在相机上添加灰尘。
- 颜色校正:如上所述,在帧的末尾,使用相当标准的颜色立方体执行较小的颜色校正,以进行颜色校正,并且还会添加噪点,使某些场景更加严谨
用户界面
UI的实现有些不寻常-它在线性空间中呈现所有元素。通常,到渲染时,UI已经完成了色调校正和伽马校正。但是,ROTR一直使用线性空间,直到帧的最后。这是有道理的,因为该游戏让人联想到3D UI。但是,在将sRGB图像记录到HDR缓冲区之前,必须将它们转换为线性空间,以便最新操作(伽玛校正)不会使颜色失真。总结一下
我希望您喜欢阅读分析的方式与我一样。就个人而言,我绝对从中学到了很多。祝贺有才能的Crystal Dynamics开发人员在创建此引擎方面所做的出色工作。我还要感谢Baldur Karlsson在Renderdoc方面所做的出色工作。他的工作使在PC上调试图形变得更加方便。我认为在此分析中唯一有点复杂的事情是跟踪着色器自身的启动,因为在编写本文时,DX12尚不具备此功能。我希望随着时间的流逝,我们都会感到非常高兴。