如何渲染中土的框架:魔多之影


《中土:魔多之影》于2014年推出。 游戏本身是一个很大的惊喜,并且它是《指环王》宇宙故事情节的副产品,这是出乎意料的。 游戏取得了巨大的成功,在撰写本文时, Monolith已发行了续集-战争阴影。 游戏的图形非常漂亮,特别是考虑到它是为包括Xbox 360和PS3在内的不同版本的游戏机发布的。 PC版的抛光效果非常好,包含其他图形选项和高分辨率纹理包,充分展现了游戏的潜力。

游戏使用相对较新的DX11延迟渲染器。 我使用Renderdoc深入学习了游戏的渲染技术。 在工作时,使用了最大可能的图形参数(超),并且包括了所有可能的“乳液”,例如与顺序无关的透明度,细分,屏幕空间的遮挡和各种运动模糊。

车架


这是我们将要分析的框架。 播放器位于乌敦地区的木制平台上。 魔多之影的使用机制类似于刺客信条(Assassin's Creed)等游戏的机制,您可以在其中攀爬建筑物和塔楼,然后从屋顶欣赏周围的数字景观。

图片

深度通道


大约有140个第一个渲染调用进行了快速的初步传递,以将最大的高程元素和建筑物渲染到深度缓冲区。 大多数对象都不会进入此初步阶段,但是当游戏中有大量抽奖电话并且您可以远看时,它会有所帮助。 有趣的是,始终位于屏幕中央且占据屏幕空间相当一部分的角色不会落入此段落。 与其他许多开放世界游戏一样,引擎使用z值倒数。 该技术将近平面绑定到1.0,将远平面绑定到0.0,以在较大距离处提高精度并防止z冲突。 在此处阅读有关z缓冲区精度的更多信息。

图片

G缓冲


此后,G缓冲区传递立即开始,大约在2700个绘制调用中执行该传递。 如果您阅读了我先前对《恶魔城:暗影之王2》的分析或研究了其他类似文章,那么您应该熟悉这一段。 表面的属性记录在一组缓冲区中,然后在照明计算过程中读取这些缓冲区,以计算表面对光的反应。 魔多之影使用了经典的延迟渲染器,但是使用了G缓冲区的相对较少的渲染目标来实现此目标(3)。 为了进行比较:本节中的虚幻引擎使用5-6个缓冲区。 G缓冲区具有以下方案:

普通缓冲

[Rg ^
普通x正常正态编号

法线缓冲区以“每通道8位”的格式将法线存储在世界空间中。 这仅足以描述平滑变化的平坦表面,有时甚至根本无法描述。 如果仔细观察,这可以在游戏中的一些水坑中看到。 alpha通道用作标记各种对象的ID。 例如,我发现255是指角色,128是标志的动画部分,并且天空用ID 1标记,因为后来这些标识符在添加阶段用于过滤它(天空有其自身的放射状绽放)。



在原始文章中,为使这些图像和许多其他图像具有动画效果,以提高清晰度,因此我建议在此处查看。

缓冲区反照率

[Rg ^
反照率反照率反照率腔闭塞

反照率缓冲区存储所有三个反照率分量和一个小范围的遮挡(有时称为腔遮挡),用于遮挡阴影贴图或屏幕空间中的后处理无法达到的小细节。 它主要用于装饰目的,例如衣服上的孔和折痕,树上的小裂缝,Talion衣服上的小图案等。



在着色器中处理敌人时,反照率会考虑到血液的纹理(有趣的是,Talion永远不会出现可见的伤口)。 血液纹理是敌人衣服和敌人身体的渲染阶段的输入,但是它没有指定血液的颜色,而是恒定缓冲区中的输入,而是确定因素/血液水平以控制显示的血液量。 同样,法线方向用于缩放效果,使您可以控制血液飞溅的方向。 然后,反照率基本上会从敌人从血液图的适当位置获取的伤口的亮度上演阴影,并修改其他属性(例如镜面反射)以获得令人信服的血液效果。 我找不到在其中渲染地图的框架部分,但我假设当剑暴露在外时就将它们记录在框架的开头,然后在此处使用。





镜面缓冲

[Rg ^
粗糙度镜面强度菲涅耳地下散射因子

镜面反射缓冲区还包含其他可以在游戏中看到的表面特性,例如粗糙度(这不是很粗糙,而是缩放的镜面度,但是可以用这种方式解释),镜面反射强度(镜面反射的亮度)可以缩放反照率以获得正确的彩色镜面反射率因子(在文献中通常称为F0,因为它是菲涅耳镜响应的输入)和次表面散射(次表面散射)分量。 后一个组件用于照亮半透明的材料,例如薄组织,植物和皮肤。 如果稍后再深入研究照明着色器,我们会发现这里我们使用了根据Blinn-Fong归一化镜面模型的变体。





延期贴花

正如我们在上面看到的,魔多之影在受伤的角色上显示出相当详细的鲜血痕迹。 当塔利翁挥舞着剑时,周围的环境也得到了黑兽人的血液。 但是,另一种技术用于环境- 延迟贴花 。 该技术包括将一组平面纹理投影到先前渲染的表面上。 以这种方式,在执行照明传递之前,G缓冲区的内容被该新内容替换。 在有血迹的情况下,只需带血的喷雾就足够了,而当依次绘制许多贴花时,很快就会创建出一片阴暗的风景。












G缓冲区通道中渲染的最后一件事是天空,HDR BC6H格式的天空纹理具有很高的分辨率(8192×2048)。 我必须进行一点色调校正,因为在HDR中所有颜色都太暗了。

图片

镶嵌


细分是游戏的一个非常有趣的“功能”(如果已打开)。 从地形到角色渲染(以及道具和角色对象),它可用于许多不同的事物。 在此,细分不会细分低多边形网格,而是根据必要的细分级别(例如,到相机的距离),使用必要的细分程度,从点云中创建多边形。 一个有趣的示例是Talion斗篷,它作为点云(在物理模拟之后)传输到GPU,而镶嵌着色器重新创建了多边形。



与订单无关的透明度



令我惊讶的第一件事是头发护理通行证,因为它执行了非常复杂的特殊着色器。 在图形选项中,提到了用于头发的OIT(与订单无关的透明度)选项,也就是应该如此。 首先,它将输出执行到一个单独的缓冲区,并计算彼此叠加的透明像素的总数,同时将属性保存在类似于G缓冲区的“深层”结构中。 后来,另一个着色器根据片段的深度对片段进行排序。 箭头似乎也以这种方式呈现(可能是它们的羽毛需要适当的排序)。 这是非常微妙的效果,不会增加任何特殊的图形差异,但仍然是一个有趣的细节。 这是一个简单的示例:上图显示了重叠片段的数量(红色,更多的片段)。 常规透明度仍由CPU排序,并呈现为传统alpha。 只有个别实体才可以进入OIT通行证。

魔多之影


SoM有许多影子源。 除了传统的动态光源阴影贴图外,SoM还使用屏幕空间中的两通道环境光遮挡,为游戏中几乎所有对象创建的微尺度光遮挡,以及类似于具有顶视图的高度图的遮挡纹理。

屏幕空间遮挡


第一遍使用屏幕空间中的环境G缓冲区和镜面反射遮挡进行渲染。 着色器本身是一个巨大的展开循环,它对完整尺寸的深度图和以前减小的平均深度图都进行采样,以给定的模式查找相邻的采样。 它使用4x4正方形纹理来选择伪随机向量以搜索遮挡源。 它渲染了一个嘈杂的遮挡缓冲区,然后通过两次遍历中的简单模糊将其平滑。 此处最有趣的功能是有两种不同的遮挡通道:其中一种用作镜面遮挡,另一种用作漫反射遮挡。 在标准SSAO实施中,计算出一个通道,该通道适用于所有烘焙的照明。 在此,还读取SSAO卡以传输到使用它的定向照明通道。



影子卡

下一个事件是阴影贴图的渲染。 由于游戏的动作主要发生在开放空间中,因此大部分光线和阴影都来自主要的定向光。 在这里,我们使用级联阴影贴图的技术(其变化是平行划分的阴影贴图 ),这是一种在长距离上应用阴影的相当标准的技术,包括从光源的一个角度针对不同的空间区域渲染同一场景。 通常,远离相机覆盖区域的阴影贴图要么距离较远,要么分辨率比以前的分辨率低,从而从根本上降低了由于几何体太远而仍不需要细节的区域的分辨率。 在此场景中,游戏渲染了4096×4096的三个阴影级联(实际上,游戏中放置了四个)。 上面的梯级非常靠近Talion,下面的梯级包括距离相机很远的山脉和物体。 在处理阴影时,游戏使用与深度图相同的z倒数的技巧。


阴影缓冲区

下一步是创建阴影缓冲区。 这是一个单通道纹理,基于来自先前阴影贴图的遮挡信息,它对间隔[0,1]中的阴影因子进行编码。 为了创建边缘周围的平滑度,使用特殊的双线性采样器的状态对阴影贴图采样4次,该采样器接收4个采样并将它们与给定值进行比较(这称为“ 百分比接近滤波” )。 获取多个样本并平均其结果通常称为“ 百分比更接近柔和阴影” 。 除了读取阴影图之外,还采样了镜面反射缓冲区的最后一个分量(即,表面散射系数),然后乘以“光渗漏因子”。 看来有必要消除这些物体的阴影,使更多的光线通过它们。


定向投影纹理

另一个光影源是定向光源采样的顶视图纹理。 这是添加到定向光源颜色的色偏,加上应用于定向照明的全局阴影效果。 似乎其中一些是在具有顶视图的自动生成的水平照明图的顶部手动创建的。 似乎是对静态几何图形的阴影的边缘进行了手动调整(也许是为了避免与真实的阴影贴图发生冲突),并且某些部分似乎也被手工稍微着色了。 这种纹理的任务可能是除了定向照明之外,还低成本地添加了大规模的环境光遮挡和轻巧的全局照明模拟。 下图显示了色相,遮挡和这两个因素的乘积,这使我们对最终的色罩是什么样子有所了解。




所有光照通过的结果都保存在R11G11B10F格式的渲染目标中。 结果是这样的。 我对结果进行了色调校正,以使水平方向照明的效果更加明显。


所有远处的山脉(未在上图中显示)也被定向光照亮,但它们作为单独的案例被突出显示,以便可以更好地控制照明。 有些是按比例缩放的,但较远的实际上是伪造者,它们巧妙地创建了法线和反照率图。 它们具有仅影响山脉的定向照明光源。

静态照明


魔多之影(Shadow of Mordor)使用非常占用内存的静态光照实现,该光照使用非常大的体积纹理。 下图显示了三种静态纹理,这些纹理用于漫反射该区域的一部分的照明量。 它们每个都是一个巨大的512x512x128 BC6H压缩纹理,也就是说,每个纹理占用32 MB或通常占用96 MB(我们使用最高质量设置)。 颜色的纹理指示进入体素的强度。 其他两个表示沿所有六个方向xyz和-xyz的强度或强度,并且法线用于选择三个分量(正或负xyz,与法线最一致的分量)。 建立此向量后,我们将其向量乘积乘以法线的平方,这将成为强度的比例因子。 计算公式如下:



静态光体积还为镜面照明绘制了立方贴图,这很可能在SLV的中心捕获。 有趣的是,体纹理存储以BC6H压缩的HDR值,而三次贴图以BC3(DXT5)格式存储,该格式无法存储浮点值。 为了补偿此限制,alpha通道保留亮度,然后从1-10缩放。 这是一个奇怪的决定,对我来说,它看起来更像是传统的实现。 不要忘记该游戏是为上一代游戏机发布的,该游戏机不支持新的HDR纹理格式。


考虑到平均图像的影响,以下框架显示了“之前和之后”的结果。 为了实现可视化,我进行了音调校正。




大气雾



Mordor的影子具有天气和时间系统,在Mordor的游戏中,阳光照耀下或雨水倾泻而下。 该系统由组件的总和控制,最重要的组件之一是起雾。 Mordor的阴影使用大气雾的相当简单但物理上合理的模型,包括单个瑞利散射以及球形粒子的散射(米氏散射)。

首先从计算照相机相对于地球中心的位置开始。 几个三角公式可以确定相机在大气中的位置,像素在哪里,以及在给定的最大大气高度下光束在大气中的传播距离。 在我们的案例中,大气被设置在高于行星表面65,000米的高度。 考虑到此信息,瑞利和球形粒子系数可用于计算雾粒子的密度类型及其颜色。 这些密度会遮盖已经阴影的像素,使从阴影表面进入相机的光散射,并导致起雾。 模拟这种散射时,要考虑到太阳的亮度和方向。



快门速度和色调校正


在计算快门速度时,使用了一种相当标准的方法:将从主HDR颜色缓冲区计算出的亮度缓冲区的分辨率依次降低为一连串纹理,每个纹理的大小是前一个纹理的一半,从主帧缓冲区的1/3开始。 随着分辨率的降低,将对相邻像素的值取平均值进行4个采样,即,将所有平均值转换为单个纹理像素后,最终结果将变为平均亮度。 纹理达到16×9像素的大小后,将启动计算着色器,将所有剩余的像素汇总。 该值会在色调校正过程中立即读取以更改亮度值。







对于音调校正,使用Reinhardt运算符,可在此处此处找到其优化公式。 在hlsl代码中,如下所示:

float3 hdrColor = tex2D(HDRTexture, uv.xy); hdrColor *= exposureValue; // This was calculated by the compute shader in the luminance downsampling pass float3 x = max(0.0, hdrColor - 0.004); float3 finalColor = (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06); 

如果绘制此曲线,我们将看到即使输入值为2.0,该运算符也会丢弃10%的白色值,同时强制将较低间隔的一小部分完全变为黑色。 这将创建一个饱和度低的深色图片。

图片

阿尔法舞台


alpha步骤有点不寻常,因为它直接将对象渲染到LDR缓冲区。 其他游戏也将它们渲染到HDR缓冲区中,以便它们可以参与快门通道。 尽管如此,先前计算的亮度纹理仅限于所有alpha照明对象(在某些情况下,例如对于发光对象,快门速度是使用着色器常数而不是纹理搜索来计算的),因此在渲染时会自动应用快门速度,并且在后期处理中不执行。 在游戏中使用Alpha的一个非常特殊的情况是过渡到幻影模式(其中,Celebrimbor的幻影是在玩家的角色上绘制的,并在LOTR宇宙主权环中伪造而成;因此,游戏显示出他始终在附近,尽管不可见)。 该游戏将几个参数传递给两个角色的网格,从而控制不透明度并使游戏部分遮盖Talion并逐渐显示Celebrimore。 游戏中处于重影模式的其他对象也会在不透明对象(例如敌人和塔楼)的顶部渲染重影版本。 这是过渡到幽灵世界的另一个场景。





雨天


在我们研究的主要框架中,没有下雨,但是天气是游戏中非常重要的部分,我想在这里提及。 它是在GPU中生成和模拟的,并在alpha阶段结束时立即渲染。 运行计算着色器,以执行模拟并将位置写入缓冲区。 这些位置由另一个着色器占据,该着色器在实例化的间接调用的帮助下渲染与上次遍历中计算的位置一样多的四边形实例。 顶点着色器具有一个简单的四边形,如有必要,该四边形会变形并向摄像机旋转。 为防止雨水穿透表面,顶点着色器还从顶视图读取高度图,使您可以拒绝重叠表面以下的所有水滴。 该高度图在帧的开始处立即渲染。 相同的顶点着色器告诉像素着色器从液滴纹理中获取样本的位置。 如果液滴靠近表面,它将选择一个包含飞溅动画的纹理区域。 此外,雨滴在像素着色器中执行雾计算,以与场景的其余部分无缝融合。 这是同一天在雨天的屏幕截图。



激活降雨效果后,将对高光缓冲区进行全局修改以创建湿表面,并且将雨波渲染到普通缓冲区中。 动画是定时的,因此仅使用循环动画​​的一帧。 修改以下所示的普通缓冲区以显示渲染到缓冲区中的波。





镜头光晕和绽放


完成alpha渲染后,将在其顶部渲染镜头光晕。 从定向光(在我们的例子中是太阳)发出的点开始,渲染了一系列移位的四边形。 此后,立即进行光晕通过。 这是一种相当标准的技术,包括一系列尺寸减小和纹理模糊的纹理,这些纹理包含亮度超过特定阈值的像素。 使用了两次布隆遍,高斯模糊遍及整个场景,而特殊的径向模糊仅适用于天空。 径向模糊是使用法线贴图G缓冲区中特殊ID的操作之一,因为仅考虑天空中的像素。 作为奖励,这种模糊将对深度图进行采样,并可以创建低成本的暮光 。 由于在此阶段我们正在使用LDR缓冲区,因此光晕阈值的值不同于HDR地毯的值(高于阈值的值通常为1.0,因此需要进行计算),这意味着从光晕阈值获得的光晕值受到了轻微限制。 无论如何,这对游戏是有好处的,这是结果。 在下面的图像中,Bloom Mip纹理的颜色看起来有些奇怪,因为每个像素都由alpha通道中包含的亮度缩放。 此亮度是在色调校正阶段较早计算的。 在最终合成中,bloom计算为bloom.rgb·bloom.a·bloomScale
















抗锯齿+景深


关于这两个操作,没有什么要说的;使用行业标准方法。 在将光晕与LDR图像合成后立即执行FXAA抗锯齿的简单遍历,并在其后立即执行景深。 对于景深,游戏渲染最终缓冲区的两个减少的模糊版本。 然后使用像素深度混合模糊图像和正常图像,从而产生散焦效果。 为了清楚起见,在此截图中,我夸大了景深的影响。 该游戏具有内置的屏幕截图模式,可让您轻松配置这些条件。








运动模糊


运动模糊包括两次通过。 首先,来自先前和当前相机方向的数据将传输到全屏速度缓冲区。 在这种情况下,纹理的两个通道在屏幕空间中以速度填充。 现在,通道r包含屏幕水平方向上像素变化的大小,而通道g包含垂直方向上像素的大小。 这就是在移动相机时如何获得径向条纹。 再次渲染角色,这次根据其当前姿势和先前姿势填充蓝色通道,就像相机一样。 蓝色通道用于标记是否应渲染。 alpha通道也填充有一个恒定值(0.0598),但是我尚未研究其值或目标。 通过在原始纹理的相对较宽的速度范围内求平均,将速度缓冲区分辨率降低到非常小的纹理。 在最后一遍中,这使每个像素大致了解实际模糊遍中的模糊半径。

然后,模糊过程读取速度纹理,深度图,原始颜色缓冲区和噪波纹理。 后者用于隐藏镜像的效果,这种效果可能在半径较大的这种类型的模糊中发生。 然后,在速度缓冲器指示的方向上对图像缓冲器进行几次采样,对颜色进行平均,这导致图像在运动矢量方向上模糊。 同样,该效果根据游戏工作的帧频进行缩放。 对于此捕获,我不得不将游戏限制为30fps,因为在60fps及以上时,这几乎没有引起注意。





色彩校正


使用“色块”执行色彩校正的最后一步。 彩色多维数据集是3D纹理,其rgb分量捕捉到纹理的xyz坐标。 这些xyz坐标包含我们用来替换原始颜色的颜色。 在我们的例子中,查找表(LUT)是中性的(即坐标和颜色包含相同的值),因此我使用游戏在相机编辑器中提供的预设来修改相同的场景。





最后一帧


在单独的缓冲区中创建主框架后,将呈现UI。 这样可以确保无论为后缓冲区选择的大小如何,UI始终将以本机窗口大小呈现清晰美观的画面,同时游戏可以根据需要更改分辨率以确保速度。 最后,两个纹理基于UI alpha通道数据混合在一起,然后渲染到最终的帧缓冲区中,准备在屏幕上显示。



希望您喜欢我的分析。 我要感谢Adrian Correge所做的出色工作,激发了我学习图形的能力,并感谢Monolith工作室的工作人员为这款真正令人难忘的游戏做准备。

Source: https://habr.com/ru/post/zh-CN430518/


All Articles