由猫枪工作室研发,哔哩哔哩游戏代理的回合制二次元卡牌手游《非匿名指令》已经在 11 月开启了全平台公测。在近未来都市风的游戏世界中玩家需要作为“代行者”,游走于势力间的制衡与斗争,踏上暗流涌动的征途。
游戏除了优秀的剧情、丰富的玩法,还以其 2D、3D 结合的游戏美术风格备受好评。本期,Unity 邀请到了《非匿名指令》开发者团队为我们分享他们的游戏开发、优化经验。
请问定制化 URP 渲染管线做了哪些优化呢?
01 Final Blit Pass
在 URP 默认的管线中会强制在最后执行 Final Blit Pass,它的原理就是将当前渲染的 RT 最终 Blit 到 FrmeBuffer 中显示,Blit 的过程中会执行伽马矫正。当前渲染的 RT 会分是否使用后处理,如果没使用后处理就是 _CameraColorTexture,如果使用了后处理会先对 _CameraColorTexture 进行后处理,拿 Bloom 来说后处理进行中间会生成临时 RT 进行降采样等卷积操作,其他后处理原理也类似,然后将后处理结果的 RT 在执行 UberPostProcess,就是将结果在 Blit 到 _AfterPostProcessTexture 中,最终在执行 Final Blit Pass 进行伽马矫正后 Blit 到 FrameBuffer 中。
每次 Blit 不仅消耗带宽还需要对片元进行中着色,关键是大部分手机和 PC 是不需要伽马矫正的,因为这些硬件已经支持了 SRGBA 转换,可以直接将线性空间结算的结果提交给显卡,显卡自己会做伽马矫正直接显示出来。以我们目前测试经验来看只有一些安卓模拟器还有一些很老的设备不支持 SRGBA 转换,所以优化的第一步就是“干掉” Final Blit Pass。在管线中可以执行代码 Display . main . requiresSrgbBlitToBackbuffer 判断是否需要进行手动伽马矫正,在渲染的时候就可以直接渲染到 FrameBuffer 中,而不需要所有都渲染到 _CameraColorTexture 中,然后再额外进行 Final Blit Pass 了,这样性能上能得到一个很大的提升。
02 后处理流程优化
要进行后处理就必须要先将结果绘制到一张 RT 中,因为 FrameBuffer 中的颜色管线中获取不到。URP 管线是可以对任意相机进行后处理的,但其实 UI 是没有后处理的需求的,真正需要后处理的只有 3D 相机。这样 3D 需要先画到 _CameraColorTexture 中,然后执行后处理操作,将颜色直接 Blit 到 FrameBuffer 中,这样就不需要先 Blit 到 _AfterPostProcessTexture 在进行 Final Blit 了,一下就可以减少 2 次 Blit 性能又得到一次很大提升。
03 降低分辨率不包含 UI
通过以上优化后内存中只有一张 _CameraColorTexture,此时只有 3D 画在这个图中,所以可以只对它进行降低分辨率,因为它降低了分辨率后处理也会降低分辨率,接着才后处理,接着此时就 Blit 到 FrameBuffer 中。后面的 UI 直接给 FrameBuffer 中画,并没有通过中间 RT 所以 UI 显示是完全高清的,如果在平板这样的设备上,因为屏幕分辨率比较大可以整体优先降低分辨率 Screen . SetResolution() ,然后再针对 3D 部分在单独降低一些。
风格化卡通 PBR 的渲染方式 做了哪些融合方式?
01 制作方面
我们游戏并没有使用传统二次元赛璐璐风格的渲染,而采用的是 PBR + NPR 混合的渲染方式,而且对渲染公式也做了很多调整。在装备武器上使用 PBR 占比多一些,在 PBR 的基础上增加了卡通渲染的分阶着色、各向异性、两层高光的头发渲染、车漆、毛发、布料、绒等效果,还对阴影进行了修正,修改了光照模型以及材质表达做了不同程度的融合。在皮肤头发中使用 NPR 占比多一些,还增加了皮肤多层颜色过度羽化功能,还有一部分 mask 图来修正高光、描边、面部阴影等细节。
02 优化方面
我们的 Shader 分了两个,一个是 PBR 的一个是 NPR 的,常用的 Shader 效果多多少少扩展了 30 多种吧,加上 Unity 自带了宏定义就 40 多个了,记得当初有个版本定义了很多 multi_compile 导致光 Shader 内存就过 1G 了非常恐怖。优化的手段就是优先使用 multi_feature 来代替 multi_compile,但是有些死亡溶解、贫血边缘光效果它需要动态开关,这种我们需要做宏剥离 IPreprocessShaders。比如溶解和边缘光宏是不可能存在同时开关需求的,还有一些其他宏不可能出现得动态组合,这些都可以预先剥离它。multi_feature 也面临是一个问题是当 Shader 进行依赖时,单独打 Assetbundle 的时候可能会被玻剥离掉,所以我们还开发了一个工具一键生成变种 ShaderVariants 收集器,原理就是离线对所有场景、Prefab 遍历收集将使用到的宏组合预先收集到 ShaderVariants 中,这样定义过 multi_feature 就不会被剥离掉了。
除了我们自己写的 Shader 以外,Unity 默认的宏有些也不太合理,目前角色都使用 SRPBatcher 不需要 GPUInstacning,还有 multp_compile_fog 都会产生额外的宏如果不需要都可以进行剥离。通过这些修改 Shader 内存直接从原先的 1G 优化到 20M 左右。
03 字节对齐
URP 的 shader 我们还遇到了个需要字节对齐的坑,在部分机器上可能闪退或者显示结果异常。如下代码所示,要将占用大的写在前面,而且不要出现 half3 和 half2,统一使用 half4 来代替。
CBUFFER_START
half4
half4
half
half
END
3D、 2D 界面结合 可以具体讲一下是想实现怎么样的效果?以及怎么做的?
我们游戏使用的是 UGUI 开发的,3D 界面使用透视的相机,但是 3D 界面有个前提是需求上它是否需要自适应。如果需要自适应,在 Canvas 中还使用 Screen Space-Camera 模式单纯将 UICamera 的 Projection 设置成 Perspective 就可以了,这样 UI 就可以进行旋转拥有近大远小的效果。如果 3D 界面不需要自适应,比如场景中有个模型,旁边用 UI 写个文字一类的描述。因为模型是被 3D 相机看的,当屏幕尺寸发生变化可能模型可能就显示不全,如果此时 UI 还进行自适应效果就出问题了,所以针对于这种不需要自适应的 UI 需要再 Canvas 中设置 Word Space 模式。这样无论 3D 还是 2D 界面都可以用一套 UI 框架打开,对于 UI 框架来说它们都是完全一样的,至于界面中动画效果就是正常的由 UI 策划同学挂 Tween 一类脚本实现。
游戏中还用到了哪些优化手段?
01 角色部分
模型采用 3D 模型 + Spine 骨骼 + Live2D,我们单模型 4 万面以上,骨骼在 120 格以上,整个角色的渲染压力非常大,GPU 一直在等 CPU。所以我们做了 Shader Lod 功能,在 UI 界面中观察模型时,所有效果都拉到最满,但是进入战斗会把大部分效果剥离,这样能进一步减低 Shader 片元着色的压力。
02 场景部分
我们场景在 20 万面左右,在低端机上我们会限制镜头移动,这样可以在进场景后的第一帧将场景渲染的结果以及渲染深度写在另一张 RT 上,后面渲染的时候就不对场景进行渲染直接使用这个 RT,这需要在 URP 中增加一个 Renderer Features。第一帧先渲染场景将 RT 保存,后面直接使用这个渲染的结果,因为渲染的时候要保存深度,所以不会和后面渲染的模型产生遮挡问题,这样在低端机上可以直接节省掉这 20 万面的场景渲染压力。
03 特效部分
我们开发了特效填充率优化工具,提前对每个特效的 Overdraw 进行统计并且计算出一个数值出来交给美术同学,美术同学重点对有问题的特效进行优化。
04 iOS 移植方面
美术同学开发都是使用 Windows 的,但是 Android 和 iOS 贴图压缩格式是需要单独设置的,如果每张图都让他们设置 2 回也容易出错。所以我们开发了贴图默认压缩工具,贴图放入游戏就可以自动设置最优化格式,角色、场景、UI、烘焙图等都会自动设置,而且 iOS 和 PC 会自动保持和 Android 或相等消耗的压缩格式。如果后面美术同学需要单独给某张贴图设置更高清的格式只需要设置 Android 平台,PC 和 iOS 平台会自动同步。
我们限制了 RGBA32 和 RGBA16 的压缩格式,只要设置了这两个格式脚本会自动强制改回 ASTC 格式。而且并没有做一些特殊的目录来标记可设置 RGBA32 格式,因为一旦美术设置多了对后期优化带来很大的挑战。如果真需要 RGBA32 我们手动加到白名单里,这个行为必须通过程序同学(现在累总比以后累强),这样才能进一步保证 RGBA32 不被滥用带来更多的内存开销。
因为我们没有企业证书,所以 iOS 没有太多机会被测试。只要保证了贴图内存在移植 iOS 就非常丝滑了,至于渲染问题几乎只要 Android 搞定了 iOS 就没有太大问题,通过这一些列操作我们 iOS 从开始移植到出包仅仅用了 1 个礼拜时间。
05 UI 部分
UI 最大的问题就是打开界面慢,首先 Unity 引擎是强烈不建议使用 Assetbundle 同步加载的,因为当异步加载的过程中如果进行了同步加载会带来一个非常大卡顿。UI 如果使用异步加载框架需要进行调整,因为 UI 有关闭老界面打开新界面的需求,所以需要等新界面加载上才能关闭老界面,加载的过程中需要锁屏。
还有就是 UI 的 Prefab 可能会产生很多依赖的情况,所以我们界面再制作时会通过一个工具将界面上依赖的贴图特效统统从 Prefab 中剥离,这样界面的 Prefab 只保留所有 GameObject 的节点。运行时在打开界面时会按需异步加载,这样对一些 Tab 页的 UI 当用户没有点开时是不会加载资源的,在配合所有 Assetbundle 都是异步加载的,所有我们游戏几乎从立项以来没有被策划同学吐槽过打开界面慢。
06 其他
从体验上可以对游戏结果可以分为两类:用户在观察模型的细节时和自动战斗时。因为这两种情况下可以做一些特殊优化,观察模型时镜头趋于固定,用户希望能看到更高清的效果,此时画面分辨率需要清晰,但是对帧率没有特别高的要求。当自动战斗时,此时镜头已经在乱动了很难看清效果了,此时用户对帧率有要求,但是话画面分辨率就没要求了。
结合以上两种特点游戏中我们进行这样的优化,当用户有操作屏幕时会慢慢降低分辨率提高帧率让用户感受不到卡顿。而当用户不操作屏幕并且镜头没有发生移动时,慢慢恢复画面高分辨率而降低帧率让用户看到的画面比较清晰。
Unity 官方在 PBR 制作上给与项目组哪些帮助?
我们购买过 Unity 官方技术服务,官方的技术大大给我们给我们美术同学科普过几节 PBR 的原理课程。我觉得这个非常有必要,因为美术同学可能不太在意实现的细节更关注结果,这样往往就忽略一些潜在引发的性能问题。还有美术在做场景时总出现烘焙后 PC 和移动端不一样的效果,也是官方的技术大大给我们讲解了原来移动端会将烘焙贴图的压缩格式改成 DLDR 格式,强制将光的强制限制在 0-2 之间。因为 PC 没有这个限制所以两端结果是不同的,解决的办法也很简单,避免使用那种强度很大的光源,合理的规划光源的位置来避免。
最后我还想说的是我们做项目很多知识点是较为落后的,因为我们必须保证它必须在所有设备上能跑起来。Unity 官方大大们能提前接触到世界上最前进的技术,可能它不一定能立即在项目中落地,但是能对我们起到一些提前引导的作用。
来源:Unity官方平台
原文:https://mp.weixin.qq.com/s/U7xl9iQXQuNZWwOYKn6beg