哈希游戏- 哈希游戏平台- 哈希游戏官方网站
同学们下午好。今天我给大家带来的题目是《龙族幻想》材质系统的优化,以及在天气系统中的应用。《龙族幻想》是祖龙娱乐在2019年推出的一款使用UE制作的次世代手机游戏。它也可以说是国内第一款使用UE在手机平台的MMORPG的次世代游戏,同时也证明了UE可以在移动端制作品质非常优秀的游戏。因为它之前的给人印象是优势主要在主机和PC平台。《龙族幻想》充分展示了UE在移动平台也是可以制作出性能优良和画面精美的游戏。
第三个是UMaterial Instance,当UMaterial写好之后呢,可能里面有很多参数比如一个float值,还有贴图。但是如果不用Instance,而直接用UMaterial的话,那么参数是固定的,在运行期的话是没法改的。当你把一些参数导出成Parameter参数的时候,你可以创建一个UMaterial Instance的类,再通过这个类赋值。在运行期的时候,可以通过接口来修改参数。这样的话,其实可以做到UMaterial共享,不同的参数有不同的UMaterial Instance,设置不同的参数。底层使用同样的一个UMaterial类,来达到共享以及效率的提升。
FMaterial这个类包含了一个FShaderMap。他增加了些接口方便大家用。比如说我现在是一个高质量的画质,或者是一个低质量的画质,通过参数的调节,来找到一个适合的FMaterial。在拿到这个FMaterial之后呢,他会最终找到这个FShaderMap。拿到FShader Map之后呢,你可以通过刚才说的FShaderType或者是顶点结构factory去找到真实的渲染需要的Shader。
这个数组记录着很多ShaderMap,它是做什么用的呢?比如说我们在游戏里面,在系统设置里面,会选择高画质低画质。每一种画质每一种质量级别,对应着Loaded Material Resources这里面的一项。比如说游戏里有3种级别,高中低的画质。对应的数组里面就有三项,每一项都是一个ShaderMap,每一项最终都会得到一个ShaderMap,每一个FMaterial Resources里面他表示的一个质量等级。
首先我们加了一个QualityBias,这个Bias就是一个偏差。我们可以设置一个全局的级别,因为当你在系统设置页面,你设置的可能是一个最高的精度,也可能是最差的精度,不管什么精度,可能你的机器差一点,设计个中等精度。那么我们通过材质的QualityBias,这个偏差值,来叠加在你设置的这个级别上。比如说0的话,你在系统设置里面选的中精度,渲染还是中精度。如果是1的话,它的这个质量级别应该会高一个等级,中就会变成高。当然这个中是不是变成高,这取决于你代码里的具体数值,看你们怎么去改引擎。
为什么放Primitive Component呢?因为他静态的Mesh和蒙皮动画都是这个类的子类。所以我把接口放在这个类上面。那么第一步需要改的,就是当你调用这个接口的时候,我们发现当你设置的Material它不是一个Dynamic Material的时候,一般情况下美术做的时候它都会是一个Instance,美术做不出来Dynamic,Dynamic只是程序运行期生成的。一般都是Material Instance这么一个类,它在这个类上是存不了数据,存不了我自己自定义的质量等级的。只有Dynamic这么一个类,它上面会存着我的材质的质量等级。
如果你不选这个勾的话,这些东西它是存在这个UMaterial里头的,编译完以后存在那个文件里面。如果你勾选这个勾,因为你不同的UMaterial生成的ShaderCode有可能是完全一样的,为了共享,UE就把它放到了一个超大的文件里。因为ShaderCode很多,有几万个,他会放在一个很大的文件里面去。这个UMaterial文件和那个UMaterial文件,变出来的可能是一个完全相同的ShaderCode。
但是这种优化,和我们《龙族幻想》的方式还有点冲突。所以我们在新版里面,我们重新把这个大的文件分拆成之前的版本,一个ShaderCode是一个文件,文件名是它的哈希值。因为很多的游戏公司都有自己的虚拟文件系统,当面对UE的这么一个很大的文件的时候,其实是没办法使用自己的虚拟文件系统来做这个热更新的。现在这个ShaderCode按照《龙族幻想》的级别应该是在四百兆左右的大文件,如果是用UE的方式组成一个大包,那更新量是很大的。所以我们把它分拆了。
分拆了之后若干个文件,我们需要使用自己的文件系统。我们改变了哪些ShaderCode文件,就单独更新那些文件。然后在这个FShaderCodeLibrary这个类,也做了修改。这个类的话,原来它的实现方式是你打开一个文件,首先打开那个library,那个library里面是一个很大的文件,里面再去逐个地打开读取ShaderCode。那么我们现在就是去打开单个ShaderCode文件的时候,我们就直接打开某一个文件,然后把里面的文件内容读出来。它的好处就是我们只会下载单独的ShaderCode的更新包。
限于时间关系,我给大家简单介绍一下在渲染优化上做的一些事。UE原生的FShader没有卸载的机制,加载进来之后就永远留在那个地方,我觉得其实是一种效果浪费。因为可能FPS游戏的Shader类型不是很多,当我们做《龙族幻想》的时候,对渲染效果要求很高。再加上我们加了很多质量等级去实现天气效果,FShader的量就会很大,这个时候如果不卸载FShader的话,就会带来很大的内存开销,因此我们做了FShader的释放。
这个原理其实也非常简单,一个FShader创建出来会使用引用计数记录在多少个地方使用,在切场景的时候我们会统一过一遍,我们会检查一遍这些所有的FShader。如果它的引用计数为零了,我们就会把它释放掉,需要放到渲染线程,或者是RHI线程里面去释放这个Shader。在不切场景的时候,我们也会时不时做一下,但是我们在切场景的时候,在Loading条走的时候,肯定要做一遍释放检查。因为loading的时候卡一下的话也没什么关系。
还有就是我们游戏运行Loading完了之后,我们需要播一个CG,那个CG有很多也是游戏不太用到的资源,它也会卡一下。我们还是想用引擎的PSO功能,记录的功能,然后把它预热一下。但是一个不够,尤其是出现怪物的时候。然后我们就做了一个录不同的PSO的cache。PSO cache需要录渲染所有的参数,Shader,各种参数都录下来。录下来之后当你需要播这些、需要画这些文件的时候,它会在后台给你把这些东西跑一遍。这样的话,当你真正渲染模型的时候就不会卡顿,我们做了多个这样的文件。比如说,在这个Loading条结束的时候,我们需要播CG,那在Loading条结束的时候,就加载这个场景所对应的记录好的文件。当这个CG播放的时候就会非常的平滑,没有一丝的卡顿。对于boss也是这样,快到播boss的时候,我们也在后台把这个cache文件加载进来,做一下这样的预热,就会达到非常好的平滑效果。今天的分享大概就是这些,非常感谢。