乐趣区

关于程序员:​TGDC-香雾空蒙月转廊天涯明月刀的云雾大气系统

一、概述

我分享的是《咫尺明月刀》端游自研引擎的体积云和体积雾的渲染零碎开发和优化。首先给大家介绍一下背景以及指标:天刀在业内始终以动静的云海体积云著称,算是最早一批利用体渲染的大型 3D 网游,次要思维就是借鉴体绘制,利用体渲染进行光线步进,整体成果十分实在,并且性能也非常高效,近年来各类 3A 大作在这方面的改良层出不穷,提出了各种各样的新技术。

自然界中的云和雾变幻无穷,令人赏心悦目赏心悦目,这些都是星球研究所拍摄的国内各个中央的自然景观,新技术以及美术成果的各种需要使咱们有了更高的谋求和指标。

下图是整体零碎,咱们须要渲染相机中所看到的各种参与性介质,比方雾、云、大气等,在间隔相机 0 -200m 的范畴咱们应用 Froxel,就是图中黄色的体素网格进行渲染,超出 200m 的范畴应用 RayMarch 进行渲染。

这段视频演示了零碎的局部成果,咱们反对全动静的工夫天气变动,美术能够任意设置云雾的状态,玩家能够随便穿梭进入体积云和体积雾。咱们是如何对这些体积介质建模的呢?

https://v.qq.com/x/page/p3310…

二、建模

首先是体积云,为了不便解说,咱们先从一个简略的例子动手,图示的一块云是从 10000 米的高度往下看,这块云体大略笼罩了 5km*5km 的范畴,它是依据左上角的 2D 仰视角的 Cloudmap 生成的,在高度上没有变动,咱们须要引入一张高度 LUT 贴图来管制他在高度上的变动。

左上蓝红色的竖条是以后的高度 LUT,云层在高度上便有了变动。

最初咱们退出 displacementMap 以及 PerlinWorley3D 贴图来刻蚀云体的边缘以及细节。两张贴图如上图左下角所示,最终云体模型会变得更有档次,细节丰盛。

这个视频演示了 Cloudmap 变动对云体形态的影响,Cloudmap 如下图红色箭头所示。

https://v.qq.com/x/page/d3310…

这段视频演示了高度 LUT 贴图对云体状态的影响,LUT 贴图如红色箭头批示,实时游戏中 LUT 的变动会插值进行过渡, 咱们还能够应用各种程序化参数来扭转云层的状态。

https://v.qq.com/x/page/n3310…

接着是体积雾的建模,FogMap 跟 CloudMap 的作用一样。它是张 2D 贴图,笼罩可玩区域,R 通道在 Houdini 中以地形高度图为根底微调,G 通道在 Houdini 中依据噪声生成,对特定区域进行手工编辑,B 通道是浓度,实时程序化生成。

这段视频演示了 FogMap3 个通道变动对地图中体积雾的影响,R 通道管制起雾的高度,G 通道管制起雾的衰减,最初 B 通道管制起雾的浓度。

https://v.qq.com/x/page/b3310…

天空大气的原理: 咱们在外太空会看到地球上有一层大气光环,在晴天咱们看到的天空是蓝色的,在傍晚时看到的天空是橙红色的,这都是因为地球上的大气所造成的景象。

类地行星上存在着粘稠的气体以及其余细小颗粒,它们形成了一种非凡介质,会与光线有交互,造成各种景象。如果空气颗粒比拟细小,短波长的蓝光会受它的影响进而散射,这就是 Rayleigh 散射,散射进来的蓝光使天空变蓝。如果是比拟大的颗粒,光线会向五湖四海散射,而它在流传主方向上散射的光比拟多,这就是 Mie 散射,咱们平时所见的雾霾就是 Mie 散射的一个例子。

上面是 Rayleigh 散射变强的比照,在黄昏太阳光线在空气中流传间隔比拟长,导致蓝光大部分散射开来,天空呈橘红色。

这是 Mie 散射变强的比照,空气中大颗粒粒子比拟多,会造成雾霾景象。

体素介质的建模:咱们有时须要一些非凡的参与性介质,它的精度要求比拟高,可能须要一些非凡的光照,它的静止轨迹能够由美术来设置。

咱们在 Houdini 中依据力场生成带扰动的风场,来驱动这些介质,用两套实时计算的偏移场来混合介质的位移。

三、渲染

渲染分成 4 个局部,首先是 Froxel 的渲染。Froxel 是 Frustum+voxel 的缩写,从字面上就能够晓得,它是将相机的视锥宰割为一个一个的 3D 体素,咱们之前说了,应用 froxel 渲染的是 200m 以内的视锥。

视锥宰割成了体素后,第一步咱们会对每个体素注入介质的属性,同时咱们对每个体素生成 shadow mask 来保留它们的可见性信息。第二步计算每个体素的光照后果,第三步将每个体素的光照后果沿相机眼帘方向进行离散化积分,失去最终的光照后果。

这个视频展现了近处 200mFroxel 的渲染后果,太阳光是从左上角打下来,场景中有多个部分动静光源,这里的体积雾的建模会依据风力随机扰动属性值。

https://v.qq.com/x/page/p3310…

接下来是 RayMarch 的介质渲染,如图所示,咱们会在 Froxel 后发射光线来持续渲染之后的参与性介质。首先咱们须要计算光线的终点和起点,而后扰动终点地位来避免一些 ray march 的 pattern,红色箭头指向的小图中,每个红点代表 raymarch 的采样地位,能够看到因为终点扰动,每个采样点并不是规定的齐步步进。

一开始咱们会应用比拟长的步长来疾速步进没有介质的区域,当咱们采样到介质时,如图所示,咱们会屡次试探来确定一个比拟好的步长,咱们在采样到介质之后,会每次减半步长来回退,如果回退还在介质中则持续减半步长回退,直至来到介质,而后再次步进介质中,如此循环直至回退数达到设置的最大值。

在介质中咱们会逐步加大步提高长,当来到介质后,咱们会从新应用更大的步长来步进。

上面咱们具体探讨下光照模型,咱们计算相机通过介质看到的最终色彩,首先要晓得背景色彩,就是图中 xs 这一点的光照色彩,思考到空气中有参与性介质,背景色彩会依据穿过介质的透射率(图中 Tr 项)做交融,最终的色彩除了背景色还有介质自身受光的色彩,咱们须要在眼帘方向上做积分来计算介质自身的色彩。如图所示,咱们有多个绿色的离散采样点,须要计算这些采样点的光照,而后再计算采样点各自的透射率,最终进行离散的积分。每个采样点的光照比较复杂,它首先有 BSDF 项(f),咱们个别用一个介质相干的 Phase Function 来表白它,其次是 Visibility 项,他来形容采样点和光源间接是否有遮挡。

那么咱们来进一步看下理论游戏中 visibility 项如何失去。它由三局部组成,第一局部是咱们相熟的 CSM,第二局部是介质的 shadowmap,它的深度是 ray march 失去,会依据 ray march 的透射率拟合一个深度,再应用 esm 和 mipmap 来进行暗影的柔和和反走样。第三局部是地形的 shadowmap,能够依据光照方向和地形高度图离线解决取得,咱们为什么要退出一个独自计算的 shadow mask volume 呢?

起因是暗影的计算因为欠采样会有很多瑕疵。如图中红圈所示,它对于最终画面成果影响很大,所以咱们思考应用 temporal 的时序累积来晋升暗影采样效率,以及稳固暗影数值。

这个视频是咱们加了 temporal 之后的成果,能够看到即便是动静的变动,画面也会十分稳固。

https://v.qq.com/x/page/b3310…

对于光源的光照计算,咱们会应用 Zbin 的数据结构来缓存多个光源的可见性信息,进行光源的剔除,而后计算以后采样点的光照色彩,以及读取以后采样点的 Visibility 可见性信息,为了画面的稳固,咱们会应用 temporal 来累积时序上的光照后果,近似晋升采样率。

然而有一个比较严重的问题,不同于 Visibility 的低频信息,光照是一个高频信息,对于全动静的光源,时序上的累积会造成两个 BUG,Ghosting 景象和拖尾景象,Ghosting 景象很好了解,当光源挪动时,对于之前体素的光照累积会造成光照的拖影,这对于手电筒,探照灯等动静光源会有很显著的瑕疵。

第二个问题是相机变动时的拖尾景象,在参与性介质各向异性属性特地强时会呈现,如图所示,当相机转换视角时,光照变动十分激烈,此时再进行时序累积,会造成拖影。

然而如果咱们因而不进行光照时序积攒,会使体素光照欠采样产生噪点与画面抖动。

所以咱们须要采取更精准的办法来解决全动静部分光源,对于一条穿过光源的眼帘能够和光源原点形成一个立体三角形,针对其中一个三角形,顺时针扭转眼帘方向来进行离线预计算,一共应用 96 种相机方向,如图所示,这样每个立体三角形会生成一个 3D 纹理,而后咱们依据角度将整个光源分为 15 个立体三角形,又因为对称性,只须要记录 8 个。

在光照的时候,咱们依据眼帘与光源的交点计算 3D 纹理坐标,进而进行采样预积分的光照进行合成。

这个视频展现了应用间接计算光照和预积分计算光照的比照,能够看到前半段的间接计算因为欠采样会有很多瑕疵,后半段的预积分能够完满解决问题。

https://v.qq.com/x/page/t3310…

最初一部分是重建合成最终的光照,为了性能思考,咱们会在 1 /16 分辨率上进行光照计算,每四帧顺时针旋转发射光线的像素点。

如图所示:第一帧发射最左上角的像素,第二帧发射右上角的像素,以此类推,在重建 1 / 4 分辨率尺寸的光照时,如图所示,绿色的像素点是以后计算的像素点,红色的区域是咱们须要重建的像素点,假如图示的红色像素须要重建,咱们思考它四周 3 ×3 的绿色无效像素进行加权插值,当然咱们要思考深度的不连续性,避免前景和背景谬误交融,如图示的红色像素须要进行重建,首先咱们会抉择出四周的 3 ×3 像素,而后依据以后像素的深度去进一步过滤深度类似的像素,比拟深度会引入一个问题,就是咱们之前说每四帧顺时针旋转像素,会造成重建时 3 ×3 的像素深度都是前景或者背景,如图所示,这 9 个采样点全是背景没有前景,所以咱们须要应用非凡 pattern 来获取最小和最大的深度范畴。

这张图是重建前的 1 /16 分辨率的光照后果。

这是重建实现 1 / 4 分辨率的光照后果。间接将 1 / 4 分辨率放大为全分辨率会带来各种问题。

在前景背景动态变化的地带,比方树叶间隙会造成重建不稳固,画面闪动。

所以咱们应用一个带深度权重的滤波核,对其进行双边滤波,并且应用噪点来扰动采样地位,并依赖前期的 TAA 来进行时序上的降噪,稳固画面。

四、性能统计

咱们曾经在天刀端游上线了第一版的大气零碎,过后应用的测试条件如图所示。

测试屡次性能取均匀,最终的后果如图所示:整体耗时在 1.4 毫秒左右,相较于它对于整体画面的晋升,游戏帧率的降落简直能够疏忽,总体性能以及性价比十分高。

最初,总结一下咱们的大气零碎,它是实时全动静的渲染零碎,能够大幅度晋升画面的品质与真实感,在整个时段和天气零碎中能够对立的整合进配置,不便美术制作、调整参数设置以及针对不同场景天气进行治理,它的性能非常高效,对游戏的帧率损失微不足道。

将来的改良方向,咱们心愿精简参数更容易达到比拟好的画面成果,以及更自动化离线制作的管线。

Bonus

此外,咱们还持全动静的时段以及天气,建模的体积云和体积雾能够依据以后的参数来程序化混合,任意变换各种状态以及光照成果。

这个视频简略的展现了从中午到黄昏,从晴天到下雨起雾的无缝动静过渡切换的过程,暴雨天气下雨水打到物体上会主动程序化生成水雾。

https://v.qq.com/x/page/a3311…

咱们复用了下雨湿润成果的 OcclusionMap,以及 ESM 深度比拟来自动化实现,视频展现了雨雾开和关的成果比照。

https://v.qq.com/x/page/j3311…

半透雾效是一个比拟辣手的问题,如红圈所示,水流这种半透物体的受雾特地容易穿帮,在浓雾中,咱们心愿玻璃这种相似材质能够齐全荫蔽在雾中。

一个折中的解决办法是在 RayMarch 的 pass,独自输入一个 Fogmap,对它进行 mipmap chain 的生成以及高斯含糊,为了性能思考这张 fogmap 只有 32×32 像素,alpha 通道存储介质的透射率深度,咱们最终雾效合成的代码如下,他并不是基于物理只是一种近似。

美术在游戏中须要手动管制室外室外不同的介质浓度,美术会制作房子的简模作为突围盒,用于 GI 等零碎。在体积雾中咱们复用这些突围盒,程序会主动的把它们体素化进 Froxel,注入介质的时候依据后果抉择属性。

Q & A

Q:体积云和体积雾的光照有什么区别?
鲍鹏:次要有以下几点区别:第一点它们反对的动静光源类型不同,体积云反对太阳光、闪电,体积物反对太阳光、部分光源以及 GI,另外体积云的可见性计算只有云层的体积暗影,然而会有多个 Phase Function 来模仿多层闪射。

Q:比表暗影笼罩间隔?
鲍鹏:地表暗影依据地表高度图解决的笼罩所有可玩区域,云层暗影的笼罩间隔能够配置,边界会应用淡出成果。

Q:Froxel 的划分以及 rayMarch 的间隔?
鲍鹏:8×8 像素是一个体素,深度上会有 64 层,ray march 会依据地球大气圈的高度计算一个起点,对于布径有一个最大间隔限度,如果透射率大于等于一,会提前退出。

Q:天空盒的解决?
鲍鹏:个别天空根本只有程序生成的大气闪射以及体积云雾,有时美术会需要应用天空盒贴图作为背景,咱们会应用透射率以及美术可调的参数,让美术手动抉择做 Blender。

退出移动版