Origin
mp.weixin.qq.com
Tags
Unity
风格化
渲染
经验分享
项目
米哈游
收藏夹
创建时间
收藏类型
Cubox 深度链接
更新时间
原链接
描述
在 2020 年 Unity 线上技术大会中,米哈游技术总监弋振中以 “从手机迈向主机” 为题,分享了他们的最新作《原神》针对主机平台的渲染技术要点,以及解决思路、方案。
需注意的是,《原神》在不同平台面临的具体技术难点或有不同,但透过面向主机平台开发时的技术要点,也能窥见米哈游在这款游戏上深挖的技术方向。话不多说,以下为弋振中的演讲内容。
大家好!今天我演讲的主题是从手机走向主机,《原神》主机版渲染技术分享。简单介绍一下《原神》,它是一款开放世界 RPG 游戏,有独特的二次元画风,具备跨平台、长期运营,长期更新等特质。
稍微介绍一下自己,我叫弋振中,有十多年的主机游戏开发经验,毕业之后加入了 Ubisoft Shanghai,2012 年去了加州的 Zindagi Games,为当时还没有上市的 PS4 做一款独占的游戏。然后去了纽约的 Avalanche Studios,参与制作了《正当防卫》的 3 代和 4 代。回国之前的最后一站是在西雅图的微软 Xbox。2019 年初我回到上海,加入了米哈游,组建了研发团队,目前负责《原神》的主机平台开发。这是我参与制作过的一些项目。
今天,我首先会介绍一下《原神》主机平台的基本情况,然后按照我们开发的时候改造渲染管线的思路,选择部分的技术点进行更深入的分享。希望从渲染的角度,让大家对于我们如何将《原神》带到主机平台有大致的了解。最后是分享一点我个人的开发体会。
Unity 是我们的游戏使用的引擎。这是一个灵活度很高的引擎,代码风格很简洁,所以我们能够更方便地定制化开发《原神》的渲染管线。Unity 中国的技术支持团队很配合我们,在此对他们表示感谢!
PS4 作为游戏的主机,硬件架构可以说是为游戏开发量身打造的,主机开发过程当中大量的精力是花费在如何更好地利用硬件特性上,也积累了不少我们认为还可以的技术实现。不过因为索尼 NDA 的原因,今天的分享就不涉及相关的内容,也不涉及底层的优化。前面也提到今天的分享,主要是针对渲染管线的,所以也不涉及 CPU 和其他的模板。
下面是主机渲染管线的简介,首先我们有非常强大的引擎团队,在 Unity 上面为《原神》进行了深度的开发。主机平台和手机平台采用了不同的渲染管线,但是游戏的基调是一致的,都是基于 PBR 的风格化渲染。《原神》的主机平台开发起步时间稍晚于手机平台,在平台管线搭建好以后就进入了同步开发的节奏。资源的制作、功能的开发等多个方面都需要兼顾到多个平台的情况。
基于 PBR,是为了让整个大世界的光影效果保持统一,因为我们的光影都是实时计算的,有 24 小时的循环,有动态的天气系统。PBR 能够确保不会在不同的光照条件下出现脱离预期的渲染效果,作为风格化的游戏,也需要根据美术的需求修改不同的材质。
《原神》在 PS4 上的分辨率是这样设置的,PS4 pro 上面是原生的 4K 分辨率,在 PS4 的 base,我们把 1440P 作为渲染分辨率,最后输出到 1080P,这样我们得到的最终画面会更加清晰。作为《原神》为主机开发的功能,我们大量使用了 compute shader,compute shader 有很多很好的特性,而且在支持 Async compute 管线的平台上,我们还能够进一步隐藏开销。
《原神》的风格化渲染是非常独特的,因此美术对于图形功能的要求也和写实类游戏不一样,尤其是光影效果,大家可以看到脏、黑、死、焦、噪这些词都频繁地出现在美术和程序的沟通当中。
下面我们要提到的所有的技术,都经历了很长的磨合期,有一些甚至还在磨合当中。在经历了反复的打磨和修改,直到美术对最终效果满意,程序对最终实现方案的性能也满意的情况下,我们才会大规模铺开制作。
在介绍具体的功能之前,先跟大家讲一下在 PS4 平台刚开始开发的时候,我们面临的一个状况。首先是我们有一个已经针对手机做了大量开发的 Unity 引擎,这就意味着「简单的切换平台就想让游戏能够在 PS4 上跑起来」,是不可能的了。
很多项目之前做的改动,在实现的时候也没有考虑到主机平台的特性。再加上各种计划为 PS4 开发的图形和游戏的功能,这都意味着大量的工作量。另外还有 TRC、索尼账号等等一系列 PS4 独有的问题需要解决,工作量和工作难度都非常大,然而我们能够给主机开发的资源又很有限。
一开始的主机团队就我一个人,光杆司令,团队搭建需要一个过程。为了全球同步上市,留给我们的开发时间大概只有一年半,一年半的时间要让一个平台从无到有,还要达到一定的品质,这期间还要准备 ChinaJoy、TGS 等展会,能够顺利地完成这一切,真的是非常感谢《原神》的整个开发团队的努力,非常的不容易。
接下来讲一下我们在渲染管线做改动的时候一些思路,因为开发的时间很紧,所以在选择技术改造点的时候,遵循下面几个原则。
第一个是关于功能的选择,我们首先排除掉开发周期长的,需要过多前期研究工作的功能,因为我们没有时间。
然后根据游戏的美术风格,我们选择一些对于画质的帮助更大的地方去做提升。另外因为时间不多,所以我们希望新加的功能能够更多地发挥作用,所以最好是能够在相互之间产生互动,这样会显得画面更加系统化,得到的画质提升也会有 1+1>2 的效果。
具体来看一些技术点。首先我们从场景的光影方面选择了几个技术点,主要是侧重一些方法,会稍微涉及到一点点的优化思路。
第一个是关于方向光的阴影,《原神》的大量游戏时间是在室外,室外方向光的阴影质量非常重要。一方面近处的阴影细节需要更加细腻,才显得画面更干净。另外一方面是阴影覆盖范围需要足够大,因为游戏的可视距离非常远。
《原神》的阴影范围是 800 米。大家可以看一下这张贴图,即使在远处墙壁上一小片的绿植产生的阴影,在放大之后都能够看到树叶的轮廓。这个地方放大之后,能够看到树叶轮廓,而且非常稳定。整体上来说,我们对于《原神》的方向光阴影的质量是比较满意的。
我们阴影的技术还算是比较常规,使用了 Cascaded shadow map 加上基于 Poisson disc 的 soft shadow,我们游戏没有使用通常的 4 级 Cascades,而是用了 8 级,这属于大力出奇迹的方式。大力出奇迹带来了更好的阴影效果,当然也带来了更多的性能开销。更多的 drawcall 会带来 CPU 开销,更多的 Cascades 也会带来 GPU 的开销。
当我们把质量提升上去之后,会想办法来解决性能问题,那我们怎么去做的呢?
首先在 CPU 端,我们做了一个 shadow cache,8 级 cascades 的前 4 级我们每帧都更新,后面 4 级是采用轮流更新的方式,确保每 8 帧所有的 cascades 都能至少更新一次。每一帧的话,我们只更新 5 级 cascades。
其次,主要的工作量其实在 GPU 端,用了 8 级 cascades 以后,我们的 screen space shadow map 的开销长期是大于 2 毫秒的,在某些情况之下能够超过 2.5 毫秒。GPU 比 4 级 cascades 的情况下,爆涨了 0.5 到 0.8 毫秒。
我们的软阴影采用的是泊松分布的采样,而且每个像素会去做一个旋转,来消除重复的 pattern,这一整套的操作都是很重量级的。但是我们仔细想一想,真的需要对每个像素都要做这么多操作吗?
所以我们的优化思路,是尽量只在必要的地方做软阴影计算,我们会生成一张 Mask 贴图,在贴图里面标出阴影、半影和非影片区。阴影区和非阴影区只需要直接返回 0 和 1 就好了,只有在半影区才会去计算软阴影,通过这种方式,我们的 GPU 开销大致减少了 30% 左右。甚至比采用 4 级 cascades 还要再快一些。
大家可以看一下这张图,图里面被红色标注的区域,就是我们的半影区,这个是需要我们去做软阴影处理的区域。其他的区域,就是在阴影区域或者是非阴影区域,我们直接返回 0 和 1 就好了。大家可以看出来,绝大部分的像素都可以去掉软阴影计算这个繁琐的步骤。
那么这张神奇的 Mask 贴图是怎么生成的呢?这张 Mask 贴图的分辨率是屏幕分辨率的 1/4×1/4,也就是说一个 Mask 值对应的是一个 4×4 的 block。然后我们对 4×4 的 block 里面的每一个像素,来判断它是不是在阴影中,最后汇总成一个阴影、半影和非阴影的三个状态,保存到 Mask 贴图里。这样我们能够得到一个准确的半影信息,但是它不够快,所以我们做了进一步的优化,只选择 4×4 这个 block 里面很少的几个像素,来判断是不是在阴影当中。
这几个像素的判断结果,就代表了整个 block 的信息,显然这样会出现一些误差,因为我们是拿几个少数几个像素的结果来代表整个 block,所以我们把这样计算得到的 Mask 贴图做了模糊处理,让半影的区域稍微扩散出去。整个 Mask 贴图的生成,包括模糊处理大概的开销是在 0.3 毫秒左右。
大家可以看一下对比图。优化出来的效果非常好,肉眼可以说是看不出任何的区别。这样优化完之后,我们的 GPU 开销时间大概稳定在 1.3 到 1.7 毫秒。
把阴影搞好以后,下面我们来看看 AO(Ambient Occlusion 环境光遮蔽)。大家可以考虑一种情况,就是人物和场景的物体都已经处在山或建筑物的阴影当中,这个时候人物和物体的投影跟山和建筑的投影是融为一体的。这种情况之下,画面缺乏对比,人和物体就会显得浮空。
为了解决这个问题,我们在游戏里面采用了多种的 AO 技术,针对不同的情景生成不同的 AO。首先我们使用了 HBAO,这是一个比较常规的实现,能够提供一些比较细节的 AO 效果。其次我们对静态物体和动态物体分别采用了 AO Volume 和 Capsule AO 这两种技术。
大家可以看一下这是 HBAO 开关的对比图,效果还是很明显的。
下面这个是 AO Volume 的开关情况,大家可以重点看一下我们在红圈里面的区域,椅子对地面产生了柔和的投影。
和 HBAO 相比,AO Volume 能够产生更大范围的 AO。它可以针对类似桌子或者椅子产生大面积 AO。因为技术原理和性能的限制,HBAO 是没办法产生这种效果的,AO Volume 这个时候就体现很好的补充。
要实现 AO volume,首先我们是在离线的时候对需要产生 AO volume 的物体做一个遮挡信息的计算。这个计算是在物体的本地空间(Local space)去做的,生成的遮挡信息我们保存下来,在运行的时候注入到 volume texture 中去使用。这个技术在 2012 年 GDC 关于《InFamous 2》的讲座上有提到过,大家有兴趣可以去看一下。
下面是关于 Capsule AO 的对比图,大家可以重点看一下屏风和地面,被我们红色的圈给圈出来的区域。大家可以看到相邻在屏风和地面,能够产生出能够反映体形和人影的投影。而且如果在游戏中大家去观察的话,随之相邻动作的改变,阴影的形状也会随之产生变化。
我们前面提到 AO Volume 主要是针对静态物体的,因为遮挡信息是通过离线计算的方式保存下来。像角色这种带骨骼动画的,是不能采用这种方式的,因为形状会不停地发生变化。
Capsule AO 的做法就是用一些胶囊体包裹住人物的四肢和躯干,这些胶囊体和角色的骨骼动画绑定进行同步更新。然后这些胶囊体会被用来做遮挡计算,计算的时候我们把它分为无方向的环境遮挡计算,以及带方向的遮挡信息计算。带方向的遮挡信息计算采用的方向是主光源方向和法线进行混合之后的得到的虚拟遮挡方向。通过这种方式,角色可以同时在周围的墙和地面等投出多个阴影。
下面是一个关于 AO 的优化技巧,《原神》的 AO 都是在 1/2×1/2 分辨率的 RT(Render Texture)上去做计算。为了保证画面的干净,我们对 AO 还做了一个模糊处理(blur)。然后再 Upsample 一个全分辨率的贴图上面去。所有的模糊处理和 Upsample pass,我们都用了一个 Bilateral filter,确保不会有无效的 AO 渗透到周围的区域。
从前面的描述可以看出来,模糊处理和 Upsample 加起来一共有三个 pass,这就意味着 AO 需要被读取和写入多次。而且你如果你了解 Bilateral Upsample 的话,大家可以知道相邻的像素之间有很多的计算其实都是重复的,所以我们采用的优化方式是将所有的计算都放到一个 compute pass 里面去做。然后通过 LDS 来保存 blur 的中间值,通过同时输出四个像素的方式,来重用相邻像素的计算。最终我们还可以通过 async compute pipe 把性能开销进一步降低。
关于我们的 Local Light,我们在游戏里面采用了 Clustered deferred lighting。我们支持是视野内同时出现最多 1024 盏灯。大概的做法是我们将屏幕分成 64×64 像素的 tile,然后每一个 tile 在深度的方向上面继续分为 16 级的 clusters。通过这两张图,可以大概看出我们能够支持多少灯。
这张图是一个游戏里面的截图,是一个典型的通过 Local Light 的阴影提升画面效果的情况,多个不同的 Local Light,它们的照明范围是交错存在的,然后角色也投下多个不同的阴影朝不同的方向,画面就显得细节很丰富。
我们怎么做的呢?我们的 Local Light 阴影系统支持接近 100 盏灯的实时阴影,理论上我们可以支持更多的,不过这已经很够用了。阴影的分辨率是根据优先级和距离进行动态调整,最终的阴影是通过烘焙的静态场景阴影和实时生成的动态场景阴影结合得到的。
游戏里面有很多的 Local Light,如果每一个 Local Light 都去烘焙它的 shadow texture 的话,会占用的硬盘空间非常大。而且因为是深度贴图,所以不能够随便使用 BCn 的压缩,那样瑕疵会非常风险,所以需要一个好的算法来对于烘焙的 shadow texture 做一个压缩。这个压缩需要在精度损失足够低的同时,还要保持压缩率足够高,同时我们的解压开销要非常小才行。
我们开发的这个系统是在离线制作的时候,对于 shadow texture 做一个压缩,尽量地去保持精度,运行的时候解压的速度也非常快,用 compute shader 去解压的情况,1K×1K 的 shadow texture,我们解压只需要 0.05 毫秒,可以说非常非常快。
那压缩率和压缩质量呢?我们先介绍一下压缩的算法思路。首先我们对于 shadow texture 按照一个 2×2 的 block 来进行编码,每 4 个深度值,我们用 32bit 来保存。如果想要降低精度损失,可以选择高精度压缩,这种情况之下每个 block 的大小变成 64bit。
编码的方式有两种,一种是基于深度平面方程的方式,或者是通过压缩的浮点数方式。编码完成之后,还要进一步通过一个 quad tree 来合并编码以后的数据,进一步提高压缩率。quad tree 是每个 tile 要保存一个,而每个 tile 又包含了 16×16 个 block,大家可以看到下面的三个图,从左到右分别是没有压缩的深度贴图,中间是我们的平面方程编码的视图,最右边是我们 quad tree 0 到 4 级的深度视图,黑的地方是深度为 0 的区域。我们参考了 Li Bo 在 2019 年 Siggraph 上面的讲座,大家有兴趣可以去看一下。
压缩比:在一个典型的室内场景默认精度压缩比是在 20% 到 30% 左右。如果开启高精度模式压缩的话,大概默认精度压缩到 40% 到 70%。阴影贴图的压缩是非常必要的,可以帮我们容量下降一个数量级。
大家可以看一个对比,这是默认精度压缩,能够看到红圈里面有一些瑕疵。这实际上是我们找到的可以说是最差的一个情况。
这是高精度压缩,基本上看不出任何瑕疵来。如果把高精度压缩的图和不压缩的情况做对比的话,其实肉眼是看不出什么差别,所以没有放这个图。
上面的图是一个 2K×2K 的 shadow texture,大小如果不压缩的话是在 8MB,默认精度压缩大小变成了 274.4KB,压缩率是 29.85%。如果替换成高精度压缩,就是肉眼看不出差距的压缩,贴图大小变成了 583.5KB,这种情况的压缩率还是有 14% 左右,所以还是相当不错的。
在搞好了 Local Light 以后,我们接下来为游戏添加了体积雾,体积雾是可以接受 Local Light 的照明影响,在灯的影响范围内形成一圈光晕,可以极大地提升画面的体积感。大家可以看到图里面近处的灯笼周围会有一圈泛光。包括画面远处的建筑物,因为笼罩在灯光下会使得周围的体积雾也被照亮,而显得有一丝的朦胧。
这个图是一个更有意思的情况。如果我们给 Local Light 加一个 projection texture,也就是我们通过这个贴图来控制 Local Light 光照的形状,就像右边这样,体积雾也会产生相应的变化。
我们的体积雾的计算是基于物理的。我们也支持通过不同的参数让体积雾在大世界里面的不同区域有不同的表现。体积雾支持 Local Light,这个在前面已经展示过了。为了让体积雾更加稳定,画面更加细腻,我们给体积雾添加了 Temporal filter,进行了多帧的混合。整体的 GPU 开销,也控制的不错,在 PS4 Pro 下面大致在 1 毫秒甚至更少。
大致的实现是这样的:首先是基于相机空间,我们把 view frustum 分成很多的 voxel,这些 voxel 跟我们前面提到的 clustered deferred lighting 的 clusters 是对齐的,这样方便我们在后面对 Local Light 做 scattering 计算的时候进行一个加速。
前面提到的体积雾参数和 Local Light 的信息,都会被注入到这些 voxel 里面去,然后我们通过 Ray marching 的方式去计算体积雾。在这个时候,Local Light 的信息就自然而然被考虑进去了。
有了体积雾,我们不得不提到 God Ray 效果,首先大家可以先看看游戏里面 God Ray 的表现。
对于方向光进行遮挡就可以产生 God Ray 的效果,我们的做法是有一个单独的 pass 来生成 God Ray,然后是在 1/2×1/2 分辨率下面。God Ray 也是通过 Ray marching 的方式去生成的,我们会去采样 shadow map,但是最多会采样 5 级的 cascades。
God Ray 生成完之后,我们会提供美术一些可以调整的参数,然后将 God Ray 的结果叠加到体积雾上面去。它在使用上面,并不是一个物理上正确的东西,但是它的效果是能够让美术满意的。
了解体积雾的人可能会有一个问题,为什么要单独使用一个 pass 呢?体积雾本身就可以产生 God Ray。这个就是一个很好的技术和美术磨合的例子,我们有体积雾直接生成的 God Ray,在游戏里面实际效果其实不能够让美术满意。原因有两点,第一是分辨率不够,因为体积雾的分辨率是靠 Voxel,而我们的 Voxel 是不会划分的特别精细的。第二是因为体积雾生成的 God Ray 强度是完全依赖于体积雾的浓度。
要想得到很明显的 God Ray,就需要雾的浓度提的非常高。雾的浓度一旦提高了,画面就会显得不通透,太脏,这就是回到前面提到的两组词,这是美术不能够接受的。所以我们是采用了单独的 pass 去生成 God Ray,这样可以得到更锐利、更清晰的效果,美术调整也更灵活。美术想要什么,我们就给他做什么。
下面给大家看看对比图,大家就能更好地体会到我说的是什么意思。这个 God Ray 就是通过体积雾生成的,包括整个画面的表现。
这张是我们游戏里面现在使用的方式,就是单独的 pass 去生成,做一个对比。
大家可以看到第一张 God Ray 不是很明显,而且画面雾的浓度非常高。而第二张图,God Ray 会更清晰一些,而且整个画面是更加干净、更加通透,这就是美术想要的效果。
接下来是 IBL(Image Based Lighting)系统,大家先看一下演示视频。图中左边的是 Reflection probe(反射探针),右边是 Ambient probe,随着 24 小时的变化,我们的 Reflection probe 和 Ambient probe 的内容也会跟着变化。
我们先看一下左边的 Reflection probe,Reflection probe 是用来给场景提供反射信息的。因为游戏的光影不断变化,我们是不能够简单地为反射探针烘焙一张环境贴图作为反射信息之用。所以对于每一个 Reflection probe,我们是烘焙了一个 mini GBuffer。产生在游戏当中,根据当时的光照条件去实时生成环境贴图,美术可以在游戏里面摆很多个这样的 Reflection probe,只要他们需要。
然后在运行的时候,我们会去更新场景的 Reflection probe 的 cubemap。整个过程我们大致分为三步,第一步是 Relight,第二步是 Convolve 和 Compress。我们使用 Compute Shader 去同时处理六个面,然后分帧进行,同时只处理一个 probe,不停地做循环。
第一个步骤,就是 Relight 步骤,大家可以通过图能看出来,就是一个简单的把当前的光照环境用来照亮 mini GBuffer,得到环境贴图的过程。
然后生成的环境贴图,需要经过 Convolve 这一步,得到 mipmap 的正确信息。最后这个贴图需要再通过一个 Compute Shader 的做法,压缩成 BC6H 的格式,然后送到渲染管线里面去使用。大致是这么三步的过程。
下面是我们的 Ambient probe。Ambient probe 也是实时生成的。
我们在做完 Relight 以后,Reflection probe 是包含了当前的整个光照信息,我们可以从中提取出当前的 Ambient 的信息,并且把它转化成一个 3 阶的 SH(Spherical Harmonic)系数保存下来。这个提取的过程,在我们把 Reflection probe 处理完成以后会自动进行,也是同时使用 Compute Shader 来处理六个面。
这么看下来,我们整个系统算是完成了,但实际上里面有很多地方是可以改进的。
第一个是 Relight 是没有阴影的,因为单靠 mini GBuffer 我们是没有办法在 Relight pass 生成阴影,这样会导致一个很大的问题。就是在 Relight 完成得到的环境贴图是漏光的,本来应该处于阴影当中的地面也会变得非常明亮。
通过这样的环境贴图算出的环境光(ambient)也会出现有问题的情况,那怎么解决呢?我们的做法是,我们把 24 小时的 shadow 都烘焙下来,就是隔一段时间我们烘焙一下,把 shadow 转化成一个 shadow SH 保存起来。在运行的时候简单通过当前的时间对 shadow SH 进行插值,用来压暗 Relight 以后的结果。
这样得到的效果是出乎意料的好,而且我们需要保存的数据非常的少。因为 shadow SH 很糊,所以我们做插值也没有什么大的问题。
同样的方式,我们还可以把 Local Light 的信息也保存下来,作为 Local Light 的 SH 在 Relight 的时候也加上去,这样可以得到非常好、非常廉价的一个 Local Light 反弹的效果。
大家可以看一下对比,这张图是没有添加 shadow SH,这张是添加的,大家可以看到没添加的情况之下,屋檐和地面都莫名其妙的亮,添加之后就能够看出来是在阴影当中了,所以效果是很明显的。
下面是我们把 Local Light SH 加进去的情况对比。这是没有添加的,大家注意看一下画面右上角那片屋檐下面暗的区域,这是没有添加 Local Light SH 的情况。
这是添加了的。那块区域被照亮了。
我们现在已经解决了漏光的问题,并且添加了 Local Light SH。接下来是一个室内室外光照环境不一致带来的问题,因为室内和室外的光照环境往往是很不一样的。如果不加区分的话,室内外的环境光(ambient)混在一起,得到的效果就很容易让人觉得不对劲。
我们是把 Reflection probe 分成室内、室外两种,然后美术通过摆放一个室内环境用的网格(interior mesh)来标记受室内光影响的像素。Ambient probe 也会相应地为室内、室外生成不同的环境光。
下面看一下对比,这是没有开的情况,如果不区分的话,室内跟室外一样都会受天光的影响而变得很蓝,然后做了室内(interior)标记,室内的像素就能够正确地反应出室内的光照条件,会显得更黄一些。
而且我们还做了一个过度的处理,就是在门口这个区域当室内光照和室外光照环境切换的时候,不会出现一个因为明显的光照差异不一样而产生的硬边的效果。
下面这个就是室内环境用的网格(interior mesh)生成的 Mask 标记图,红色区域就是室内的区域,大家可以看一下对照关系。
除了通过 Reflection probe 得到的反射,我们还有 Screen space reflection 来提供实时的反射信息。
SSR 在 PS4 Pro 上面的 GPU 开销大概是在 1.5 毫秒左右,我们对 SSR 也加了一个 Temporal filter,通过当前帧的 SSR 信息和历史信息混合起来,来提高 SSR 计算结果的稳定性,让画面也更平滑一些。为了得到更多的反射信息,我们为 SSR 生成了 Hi-Z 的 buffer,我们可以让每条射线通过 Hi-Z 最多能够跟踪的距离达到整个屏幕。
下面是一个 SSR(Screen space reflection)效果开关的对比图,在比较光滑的地板上效果尤其明显。
从前面的截图大家也能够看到,在没有 SSR 的情况之下,我们还有 Reflection probe,它也是可以提供场景的反射信息。我们是使用了一个 Deferred reflection pass 来计算 Reflection 和 Ambient 信息。在计算 Reflection 的同时,我们把 AO 信息也考虑进去,这样可以有效地降低漏光。
接下来是我们的最后一个技术点,HDR Display。这里面的 HDR Display 包含了两个方面,一个方面是指亮度,需要使用 PQ ST2084 的 EOTF,最高是能够让画面亮度达到 10000 nits。另外一方面就是在色彩空间这边,我们需要支持 Rec.2020 色彩空间,Rec.2020 色彩空间和现在普遍的电视机使用的 Rec.709 相比,它可以显示的色彩范围要大得多。
大家可以看看在 CIE 1931 色度图里面的覆盖范围对比。Rec.2020 色彩空间覆盖范围大概能达到 75.8%,相比之下 Rec.709 只能在 35.9% 的样子。
在这里我们称使用了 ST2084 和 Rec.2020 色彩空间的渲染管线为 HDR 管线,而使用的 Rec.709 色彩空间的非 HDR 管线,我们把他们叫做 SDR 管线。关于 HDR Display 很多基础的信息,有很多人都已经讲过在这里面就不细讲了。下面主要讲一下,为了让这个技术放到《原神》里面去,我们做了哪些调整。
下面这张图是《原神》的 SDR 和 HDR 的管线对比图,和 SDR 管线相比,HDR 管线没有了 tone mapping,color grading 变成了 HDR 的 color grading。而代替 tone mapping 的是 RRT+ODT(reference rendering transform + output display transform)的组合,这就是很多人熟悉的 ACES 调色。
另外,UI 在 HDR 下面,也是单独画到一张 RT 的。然后再跟场景做一个合并,这是因为 UI 的亮度处理方式跟场景是不太一样的。在图上大家看到 RRT+ODT 是灰掉的,我们待会会再来细讲这个事情。
《原神》从 1.2 开始,会在 PS4 上面支持 HDR10 的模式,然后替换 SDR 的 Color grading 是我们的 HDR Color grading,美术会在 Davinci 这种软件上面去做 HDR 校色。然后通过我们的脚本输出 HDR 的 Look-Up-Table(LUT)。
在运行的时候,白平衡 HDR 的 Color grading,还有我们的 Color expansion 这些操作,都会在一个 compute pass 里面,输出到一张 Color 的 Look-Up-Table 里面去。因为这个 Look-Up-Table 很小,所以尽管前面提到的这些都是需要大量的计算操作,但实际上开销是很小的,大概不到 0.05 毫秒。
前面管线图的 RRT+ODT 部分是灰掉的,虽然这个是一个主流的调色方式,但是我们并没有采用它,因为和我们的游戏风格不太搭。所以尽管主流,我们还是放弃了。
为了保证在低亮度范围内的画面和 SDR 版本的游戏一致,我们将 HDR 的渲染画面和 tone mapping 处理之后的画面做了一个基于亮度的混合,然后在亮度不高的地方,就尽量保证了 filmic tonemapping 的关于 toe 部分的处理。
下面说一下在低亮度范围内一致性的问题,这里面涉及到 OOTF 的事情。前面我们提到在 HDR 上面是用了 BT1886 作为一个 EOTF 曲线,然后在设备处理游戏输出的时候会用到,相应的游戏在输出信号给电视机的时候也会增加一条曲线,这个就是 OETF。然后在 HDR 的管线里面,实际上就是一条 gamma 曲线。
这里面存在一个问题,就是 1886 的 gamma 是 2.4,但是 SDR 的 OETF 是一个分段函数,大致上可以看作是 gamma 2.2,这就出现了一个问题,也就是在 PPT 上写的问题。OETF 处理完的颜色,经过 EOTF,它得到的并不是它本身,就产生了一个误差。
但是在 HDR 下面,因为我们 OETF 和 EOTF 是被很好的定义了的情况,所以他们是互逆的,于是这个颜色在经过这两个处理之后能够得到原来的颜色,所以在 HDR 下面没有这么一个问题。
而大家已经习惯了在 HDR 下面有这么一个误差的画面,所以为了模拟这个误差,我们是在 HDR 管线里面添加了 OOTF,把差异给补上去了。
做完这些,是不是 HDR 就彻底没问题了呢?并不是。这还有一个大坑,叫做 Hue Shift。
什么解释一下什么是 Hue Shift?举个例子,大家可以看一下游戏里面火焰制作的示意图,我们通过一个灰度图,就是下面像馒头一样的东西,加一个噪声贴图,然后进行扰动,得到了火焰扰动的纹理,然后我们用橘色去染色。
最后我们把火焰的整体亮度往上提,这个时候就得到大家可以看到的见证奇迹的时刻,在 SDR 下面因为有 tone mapping 的原因,tone mapping 在亮部是有一条曲线的,会让亮度增加逐渐变慢,于是橘色的 R 通道跟 G 通道的差异本来是很大,但是随着亮度的增加,tone mapping 曲线介入了,R 通道的增长就变慢,G 通道就逐渐赶了上来,于是就产生了 Hue Shift,画面渐渐开始发黄,于是就得到了后面看到的 SDR 下面火焰的效果。
但这是美术想要的一个效果,只不过他是通过这种神奇的方式得到的。那么问题来了:因为在 HDR 下面,我们是没有 tone mapping 的,Hue Shift 是不会发生的。它叫做 Hue Preserving。R 通道跟 G 通道的比例关系是得到一个保持的,所以能够得到亮度非常高的橘色。但是在视觉上,不会让人家觉得很明亮,因为没有黄色。
那怎么去修改呢?一种常用的方法,也是很多大作都用的方法,叫做黑体辐射。这是一个基于物理的算法,美术去指定温度,根据温度计算出应该是什么颜色。通过这种方式,我们是需要修改美术的资源。不过我们没有采用这种方式,为什么?一方面是因为 HDR 这个功能是在很后期才加入的,我们不能够让美术去大量修改已有的资源。
另外游戏并不是写实类的游戏,我们的火可以是各种颜色,而且还有很多其他的特效是随着元素反应去转换颜色的。所以我们自己搞了一个方法,我们在 shader 里面去模拟了 Hue Shift,并且把模拟放到了 color grading pass 里面去,合并到 Look-Up-Table 的计算中。
这样的好处是,首先我们不需要修改任何的效果,得到的效果非常满意,我们不仅仅是让火焰特效在 HDR 下面的效果跟 SDR 几乎一致。而且因为通过引入 tone mapping 的方式模拟了 Hue Shift,所以我们前面提到的在非 HDR 的亮度部分的画面一致性的问题,也被顺手解决掉了,不需要跟 tone mapping 处理之后的画面做混合。而且这个操作,也是在生成 Look-Up-Table 的时候去做的,所以它的性能增加是可以忽略不计的。
技术介绍我们就到这里介绍了,下面是我一些个人的总结和感想。
首先是全球玩家对《原神》主机版的接受程度之高是出乎我们的预料,非常诚惶诚恐,也非常的感慨。从零开始,我把 PS4 版做起来,看着它逐渐地变化,就像看着自己的孩子逐渐长大一样。随着 PS 版越做越完善,很多的小伙伴就逐渐加入了开发队伍中,于是 PS4 版变成了大家的孩子,大家努力让它变得更好,最后就怀着忐忑的心情送出来跟全球玩家见面。没想到现在这么受欢迎,所以是一件非常有成就感的事情。而且很开心,能够和这么好的团队一起做这个项目,后面我们也会继续努力,不断地改进,也不断地去优化,不断地出更好的内容给大家。
就像前面提到的,《原神》的主机版开发是第一次尝试,时间、资源和人才都很缺乏。通过一年多的开发,我们积累了很多的经验,尤其是如何把写实的渲染技术跟风格化游戏结合的经验,非常的宝贵!随着新的主机平台的到来,我们又面临了一大波的技术升级。不过和在美国的时候相比,国内主机开发的从业人员太少了,希望大家能够多多交流,和我们探讨一下技术,交流一下开发经验。
就我个人而言,在米哈游做游戏、做技术是一个非常不错的选择,我们也很热忱地邀请大家加入,尤其是加入主机团队,我们需要所有岗位的大佬,一起来做次世代的游戏。
最后是感谢!首先要感谢原神引擎团队的所有成员,也感谢所有为《原神》主机开发出了力的大佬们!其次还要感谢主机团队特别成员,每天被大家撸来撸去的 lulu。最后是特别感谢陈文礼、Terry liu,以及各位 Sony 全球的技术支持专家,感谢你们在《原神》开发期间对项目的大力支持!
我这次的分享就结束了,感谢大家!
游戏葡萄招聘产业记者 / 内容编辑,
点击「阅读原文」可了解详情
推荐阅读
最新的游戏专业书上架啦!点击下方小程序即可获取
点击下方公众号名片,获取游戏行业更多信息 > 本文由简悦 SimpRead 转码