一、概述
我分享的是《咫尺明月刀》端游自研引擎的体积云和体积雾的渲染零碎开发和优化。首先给大家介绍一下背景以及指标:天刀在业内始终以动静的云海体积云著称,算是最早一批利用体渲染的大型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分辨率尺寸的光照时,如图所示,绿色的像素点是以后计算的像素点,红色的区域是咱们须要重建的像素点,假如图示的红色像素须要重建,咱们思考它四周3x3的绿色无效像素进行加权插值,当然咱们要思考深度的不连续性,避免前景和背景谬误交融,如图示的红色像素须要进行重建,首先咱们会抉择出四周的3x3像素,而后依据以后像素的深度去进一步过滤深度类似的像素,比拟深度会引入一个问题,就是咱们之前说每四帧顺时针旋转像素,会造成重建时3x3的像素深度都是前景或者背景,如图所示,这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只有32x32像素,alpha通道存储介质的透射率深度,咱们最终雾效合成的代码如下,他并不是基于物理只是一种近似。
美术在游戏中须要手动管制室外室外不同的介质浓度,美术会制作房子的简模作为突围盒,用于GI等零碎。在体积雾中咱们复用这些突围盒,程序会主动的把它们体素化进Froxel,注入介质的时候依据后果抉择属性。
Q & A
Q:体积云和体积雾的光照有什么区别?
鲍鹏:次要有以下几点区别:第一点它们反对的动静光源类型不同,体积云反对太阳光、闪电,体积物反对太阳光、部分光源以及GI,另外体积云的可见性计算只有云层的体积暗影,然而会有多个Phase Function来模仿多层闪射。
Q:比表暗影笼罩间隔?
鲍鹏:地表暗影依据地表高度图解决的笼罩所有可玩区域,云层暗影的笼罩间隔能够配置,边界会应用淡出成果。
Q:Froxel的划分以及rayMarch的间隔?
鲍鹏:8×8像素是一个体素,深度上会有64层,ray march会依据地球大气圈的高度计算一个起点,对于布径有一个最大间隔限度,如果透射率大于等于一,会提前退出。
Q:天空盒的解决?
鲍鹏:个别天空根本只有程序生成的大气闪射以及体积云雾,有时美术会需要应用天空盒贴图作为背景,咱们会应用透射率以及美术可调的参数,让美术手动抉择做Blender。