乐趣区

关于rendering:UWA问答精选

1)Unity 性能优化剖析思路
​2)资源打包关系依赖树
3)SpriteAtlas 中 Include in Build 的作用
4)应用 Streaming Mipmap 后纹理内存没有降落的疑难
5)URP Renderer Feature 实现二次元描边,Cutout 的解决问题


UWA 每周推送的知识型栏目《厚积薄发 | 技术分享》曾经随同大家走过了 330 个工作周。精选了 2022 年上半年的精彩问答分享给大家,期待 UWA 问答持续有您的陪伴。

UWA 问答社区:answer.uwa4d.com
UWA QQ 群 2:793972859(原群已满员)

Unity

Q:Unity 有多少优化点?比方合批:动态合批、SRP 合批、GPU 实例化、UGUI Reruild、光照烘焙、反射探针、光照探针、Shader.Parse、Shader.CreateGPUProgram、场景加载优化和 GC 优化,还有哪些优化点?

A1:大方向上能够从 CPU、内存、GPU 这三个方向切入。

细分一下能够从 CPU、内存、渲染、资源优化、耗电优化、网络优化、卡顿优化、优化工具的抉择把握这几个点动手。
一、CPU 优化

  1. 缓存计算结果
  2. 预处理
  3. 限帧法
  4. 主次法
  5. 多线程
  6. 引擎模块(动画、物理、粒子、导航)
  7. 逻辑优化

二、内存优化

  1. 缓存法
  2. 内存池
  3. 资源管理器
  4. 管制 GC
  5. 逻辑优化
  6. Shader 变体数量优化

三、渲染优化

  1. SetPassCall 渲染状态切换频次管制
  2. DrawCall 数量管制
  3. 带宽负载
  4. 显存占用
  5. GPU 计算量

四、卡顿优化

  1. 降帧法
  2. 摊帧法
  3. 限度数量法
  4. 逻辑优化
  5. IO 优化
  6. 应用进度条

五、资源优化

  1. 纹理优化
  2. UI 优化
  3. 字体优化
  4. 模型优化
  5. 场景优化
  6. 粒子优化
  7. 材质优化
  8. 指定规范美术标准
  9. Shader 变体数量优化

六、耗电优化
下面说到的优化点,或多或少都会影响到手机的耗电,也是优化耗电的措施,除此之外还有:

  1. 动静调整限帧
  2. 动静调整画质

七、网络优化

  1. 缩小无用字段
  2. 升高字段精度
  3. 防止反复发送
  4. 网络异步化
  5. 压缩有效字节
  6. 压缩协定包

以上说的这些要点,大部分摘抄演绎自《移动游戏性能优化通用技法》。强烈建议多花些工夫认真浏览一下这篇文章,而后以这篇文章作为指南,再去认真钻研外面提到的优化细节该如何开展。

感激马三小伙儿 @UWA 问答社区提供了答复

A2:优化点必定是无穷无尽的,这里搬运 UWA 的客户端性能优化思路,针对常见的引擎模块的相干问题都做了剖析,讲的是比拟全比拟透的,常见的优化难题都列举了。

《Unity 性能优化 — 物理模块》
《Unity 性能优化 — 动画模块》
《Unity 性能优化系列 — 资源内存透露》
《Unity 性能优化系列—Lua 代码优化》
《粒子系统优化——如何优化你的技能特效》
《Unity 性能优化系列—加载与资源管理》
《Unity 性能优化系列—渲染模块》
《Unity 性能优化 — UI 模块》
《反对资源加载剖析、场景宰割》
《UWA 报告应用小技巧,你 get 了吗?》
《UWA 本地资源检测更新,助你严守我的项目性能的每个角落!》

感激芭妮妮 @UWA 问答社区提供了答复

AssetBundle

Q:想做包体资源剖析,大家有什么好的树显示工具或者思路举荐吗?有比拟好的开源计划也能够。最简略就像 N 叉树一样,比方 root 一个文件名,而后开展整个树结构。

A1:我本人做了一个,供参考。都是用 Unity 本人的 IMGUI 最根本的接口去实现。
EditorWindows
GUI.Box
GUI.BeginGroup
GUI.Label
Handles.DrawBezier
Handles.DrawWireDisc
TreeView
基本上,组织好各个 AssetBundle 的依赖关系其实是很好出现的。

感激黄程 @UWA 问答社区提供了答复

A2:举荐一款比拟好用的插件,不止有依赖树,还有其余打包的资源数据可供剖析:
https://assetstore.unity.com/…

感激郑骁 @UWA 问答社区提供了答复

AssetBundle

Q:SpriteAtlas 中 Include in Build 的作用是什么?

A:专门做了一些测试,具体如下:

以下表白中 Sprite 对应的是 Sprite 类型的对象,Texture2D 对应的是 Texture2D 的对象,这和 Sprite 对象是齐全不同的货色,sactx 示意生成的图集纹理。

测试状况包含 2 个变量:

  1. SpriteAtlas 对象是否被动打包 AssetBundle
  2. SpriteAtlas 对象上是否勾选 Include in Build

第一种状况,SpriteAtlas 打包 AssetBundle:

那这里要思考的是 SpriteAtlas 援用的 Sprite 是否会独自打包,如果这些小 Sprite 不被动打包,是会被动进这个 SpriteAtlas 的 AssetBundle 外面的,如果其余的 UI Prefab 中,比方有个 Image 应用了一个小 Sprite,那么这个小 Sprite 就冗余了。

这里勾不勾选 Include in Build 的区别在于:加载 Image 的时候,这个 Image 会不会主动显示,勾选了 Include in Build,会主动显示图片,不勾选,则须要脚本增加回调来被动加载 SpriteAtlas,并 callback(spriteatlas)。

第二种状况,SpriteAtlas 不退出 AssetBundle 打包:

  1. 不勾选 Include in Build
    假如小的 Sprite 打包 AssetBundle,在这个 AssetBundle 外面不会有 sactx,这个 sactx 的 Texture2D 的纹理变成“隐没”的状态,没有任何货色能够援用到这个 sactx 纹理,而且因为在工程外面有 SpriteAtlas 的存在,所以在小的 Sprite 的 AssetBundle 外面也不能让其自身对应的小的 Texture2D 纹理进 AssetBundle 包,所以图像就永远显示不进去了。
  2. 勾选 Include in Build
    所有的小的 Sprite 所在的 AssetBundle 外面都会被动蕴含 sactx 的图,且会蕴含所有没有被动打包的小的 Sprite。

如 Sprite1 和 Sprite2 是 SpriteAtlas 外面的两个小的 Sprite。Sprite1 被动打包,Sprite2 不被动打包,那么 Sprite1 的 AssetBundle 外面是会有 Sprite1 和 Sprite2 以及 sactx 纹理。

总结:

  1. 如果有 Sprite 退出了某个 SpriteAtlas,那么任何真正应用到这个 Sprite 的资源都不会有对 Sprite 对应的小的 Texture2D 纹理的援用,而是对 sactx 图集纹理的援用。
  2. 如果 SpriteAtlas 不打包,必须勾选 Include in Build,否则 sactx 纹理就“隐没”了,在勾选 Include in Build 的前提下,而且 SpriteAtlas 中的所有小的 sprite 必须打包到同一个 AssetBundle 外面,否则 sactx 会冗余。
  3. 如果 SpriteAtlas 打包了 AssetBundle,sactx 永远不会冗余了(这里的冗余是指打包 AssetBundle 造成的冗余)。SpriteAtlas 外面的小的 Sprite 也最好打包 AssetBundle,不然这些小的 Sprite 就会冗余。勾选或者不勾选 Include in Build 都不影响各种依赖关系,惟一的区别是是否会被动显示图片,勾选了就会被动显示图片,不勾选就须要脚本管制来显示图片。

感激 Xuan@UWA 问答社区提供了答复

Texture

Q:为什么我在我的项目中应用了 Streaming Mipmap 然而在 GOT 报告中看纹理内存没有降落?是没有正确失效还是统计有问题?

A:之前做过相干测试,发现 GOT Online 是能够统计到被 Streaming Mipmap 影响的纹理的正确内存的,所以我揣测你遇到的状况大概率还是没有正确失效导致的。以下是对如何让一张纹理利用 Streaming Mipmap 的简略流程总结,其中会注明须要特地留神的一些条件(有些被官网文档收录,有些则文档中没有,但试验证实为必要):

  1. 在 Project Settings-Quality 中开启 Texture Streaming 选项。然而试验发现 Editor 中开启该选项而真机上却会生效的景象,导致所有纹理的 Streaming Mipmap 设置全副生效。所以为了确保失效,首先应该在代码中调用 QualitySettings.streamingMipmapsActive API 全局地开启这个选项,能力确保 Streaming Mipmap 可用。
  2. 调整 1 中设置的参数。比拟重要的参数是 Memory Budget 参数和 Max Level Reduction 参数。Memory Budget 示意纹理资源的估算,默认值是 512MB,但依据 UWA 的大量我的项目数据来看,中低端机上个别为 200MB 左右。它的数值代表的是所有纹理资源的估算——即,它既包含了非流式的纹理、又包含了咱们想要采纳流式的纹理——但这个“估算”并不代表纹理资源可占用的下限,只是 Unity 判断对于一个开启 Streaming Mipmap 的纹理到底采纳它的哪些 Mipmap 通道的参考值,非流式纹理可能轻易冲破这个估算。

Max Level Reduction 则是代表着 Unity 通过流式存储最高能取到哪一级的 Mipmap 通道,这个参数的优先级比 Memory Budget 要高,也就会造成理论内存超过预算的状况。(比方该参数为 2 时,则最多剔除 Mipmap0 和 1 通道,即使抛弃当前还远超出预算值也不会进一步剔除。)

也就是说,如果 Memory Budget 值设置的远高于我的项目中纹理理论占用的内存,则 Texture Streaming 可能齐全不起效,所有开启 Streaming Mipmap 的纹理仍将保留它们的全副 Mipmap 通道。

  1. 设置开启 Streaming Mipmap 的纹理。1、2 中提到的设置只对开启了 Streaming Mipmap 的纹理起效,而这只是官网文档的说法,从实际操作来看,Texture Streaming 只对同时满足以下三个条件的纹理失效:

1)开启了 Streaming Mipmap 且开启了 Generate Mipmap 的纹理(这一点官网文档中没有提及,事实上开启 Generate Mipmap 才会生成 Mipmap 通道供 Streaming Mipmap 剔除);

2)被即时加载的纹理(如一开始就曾经在场景中被依赖的纹理,即使开启了 Streaming Mipmap 其内存也不会发生变化,通过 AssetBundle 加载和 Res.Load()加载的纹理则能够),也即官网文档中这句话的理论含意:

如果是进行 Android 开发,还须要关上 Build Setting,并将 Compression Method 设置为 LZ4 或 LZ4HC。Unity 须要应用其中一种压缩办法进行异步纹理加载,这是纹理串流零碎所必须的操作。

3)Gfx 局部内存(这里指的是纹理资源开启 Read/Write 选项时,复制到 CPU 端的那一部分内存是不受 Streaming Mipmap 影响的);

  1. Streaming Mipmap 剔除 Mipmap 通道的法则。它的机制其实和 Texture Quality 是相似的。咱们晓得,开启 Mipmap 的纹理之所以会变成原来的 4 / 3 倍,实际上是它各个通道所占用的内存之和。举例而言,一个具备 11 个 Mipmap 通道的原大小为 1MB 的纹理(10241024 分辨率、ASTC44 格局),其内存占用为 1 +1/4+1/16+…的 11 项等比数列之和,即约 4 /3。等比数列的各项就对应了 Mipmap0、Mipmap1、Mipmap2…等各个 Mipmap 通道。那么,当 Max Level Reduction 参数设置为 2 时,其实际意义就是保留 Mipmap2 和后续所有更小的通道,而剔除 Mipmap0 和 Mipmap1 通道,此时的内存大小为 4 /3MB-1MB-1/4MB=85.33KB。这和我在 Profiler 或 GOT Online 中看到的数据基本一致。
  2. 对于采纳 Streaming Mipmap 计划的倡议。根据上述试验和剖析不难看出,Streaming Mipmap 是的确具备一些长处的,对于对内存比拟敏感,尤其是纹理内存占用很大的我的项目,采纳 Streaming Mipmap 计划是十分正当和举荐的选项。与此同时,它的理论应用要求对我的项目中纹理资源的内存占用有相当的理解和布局——相干设置在 Quality 中,天经地义地应该思考到不同设施 Lod 分级时不同的设置。在中低端机中,设置尽量低的 Memory Budget 和尽量高的 Max Level Reduction;在高端机上则恰恰相反,在内存可承受范畴内尽量开启最好的画面体现。除此之外,对于哪些纹理要开启 Streaming Mipmap,个别是场景中 3D 物体的纹理,而 UI 模块采纳的纹理则尽量敞开。因为 Mipmap 的意义次要在于适应纹理在间隔镜头远近时的体现须要、防止失真等,而 UI 齐全不须要这些,开启只会白白浪费内存和计算工夫。

感激 Faust@UWA 问答社区提供了答复

Rendering

Q:之前咱们的卡通渲染是在 Shader 里写多个 Pass 来绘制的描边,最近尝试用 SRP Batcher 优化时发现,SRP Batcher 不反对多个 Pass 的 Shader。于是我尝试用 URP Renderer Feature 来渲染所有角色的描边。

实现起来很简略,但有一个问题解决不了,就是 Cutout 的问题。

裙子的下边缘是用贴图的 Alpha 管制的,并不是真正的顶点。以前的 Pass 写在角色渲染的 Shader 里,能够用贴图来管制,但用 Renderer Feature 来解决后,所有角色模型的描边是用的同一个材质,不能再用模型各自的贴图的通道来解决了。显示成果就是 Cutout 的描边局部无奈解决:

边缘比较复杂,然而管制的顶点就只有几个,感觉不太好实现。

当初模型顶点的色彩信息我曾经用过了,RGB 是描边色彩,A 是描边粗细。我能想到的方法是用 A 的一些非凡值来非凡解决一下某些顶点(相当于 Clip 掉一些顶点,但必定没有相干 API),但又感觉仿佛不太可行。不晓得大家有没有遇到过,或者有没有什么好方法呢?

本人尝试了用顶点信息标记点,但有瑕疵,点关联的边会受影响,描边没了:

让美术加了一些点,根本也能解决(其实外边缘还是不会显示全,但曾经看不太进去了):

但这办法还是不好。最好在 Renderer Feature 里能够获取到正在渲染的模型的材质信息。

是否能够把一个多 Pass 的 Shader 里的某个 Pass 弄到独自的 Renderer Feature 里画,而不是从新画一遍?比方画第一遍的时候禁用这个 Pass,Renderer Feature 画的时候再启用它?

A1:自定义一个 LightMode,Render Feature 里设置这个 LightMode,这样材质球上的贴图数据什么就能有。

补充个截图大略是这样的:

对于自定义的 LightMode,Unity 默认疏忽,只有 Render Feature 里手动指定了要画这个 LightMode,Unity 才会去绘制。

感激 jim@UWA 问答社区提供了答复

A2:如下,这样 SRP Batcher 终于能够合批多 Pass 的 Shader 了:

不必禁用失常的渲染。

两个 Pass,一个失常的 Pass,一个自定义 LightMode 的 Pass,lightMode 的 Pass 渲染描边用 ScriptableRendererFeature 手动指定渲染自定义的 LightMode。Unity 不会去渲染你自定义的 LightMode。

感激题主仇磊 @UWA 问答社区提供了答复


封面图来源于网络

明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在 UWA 问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官网技术博客:blog.uwa4d.com
官网问答社区:answer.uwa4d.com
UWA 学堂:edu.uwa4d.com
官网技术 QQ 群:793972859(原群已满员)

退出移动版