UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - gpu
UE是怎么管理纹理的各向异性采样的
1）UE是怎么管理纹理的各向异性采样的 2）Unity 2022动态设置光照贴图手机端显示异常 这是第459篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 From 问答社区 Q：优化我们UE项目的功耗发热问题时，在移动端真机测试中，通过各种性能工具的参数发现GPU带宽很高，且其中Anisotropic Filtered各向异性过滤的比例一直很高。按之前的经验来说，各向异性对带宽的影响就是很大的，但是在编辑器里没找到设置项，请问UE是怎么管理纹理的各向异性采样的？ A：UE中纹理是否开启各向异性需要检查以下设置： Texture Group 在DeviceProfiles.ini中可以创建TextureGroup并进行设置，例如在Engine\Config\BaseDeviceProfiles.ini中（或Project\Config\DefaultDeviceProfiles.ini）： 其中，MinMagFilter是纹理拉伸时使用的过滤方式，包括point、linear、aniso，此处aniso即开启纹理的各向异性设置。如果想对该Group的所有纹理关闭各向异性设置，可以修改为point或linear。 纹理编辑器设置 打开纹理的资产编辑器，在设置中能够通过Filter设置调整纹理采样方式，可选包括Nearest，Bi-linear，Tri-linear，Default。其中，Default为使用TextureGroup的设置，大部分默认的Group中纹理都开启了各向异性（参考上一条）。此处可以选择另外三种过滤方式，选择后将关闭该纹理的各向异性。该设置适合针对单独纹理关闭各向异性时使用。 各向异性最高采样数设置 各向异性的最高采样数可以通过Cvars：r.MaxAnisotropy进行设置。例如： r.MaxAnisotropy=8，则纹理最高采样数为8。数值为0或1时关闭各向异性。该设置为全局设置，对所有开启了各向异性的纹理生效。在Scalability.ini中各纹理级别已经包含了该设置： 此外在DeviceProfile中，也可以直接在对应平台添加该Cvars的设置： 欢迎大家转至社区交流： https://answer.uwa4d.com/question/69523009244ce21ce9ec0967 From 问答社区 Q：编辑器加载显示都正常，出安卓包，光照贴图出现错乱，使用FrameDebugger抓帧查看数据unity_lightmap有值，使用工具获取LightingSetting中设置的光照贴图也是正常的，Mesh中也添加了对应的光照信息数据。 补充测试点：同一个物件，固定在场景中，光照信息正常，动态加载的物件会出现异常（使用Yooasset进行加载）即使动态重新设置了光照贴图，固定的物件光照信息仍然正常。（提供的图片为手机端的显示和FrameDebugger的抓帧数据。） 为什么Unity 2022动态设置光照贴图手机端会显示异常？ 欢迎大家转至社区交流： https://answer.uwa4d.com/question/694eab9d682c7e5cd61bfb83 无论是社区里开发者们的互助讨论，还是AI基于知识沉淀的快速反馈，核心都是为了让每一个技术难题都有解、每一次踩坑都有回响。本期分享分别来自UWA AI问答和UWA问答社区，希望这些从真实开发场景中提炼的经验，能直接帮你解决当下的技术卡点，也让你在遇到同类问题时，能更高效地找到破局方向。 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
参数GPU Write Total Bandwidth的含义是什么，导致其值过高的因素有哪些
1）参数GPU Write Total Bandwidth的含义是什么，导致其值过高的因素有哪些 2）对Unity项目来说，性能和效果可以兼顾的物理插件有哪些推荐 这是第458篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 From UWA AI 问答 Q：GPU Write Total Bandwidth这一参数的含义，和其过高时可能的影响因素有哪些？ A：GPU Write Total Bandwidth含义： 该指标表示GPU每帧向内存（显存或系统内存）写入的总数据量，单位通常为MB/s或GB/s。它是GPU带宽压力的关键子项，反映帧渲染过程中所有写操作的带宽消耗总和，包括：帧缓冲（Color/Depth/Stencil）、RenderTexture、计算着色器输出、顶点着色器输出（如Transform Feedback）、以及部分缓存写回等。 Write Total Bandwidth 过高的常见影响因素： ✔ 高分辨率多目标渲染（MRT）或高精度帧缓冲 同时写入多个RenderTarget（如GBuffer中Albedo+Normal+Metallic+Depth），尤其使用高精度格式（RGBA16F、RGB10A2）； 主摄像机启用HDR渲染（R11G11B10或RGBA16F），大幅增加Color Buffer写入带宽； MSAA启用后（如4xMSAA），Color/Depth Buffer写入量成倍增长（需resolve前写入+resolve后写入）。 ✔ 频繁或大尺寸RenderTexture写入 全屏后处理中反复创建/清空/Blit到高分辨率RT（如2048×1536的Bloom临时纹理）； UI渲染中大量使用CanvasRenderTarget或Camera.targetTexture； 动态阴影图（Shadow Map）分辨率过高（如4096×4096）且每帧更新。 ✔ 深度/模板缓冲频繁写入与清除 每帧Clear Depth/Stencil（尤其在多Camera、多Pass场景下重复Clear）； 启用深度预通道（Z-Prepass）但未合批或存在冗余绘制，导致两次深度写入（Prepass+Main Pass）； 不合理的ZTest/ZWrite设置（如ZWrite Off缺失导致无效深度覆盖重试）。 ✔ Compute Shader高频大块写入 使用RWTexture2D/RWBuffer执行大规模粒子更新、物理模拟或图像生成，且线程组尺寸过大或Dispatch次数过多； 未使用AppendBuffer/ConsumeBuffer而采用全量写入方式。 ✔ GPU管线冗余写入行为 多个Camera渲染同一区域且未做裁剪/层级隔离，造成重复Color/Depth写入； URP/HDRP中未关闭不必要的Pass（如ShadowCaster、DepthOnly在非阴影场景仍启用）。 快速定位建议： 查看GOT Online GPU报告中Write Total曲线与Front-end Write/Texture Write/Depth Write等子项占比（若支持）； 对比FPS下降时段是否伴随Write峰值，结合GPU Freq是否因带宽瓶颈触发降频； 检查RenderPipeline Asset中是否启用了非必要高精度RT格式、MSAA或多Pass渲染； 使用UWA资源检测报告筛查高分辨率、未压缩的RenderTexture和ShadowMap资产。 优化优先级：降低RT分辨率 → 合并MRT输出 → 关闭冗余Clear/MSAA → 替换高精度格式（如RGBA8替代RGBA16F）→ 后处理降采样。 UWA GPU分析页面中还展示了很多对性能排查分析有一定参考价值的参数和其子参数，若您对这些参数的具体含义和参考价值仍有疑问，可在社区中进行查阅、或尝试使用AI问答助手解答。如以上问题，除了了解写带宽的影响因素外，您还可以提问在不同GPU架构下多种多样的读带宽子项的含义：Front-end Read、LoadStore Read、Texture Read、Vertex Read、SP Read。 分享链接： https://www.uwa4d.com/main/uwa-shared.html?shareId=8919019e-4d69-43a2-bba1-39138791414a From UWA AI 问答 Q：对Unity项目来说，性能和效果可以兼顾的物理插件有什么推荐？ A：在Unity项目中，兼顾性能与效果的物理插件需满足三大硬性条件： ✔ 跨平台行为一致（尤其iOS/Android）； ✔ CPU占用可控（单帧<1ms，中端机）； ✔ 支持表现/逻辑分层（避免帧同步风险）。 推荐方案： 首选：Unity原生PhysX+工程化封装（非插件，但最可靠） 适用场景：角色布娃娃、关卡交互物体、简单布料、碰撞反馈。 关键优化实践： 使用Rigidbody.interpolation=Interpolate+collisionDetectionMode=ContinuousDynamic控制抖动与穿透； 所有Collider替换为Primitive Collider组合（禁用MeshCollider，见FAQ文档），复杂模型用BoxCollider+CapsuleCollider分区代理； 物理更新频率降为Time.fixedDeltaTime=0.033f（30Hz），配合Physics.autoSimulation=false手动调度。 高效替代：Obi Softbody & Obi Cloth（商业插件） 优势： 纯C#实现，完全绕过PhysX，iOS/Android行为100%一致，支持帧同步逻辑层（需定点数改造）； 布料解算支持GPU加速（Metal/Vulkan），CPU负载下降60%+； 提供ObiSolver多线程调度器，可绑定至Unity Job System。 注意：需关闭Substeps（默认3→设为1），避免低端机卡顿；布料顶点数建议≤128。 表现专用（零物理开销）：Dynamic Bone（免费开源） 定位：纯表现层附属物模拟（头发、衣摆、尾巴）。 优势： 无刚体、无物理引擎依赖，仅靠Transform插值计算； 支持LOD控制（Update When Inactive+Max Distance）； 源码开放，可无缝接入URP/HDRP。 不适用于受力反馈、碰撞交互等逻辑需求。 明确规避 Havok Physics for Unity：仅限高端主机/PC，移动端无授权支持，且无法保证Android/iOS一致性。 自研PhysX封装库：跨平台浮点误差不可控，已有多款上线项目因PhysX版本差异导致iOS/Android同步失败。 最终建议路径 所有方案均需配合UWA GOT Online的Physics模块实时监控Rigidbody Count与FixedUpdate ms，确保长期稳定。 无论是社区里开发者们的互助讨论，还是AI基于知识沉淀的快速反馈，核心都是为了让每一个技术难题都有解、每一次踩坑都有回响。本期分享分别来自UWA AI问答和UWA问答社区，希望这些从真实开发场景中提炼的经验，能直接帮你解决当下的技术卡点，也让你在遇到同类问题时，能更高效地找到破局方向。 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）如何统计DrawMeshInstancedIndirect绘制物体的Triangle数据 2）如何量化骨骼数量对功耗的影响 3）Sprite Atlas和单独的Sprite的RW设置分别如何影响内存 4）Playable的Animator.WriteTransform一直执行在一个Job线程里 这是第444篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 GPU Q：我们项目的人物渲染从Mesh Render渲染改成了GPU Driven，调用DrawMeshInstancedIndirect渲染，然后看起来人物的面数在Unity Profiler的Triangle数据中统计不到了。有没有别的方法统计监控这部分Triangle数据？ A：之前我们也遇到过用这种方式绘制物体的项目，可以试试GPU底层获取的Primitive，应该是准的。比如UWA Gears或GOT Online GPU模式的参数。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/68be4643682c7e5cd61bfb18 Animation Q：我们项目角色骨骼数量现在大概是200多，怪物会少一些，但应该也属于比较多的，一直想让美术把规格降下来。现在在Profiler里看到蒙皮网格主要是在子线程有消耗，但是不影响帧率。担心对功耗有影响，但是没法量化，不确定美术这边优化到什么水平比较合理，有没有什么好的办法或建议？ A：一个模型200骨骼对移动项目来说肯定是比较多的，不过具体优化到多少也要结合同屏角色和怪物数量看。 功耗的角度上，Perfetto工具可以量化功耗的影响，但是需要Pixel设备。可以参考今年UWA DAY《移动端Unity游戏功耗调优的破局实践》话题的讨论。 还有个方法，出个关闭动画的包，对比下功耗的差异。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/68be7a93244ce21ce9ec0914 Memory Q：Sprite开启Read/Write选项，会导致额外的内存占用。如果禁用Sprite Atlas的Read/Write，单独的Sprite不禁用，内存占用会比都禁用高吗？ A：内存占用量应该以最终进包进内存的图集的设置为准。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/68be4547682c7e5cd61bfb17 Animation Q：Playable的Animator.WriteTransform一直执行在一个Job线程里，如何让其分散在各个Job里执行？ A：在场景里平铺，不要有共同的父节点。 感谢littlesome@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/68917164682c7e5cd61bfad2 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）Sprite纹理被材质引用导致冗余问题 2）为什么在Android端发现Vertices shaded次数远大于Unity场景中的模型顶点数 3）物体使用Unlit的材质，为何会被点光源打断合批 4）微信小游戏2D动画方案如何选择 这是第439篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 Memory Q1：纹理设置成Sprite后，如果再被材质引用，会被收集两次造成冗余，即使将纹理设成dependAsset进行收集也没用，这个有办法解决吗？ A：这个是Unity引擎一直都有的问题，需要复制一份，一个设置成Default，一份设置成Sprite2D。 Q2：那这样和不处理有什么区别？ A：还是有区别的，如果有多个UI和普通材质球使用这个纹理，就会冗余多份；如果复制了一份，那么就会只有1个Sprite和1个Default纹理。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/687e04fc3d306f3e9d594f1d GPU Q：在Android端发现Vertices shaded次数远大于Unity场景中的模型顶点数，这是什么原因导致的呢？已经排除了一个模型多个Pass导致的原因了，可以确定只有一个Pass。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/6874cfc53d306f3e9d594f13 Rendering Q：项目中发现有的物体使用的是Unlit的材质，但仍会被点光源打断合批（因为关闭点光源或把点光源的Culling Mask调整为不包含这些物体的Layer），可能是什么原因？ A：Unity有过类似的Bug，不排除仍是Bug的影响。 参考： https://unity.cn/release-notes/full/2021/2021.3.7f1 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/687dbf973d306f3e9d594f1b Animation Q：关于微信小游戏2D动画这一块，Unity的2DAnimation性能表现如何，建议使用吗？或者有其它比较推荐的方案吗？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/687dfeda682c7e5cd61bfac8 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）UnityEngine.Object在失去引用后用什么方法销毁或回收 2）关于纹理采样方式对带宽的影响原理的问题 3）微信小游戏缓存catalog.json文件的方案 4）微信小游戏的帧率设置用哪个更好 这是第438篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 Memory Q：UnityEngine.Object在失去引用后，是一定要调用Destroy进行销毁么？不会被GC自动回收么？ A：UnityEngine.Object是一种基类，题主的问题比较大。常见的有三种情况： C#的托管堆内存在失去引用后，会在GC后被自动回收； 从AssetBundle中被加载出来的资源，需要用AssetBundle.Unload(True)这类的API定向卸载，或在失去引用后被Resources.UnloadUnusedAssets标记并卸载； 还有一种动态创建的对象，如通过new、instantiate生成的资源，则需要手动Destroy，或在失去引用后被Resources.UnloadUnusedAssets标记并卸载。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/686b545d682c7e5cd61bfaba GPU Q1：想问下关于纹理采样的消耗这块，有没有一些相关的经验可以分享？ 从Mali的文档上看，Cache Hit的情况下是1cycle采样1纹素，Cache Miss的情况下需要100-200cycle。 那么如果触发Cache Miss，是从当前采样UV位置的附近的2*2读取到Tex Cache上，还是直接读取L1大小的数据上去。比如Adreno有1KB的L1？ A：从Onchip到L1不是固定的2*2，也不是整个L1；根据GPU型号不同，有一个固定的每次取的大小，纹理压缩率越低、单个像素信息量越大，每次能取的像素数量就会更少，Cache Miss率就更高，此时就会往更远的L1、L2去取。 Q2：那是按1维去取n个像素，还是二维去取m*m个像素？想了解下怎么样摆放数据能Cache友好些。 A：应该是m*m。一般只要开启Mipmap，交由GPU自动选择合适的层级，就能使像素的采集过渡平滑友好，Cache Miss率较低了。此外就是尽量压缩纹理格式、避免使用各向异性这种大幅增加采样次数的做法，也能有效降低带宽。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/687476f2682c7e5cd61bfabf Memory Q：现在我们不能缓存catalog，每次需要临时下载还挺麻烦的。APP的Addressables热更的方案就是先去线上请求catalog.hash文件，然后判断本地的catalog文件是不是旧的，旧的就删除掉本地的catalog文件，然后下载新的。但是在微信小游戏会一直请求catalog，所以我们每次进入游戏都要下载catalog文件。请问微信小游戏该如何缓存catalog.json文件呢？ A：我的方案是自定义了catalog1000、settings1000文件，加上版本号，相当于是自己改了settings文件里面的catalog URL，然后改了catalog文件名，也改了Addressables的源码，取到版本号后去读取指定的settings文件，这样settings文件也可以缓存了。 我的方式是catalog和settings都自定义成了下面这样： 然后settings文件的这一句话改成了我指定的catalog1000： "m_InternalId":"{UnityEngine.AddressableAssets.Addressables.RuntimePath}/catalog1000.bundle" 再改了个Addressables的源码，Addressables在初始化的时候会去找settings文件： 然后就是走微信小游戏自己的规则了，即本地有就读取本地，本地没有下载缓存。catalog和settings都加了版本号，也不怕settings同名或cdn缓存问题。 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/687482d6682c7e5cd61bfac0 Platform Q：微信小游戏现在都是推荐用Unity的Application.targetFrameRate了吗？现在我们用的这个WX.SetPreferredFramesPersecond，会产生什么问题吗？没Unity那个性能好？ A：帧率设置可能失效。现在微信小程序已对Application.targetFrameRate做了较好的适配。 更推荐用这个： https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/issues/713 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/6874a85b3d306f3e9d594f11 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）关于il2cpp.so裁剪的问题 2）如何在UE5的渲染管线里多加一个颜色 3）请问iOS用MallocBinned2的效果如何 4）XCode显示的带宽为什么这么高 这是第414篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 Script Q：我们最近在关注包体问题，发现il2cpp.so里有1/4的代码是Generics GenericMethods，请问有什么方法能减少这些代码生成的同时，运行不报错呢？ 另外对于APP，有没有人测试过IL2CPP Code Generation的两个选项OptimizeSize和OptimizeSpeed对于运行时影响有多大呢？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/675f9b483d306f3e9d594dc9 Rendering Q：UE5的Movie Render Queue Additional Render Pass的功能，将来有没有计划做一个Runtime版的？或者说UE5将来会不会做一个Runtime的Render Graph功能？ 我主要想在整个渲染管线里多加一个颜色。就是在RGB之外再加一个颜色红外IR。请问有什么办法吗？ A：在整个管线里都要多加一个通道的话，单独加一个Render Pass也不解决问题。目前没有纯数据驱动的Runtime Render Graph的计划。但是Runtime在管线特定环节注入自己的GlobalPass甚至MeshPass都是可以的，也可以自己注入自己的结构Patch到SceneUB上。 感谢Abyss@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/675fe1e7682c7e5cd61bf9c9 Memory Q：请问iOS用MallocBinned2的效果如何？我们遇到疑似MallocBinned导致的内存访问和系统调用方面的性能问题，在评估移植MallocBinned2对iOS的支持的收益，有人处理过吗？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/675fe0ff3d306f3e9d594dcc GPU Q：请问XCode显示的这个带宽是指什么？为什么1s会有26G，而且vs那么高呢？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/675fe466682c7e5cd61bf9ca 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
为什么Unity里的变体数和UWA工具测出来的不一样
1）为什么Unity里的变体数和UWA工具测出来的不一样 2）使用TextureArray为什么会导致L1 Cache Miss率变高 3）Gfx.PresentFrame耗时异常高 4）AO方案中哪个更适合移动端 这是第412篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 Shader Q：为什么Unity里的变体数和UWA工具测出来的变体数不一样呢？ A：shader_feature在编辑器中预览只会保留第一个关键字的变体，所以会少显示很多；但实际进入包体的是UWA AssetBundle工具显示的量。 顺便一提，通过变体收集的方式可以让引擎在打包的时候自动去掉一部分shader_feature关键字导致的变体组合，节省部分内存。 该回答由UWA提供，欢迎大家转至社区交流： https://answer.uwa4d.com/question/674d1de4682c7e5cd61bf9b3 GPU Q：做地形的时候用的ID图的方案，将所有的地表贴图做成了TextureArray，Xcode测试性能。发现TextureArray导致Texture L1缓存的Cache Miss率高达77%，且不用就不高了，设备是iPad Air 5。 请问是相邻像素的TextureArray的ID不同导致Cache Miss吗？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/674d2240682c7e5cd61bf9b6 Rendering Q：部分安卓机型上，某个东西显示出来的时候，Gfx.PresentFrame耗时非常高。 以下是真机测试、多线程渲染下的截图： 目前还没找到根本原因，但实验发现： 1. 挂掉出问题的Shader的Clip函数，就不会有问题。 2. 删掉工程内的变体收集器，也不会有问题（新版本和老版本相关的区别就是新收集了一波变体，然后打进了Bundle里）。 目前解决方案就是把变体收集器删了，因为现在就没做Shader预热，所以收集器其实没用到。 请问其本质原因是什么？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/674ab5a93d306f3e9d594db6 Rendering Q：请问以下AO方案中，哪些适合移动端呢？ SSAO、SSDO、HBAO+、GTAO、MXAO、LSAO、VXAO、DeepAO、以及上一年Meta的PSAO。 A：一般动态物体就是烘培AO，也就是单个物体有，静态物体就是LightmapAO也是烘培的，移动端从性能考虑大多还是走烘焙。 只有那些大世界的场景实在是太大才会想各种办法去设计一个GI效果，比如SSAO去凑整个环境的AO效果。 感谢南瓜@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/674d1ff2682c7e5cd61bf9b4 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）高通芯片GPU是否有类似于HSR的功能 ​2）UGUI上的RT动图会不会导致UI更新 3）UI经常迭代外观，如何尽量少改代码 4）开发过程中该使用AssetBundle包模式，还是模拟模式？ 这是第279篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） GPU Q：高通芯片GPU是否有类似于HSR功能？望解答，谢谢。 A：参考如下： 测试案例1：小米9 点击按钮添加全屏不透明的Quad，使用Standard Shader，离相机越远，RenderQueue越小。 结果：从FPS上看，到100层，帧率也都是60帧。说明小米9上确实有类似于HSR的功能，就是不透明对象不需要手动调整渲染队列从而让靠近相机的先渲染。 测试案例2：小米5X 点击按钮添加全屏不透明的Quad，使用Standard Shader，离相机越远，RenderQueue越小。 结果：从FPS上看，1层的时候43帧，2层就掉到25帧，3层掉到17帧。说明小米5X上没有类似于HSR的功能。 测试案例3：小米5X 点击按钮添加全屏不透明的Quad，使用Standard Shader，离相机越远，不调整RenderQueue。 结果：从FPS上看，1层的时候43帧，5层的时候依旧43帧。不调整RenderQueue后，因为Unity的不透明是按照由近到远绘制的，由于Early-Z的关系，导致被遮挡的物体不会进行Fragment Shader计算了，所以帧率比较稳定。 总结：对于像地形这样的大面积且Shader复杂度通常比较高的对象，还是需要调整渲染队列到比较靠后的，因为对于低端机型，并没有HSR这样的功能。所以可以通过调整地形的渲染队列靠后，从而让Early-Z对于大地形生效，从而减少地形的渲染像素。 对于高端机型，如小米9，因为其GPU硬件支持HSR功能，所以对于不透明物体来说，渲染队列没有关系。 总体来说，还是建议调整大地形的渲染队列，对低端机型有好处。 感谢小苗子@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61b6e84cd8413e18eb47e0f4 UGUI Q：在UGUI组件上使用RenderTexture动图会不会导致UI更新？ A：对于这个问题进行了简单的实验：实验设置了一个相机拍摄不停在运动两个物体，然后得到的RenderTexture赋给UGUI上的100个RawImage。在真机上运行，此时UI更新耗时几乎为0。 理论上，这些RawImage的顶点属性没有发生变化，确实不会导致UI更新。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61b6e8a0d8413e18eb47e1c4 UGUI Q：问题：之前的项目是用Lua做UI开发的。一个ToLua，一个XLua。策划或美术经常要调已经做好的UI的外观，一般需要变布局或节点层级。最早，代码里访问某个节点都是用GameObject.Find找到节点，层级关系一变，就需要程序配合改，很麻烦。怎么做到调整UI，不用改代码呢？ 思路：想到一个命名规范方案：程序拿到UI的Prefab绑上UI关联Lua的脚本后，把所有程序需要访问的节点，改名成rd_xxxxx，脚本控制下的子节点rd_开头的不能重复，如果有列表的子节点，上面再挂一个辅助的脚本（比如UItemHelper）。 Lua第一次加载时遍历所有节点，缓存所有rd_xxx的节点关系到脚本里（Lua里），这样，Lua代码里直接用self.rd_xxxxx就能得到对象，也不怕Prefab层级调整，也不用多次调用GameObject.Find。 规定其他人员调整UI Prefab时，不能改掉rd_xxxxx，如果需要改则要程序配合。 参考代码：ToLua项目的一个参考修改: 关联Lua脚本对象的LuaXXX.cs里（比如叫LuaMono.cs）初始化，增加： …… if (m_params.Count == 0) { //lua关联对象自动收集 SetRDObjectRef(luaClass, this.gameObject, true); } else { //节点整理期间的兼容(复用UI也走这里，避免反复搜集) foreach (ParamItem pi in m_params) { luaClass[pi.name] = pi.value; } } …… /// /// 把节点下所有节点查一下，如果名字是"rd_"开头的则是程序需要 /// /// /// void SetRDObjectRef(LuaTable luaClass,GameObject go,bool is_root = false) { if(go) { if(MyExtensions.StringStartsWith(go.name,"rd_")) { #if UNITY_EDITOR //检查是否有重复 if(luaClass[go.name] != null && !luaClass[go.name].Equals(null)) { GameFramework.Log.Error("{0} LuaClass already have GO key:{1} {2}", LuaScript, go.name, luaClass[go.name]); } #endif luaClass[go.name] = go; ParamItem item = new ParamItem(); item.name = go.name; item.value = go; m_params.Add(item); } if(is_root || (go.GetComponent<LuaMono>() == null && go.GetComponent<UItemHelper>() == null)) { //遍历所有子节点 for(int i=0;i<go.transform.childCount;++i) { SetRDObjectRef(luaClass, go.transform.GetChild(i).gameObject); } } } } UItemHelper.cs using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; public class UItemHelper : MonoBehaviour { #if UNITY_EDITOR public class ParamItem { public string name; public UnityEngine.Object value; } #endif Dictionary<string, GameObject> m_nodes = new Dictionary<string, GameObject>(); bool init = false; #if UNITY_EDITOR [SerializeField] protected List<ParamItem> m_params = new List<ParamItem>(); #endif void Awake() { Init(); } public void Init() { if(!init) { m_nodes.Clear(); SetRDObjectRef(this.gameObject, true); init = true; } } /// /// 把节点下所有节点查一下，如果名字是"rd_"开头的则是程序需要 /// /// /// void SetRDObjectRef(GameObject go, bool is_root = false) { if (go) { if (MyExtensions.StringStartsWith(go.name, "rd_")) { #if UNITY_EDITOR //检查是否有重复 if (m_nodes.ContainsKey(go.name)) { GameFramework.Log.Error("sub item node {0} already have GO key:{1} {2}", this.name, go.name, m_nodes[go.name]); } else #endif { m_nodes.Add(go.name, go); #if UNITY_EDITOR ParamItem item = new ParamItem(); item.name = go.name; item.value = go; m_params.Add(item); #endif } } if (is_root || (go.GetComponent<LuaMono>() == null && go.GetComponent<UItemHelper>() == null)) { int childCount = go.transform.childCount; Transform t = go.transform; //遍历所有子节点 for (int i = 0; i < childCount; ++i) { SetRDObjectRef(t.GetChild(i).gameObject); } } } } public GameObject GetNode(string name) { if(m_nodes.ContainsKey(name)) { return m_nodes[name]; } return null; } public Image GetNodeImage(string name) { if (m_nodes.ContainsKey(name)) { GameObject go = m_nodes[name]; if (go != null) { Image img = go.GetComponent<Image>(); return img; } } return null; } public Text GetNodeText(string name) { if (m_nodes.ContainsKey(name)) { GameObject go = m_nodes[name]; if (go != null) { Text txt = go.GetComponent<Text>(); return txt; } } return null; } public Button GetNodeButton(string name) { if (m_nodes.ContainsKey(name)) { GameObject go = m_nodes[name]; if (go != null) { Button btn = go.GetComponent (); return btn; } } return null; } // 后期添加的节点 public void AddNode(GameObject go) { #if UNITY_EDITOR //检查是否有重复 if (m_nodes.ContainsKey(go.name)) { GameFramework.Log.Error("sub item node {0} already have GO key:{1} {2}", this.name, go.name, m_nodes[go.name]); } else #endif { m_nodes.Add(go.name, go); #if UNITY_EDITOR ParamItem item = new ParamItem(); item.name = go.name; item.value = go; m_params.Add(item); #endif } } // 删除后期添加的节点 public void RemoveNode(GameObject go) { #if UNITY_EDITOR if (m_nodes.ContainsKey(go.name)) #endif { m_nodes.Remove(go.name); #if UNITY_EDITOR ParamItem item = new ParamItem(); item.name = go.name; item.value = go; m_params.Remove(item); #endif } } public Dictionary<string, GameObject> GetAllRdGameObject() { return m_nodes; } } 其他：当然各个项目情况不一样，需要各自实现。不知大家项目里遇到类似问题，用的是什么方案？ 比如有的方案不希望一开始初始化，可以在创建关联Lua时，设置元表，重写_index，当self.rd_XXX这样访问时，取到索引的前缀是rd_，则去判定缓存，没有则去找对应节点关联。 A1：基于路径的UI控件查找还是比较脆弱，直接使用Unity序列化引用更好，可以随意修改结构改名字不用担心丢失。 这里有一个不错的方案：UIControlBinding 感谢张迪@UWA问答社区提供了回答 A2：别用GameObject.Find方法，编辑器序列化直接绑定到Lua。和使用原生CS绑定一样。 感谢1 9 7 3-311135@UWA问答社区提供了回答 A3：把UI控件直接序列化到C#脚本上，运行时不要用Find和GetComponent，UI面板的Prefab实例化后，直接把这些对象塞到一个Lua Table里传给Lua。 感谢唐崇@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61af21abd8413e18eb3d51b1 Asset Q：使用AssetBundle包，能够较大程度保证资源的正确性，但一点资源的修改就要打包，比较浪费时间，特别是美术、策划同学对它比较抵制；使用模拟模式，大概只是开发效率高一些，效果错误、循环引用等问题要延后到打包后才能发现（当然有很多其他方式可以提早发现，但属于附加手段）。 最近项目中发现，即使预加载了资源，Play过程中还是会出现非常严重的卡顿（1s+），这么长时间的卡帧导致表现效果直接不对了：比如，1s从起点移动到终点的过程，直接变成闪现到终点，成了Bug。 卡顿原因查下来是：AssetDatabase.Load出来的Prefab并不会将其依赖的Texture一起加载进内存，Prefab第一次渲染的时候Texture才会加载。我们这一个很复杂的（未优化）技能特效，用了40MB+的Texture，把UploadTexture卡了。 以这样一个特殊的问题为契机，准备重新考虑下要实行的方案，希望大家能多给些建议，可以是二者的对比选择，也可以是其他更好的做法或实现。 A：把Unity项目放在高速SSD中应该能缓解不少。 之前见到一个方案是在预加载时，创建一个玩家看不到的相机，把技能特效的Animator、ParticleSystem和音频组件等都执行一下，同时调用Camere.Render，强制把各类资源都WarmUp好，这样进入游戏后基本没有IO操作，应该能大大缓解题主描述的问题。 针对一些特效是被Timeline的轨道管理的，然后Timeline本身是一个独立模块的，可以根据技能配置找到特效（技能配置关联Timeline，Timeline关联特效），然后对特效预渲染就可以了，Timeline只是逻辑流程控制器。当然如果Timeline本身卡顿，还是要预执行一遍更好。 感谢张迪@UWA问答社区提供了回答，欢迎大家转至社区交流：： https://answer.uwa4d.com/question/61af045cd8413e18eb3d22c6 20211213 更多精彩问题等你回答～ Unity增量打包AssetBundle没变化的资源也会被重新打包 在模型有UV2的情况下开启Generate Lightmap UVs 如何实现AAB包的增量更新 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
本期聚集话题：PBR在手游开发中的适用性、如何设计渲染等级（进阶版）、如何判断硬件支持GPU Instancing、25个你需要规避的DrawCall增长的问题... 这是第109篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间15分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群：793972859（仅限技术交流） 渲染 Q1：我们是MMO游戏，现在想给主角（包括其它玩家）使用PBR材质（Untiy自带的Standard），其它角色和场景不使用。不知这种情况下性能是否能抗住，同时我也想了解下目前手游中对于PBR材质的使用情况。 UWA：目前手游中使用PBR材质的项目明显增多，特别是MMO、RPG等项目中，主要的角色、NPC和大Boss等都开始往PBR方面走了。 如果PBR仅是在以上情况下使用时，Unity引擎自带的PBR是没有太大性能问题的，因为一般角色占据的屏幕面积都会比较小，而过剧情时，虽然屏幕面积占据较大，但时间一般都很短，所以给总体性能造成的压力不大。 但是UWA暂时还不建议大家在地形等常常在屏幕中占据较大面积区域的物体上使用PBR（尤其是中低端设备），因为它确实会造成较高的GPU压力，我们在UWA Day 2018上也对此进行了定量分析（类似下图），这点需要研发团队在使用时注意。但是，一切应该以自身项目测试为主，具体查看项目在自己设定的低配机器上到底性能如何。 最后，在使用Standard Shader时，除了关注性能外，还要关注下它对于内存的影响，可以参考这篇文章：【求知探新】Unity中ShaderLab内存优化 该回答由UWA提供，欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5ae2f40132493548fbd32bbc 渲染 Q2：UWA DAY 2018大会中说有25种造成DrawCall增长的原因，我想知道是哪25种呢？ 导致批处理失败的原因如下： 1、Additional Vertex Streams — 对象使用MeshRenderer.additionalVertexStreams设定了额外的顶点信息流。 2、Deferred Objects on Different Lighting Layers — 该物件位于另一不同的光照层中。 3、Deferred Objects Split by Shadow Distance — 两个物体中有一个在阴影距离范围内而另一个不是。 4、Different Combined Meshes — 该对象属于另一个已合并的静态网格。 5、Different Custom Properties — 该对象设定了不同的MaterialProperyBlock。 6、Different Lights — 该物件受不同的前向光照（Forward Light）影响。 7、Different Materials — 该对象使用不同的材质。 8、Different Reflection Probes — 该对象受不同的反射探头（Reflection Probe）影响。 9、Different Shadow Caster Hash — 该对象使用其他的阴影投射着色器，或是设定了不同的着色器参数/关键词，而这些参数/关键词会影响阴影投射Pass的输出。 10、Different Shadow Receiving Settings — 该对象设定了不同的“Receive Shadows”参数，或是一些对象在阴影距离内，而另一些在距离之外。 11、Different Static Batching Flags — 该对象使用不同的静态批处理设定。 12、Dynamic Batching Disabled to Avoid Z-Fighting — Player Settings中关闭了动态批处理，或在当前环境中为避免深度冲突而被临时关闭。 13、Instancing Different Geometries — 使用GPU Instancing渲染不同的网格或子网格。 14、Lightmapped Objects — 对象使用了不同的光照贴图，或在相同的光照贴图中有不同的光照贴图UV转换关系。 15、Lightprobe Affected Objects — 对象受其他光照探头（Light Probe）影响。 16、Mixed Sided Mode Shadow Casters — 对象的“Cast Shadows”设定不同。 17、Multipass — 对象使用了带多个Pass的着色器。 18、Multiple Forward Lights — 该物件受多个前向光渲染影响。 19、Non-instanceable Property Set — 为instanced着色器设定来non-instanced属性。 20、Odd Negative Scaling — 该对象的缩放为很奇怪的负值，例如(1,-1,1)。 21、Shader Disables Batching — 着色器使用“DisableBatching”标签显式关闭了批处理。 22、Too Many Indices in Dynamic Batch — 动态批处理索引过多（超过32k）。 23、Too Many Indices in Static Batch — 静态批处理中的组合网格索引过多。对于OpenGL ES来说是48k，OSX是32k，其他平台是64k。 24、Too Many Vertex Attributes for Dynamic Batching — 欲进行动态批处理的子网格拥有超过900个顶点属性。 25、Too Many Vertices for Dynamic Batching — 欲进行动态批处理的子网格顶点数量超过300个。 感谢果果爸@UWA问答社区提供了回答，欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5ae1eef932493548fbd32ba1 GPU Q3：我在国产安卓机上发现不少据说支持ES 3.0、但是实际上却不支持GPU Instancing功能的情况。那么现在大家都是如何判断硬件支持GPU Instancing的？ UWA：建议使用SystemInfo.supportsInstancing，链接： https://docs.unity3d.com/ScriptReference/SystemInfo-supportsInstancing.html 该回答由UWA提供，欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5ae714b00956834900752685 渲染 Q4：接我们上期的问答《如何设计项目中的渲染等级》推出后，又有两位行业大咖分享了他们的灼见： 王亮：我们的项目还在开发中，也经历了同样的过程，最后也形成了类似的文档。这个过程中我的体会是，虽然感觉最后形成的是一个表，会根据这个表提供机型的预设配置。但是实际上针对自己的项目，高中低主力机型，FPS的目标，渲染效果的要求不同，结果可能会大不一样。 抛砖引玉分享一下我的思路： 1、首先是确定测试用例 我们分别选取主城镇、不同战斗类型的最高面数场景作为场景用例 2、角色方面选择不同战斗类型的最高面数角色 3、接下来配置测试关卡，编写采样脚本 本次测试数据采用脚本记录帧数功能 测试方法集成到了Cheat上，用于测试指定时间的帧数范围和平均帧数粗略地描述性能 满怪渲染：主角绕着小怪 把测试的所有小怪聚拢 保持同屏全部渲染 满怪释放技能：尽量保证打中尽可能多的小怪 验证粒子压力 4、请测试同学跑测试组合用例，并且采样数据 我选取第2次测试报告作展示如下： 其实中间经过了几次迭代。表格里的最后一列是针对帧数目标，经过反复调整配置形成的数据。 5、最后根据测试数据，调整分级表的配置 最终形成的这次配置如下，红色是本次测试，调整和新增的内容： 以及本次测试用发现的问题： 感谢王亮@UWA问答社区提供了回答 文雅：正好我也整理过项目的LOD规则，可能不限于题主描述的渲染等级。一套好的LOD系统需要美术在资源上做大量的资源分级工作，需要程序搭建完善的LOD框架和辅助工具，每增加一个功能模块都需要把LOD考虑进来。 一、如何确定项目的LOD分档和性能标准 LOD（Level of Detail），这里的D代表Detail而不是Distance，也就是说，所有游戏画面和游戏功能的细节都可以进行分级，不必受限于距离。 1、如何制定性能标准？ 在分级之前，需要确定目标（省电、正常的游戏体验、噱头），我们要在哪一种机型上面运行哪些表现效果和功能，达到怎样的性能目标（帧数FPS、内存占用、Drawcall、同屏三角面数等）。 ◆ 经验总结： 1）画质的表现力与性能开销是相互矛盾的 2）需要持续维护，相关功能系统的设计都需要考虑到不同画质 3）极简画质牺牲了可玩性，同时会增加LOD框架的复杂性，使用最简单暴力的方式去处理 ◆ 兼容性问题： 1）低配画质需要考虑的适配问题 • 可能不支持Shader定义8张以上的贴图纹理 • 可能不支持ETC2格式的贴图纹理 • 可能不支持OpenGL ES3.0 2）高配画质需要考虑的问题 • 支持PBR、线性空间 • 奇怪的Bugs 3）需要解决各个画质下的兼容性问题，建立黑名单和白名单。 2、如何找出可以进行分级的细节？ ◆ 优先找出开销大的点 • 后处理效果Bloom、HDR、ToneMapping、MotionBlur、DOF等 • 实时光照和阴影、水面实时反射 • PBR物理光照 • Ragdoll、DydamicBones等物理系统 • 日夜循环、特效天气系统 ◆ 细节模块的LOD功能考虑 • 场景 / 角色 / 特效 / 摄像机相关 / 其他系统模块 二、LOD模块细分 1、场景相关 ◆ Shader LOD • shader.globalMaximumLOD来指定不同画质的LOD值 • Shader内部定义多个SubShader，逐个降低计算和纹理采样 使用Shader LOD存在一个问题，在Properties中定义的贴图，在低级别的SubShader中虽然不被采样和计算，但是仍会有内存占用。建议和角色一样，设计LOD框架时考虑两份prefab，支持场景转一份低配场景文件给低配使用。 ◆ 制作时合理使用高低模 ◆场景物件分级Level_1、Level_2、Level_3 • 含场景特效、场景动画物件等，在不同的级别显示/隐藏 最早期我们采用距离来控制场景特效的显示。 缺点： 1、实时监控距离的开销 2、每个场景需要单独配置合理的距离值 使用层级来控制更为直接，在制作LOD时最优先考虑最简单暴力的方式，这样美术规范也不会太复杂，后期也不需要过多的维护。 ◆ 场景光照切换 • Light/Light_High 涉及实时灯光对角色、场景阴影绘制的控制 • 场景光照图切换 ◆ 物件的Layer层决定阴影 • 场景文件中配置的Cast Shadows和Receive Shadows设置给烘焙使用，不方便存储下来 • 设计不同的Layer来决定物件是否产生阴影和接受阴影 ◆ 角色产生阴影 • 高配时玩家和Npc角色的Layer修改为Shadow，低配时修改为Player/Npc • 中配时玩家自身和Boss角色的Layer修改为Shadow，其他玩家为Player • Shadow层绘制实时阴影，Player/Npc会使用圆片来绘制脚底阴影 ◆ 角色接收阴影 • 仅在高配画质开启接收阴影 ◆ 裁剪距离、雾效距离 • 设计裁剪距离与雾效距离按照百分比减小的算法，防止数值异常 ◆ 后期处理效果 • 全局后期处理 通过全局后期处理管理后期的开启、关闭，控制自定义的选项。 • 场景后期处理 场景配置高、低两种后期处理方案，低配时只使用ColorGrading • UI和剧情动画中的后期处理只在高配时开启 比如：UI中使用的Bloom、剧情中使用的RadialBlur运动模糊等 ◆ 单个场景的特效等级 通过配置表定义场景的类型和同屏可显示的特效数量和等级 2、角色相关 ◆ 角色材质、shader、贴图 • 通过配置表，在不同画质调用不同的prefab • A.prefab和A_Low.prefab使用A.mat和A_Low.mat • A_Low.mat使用的Shader降低计算和贴图纹理采样 • A_Low.mat使用的_MainTex替换低精度贴图纹理 ◆ LODGroup • 角色的模型面数标准提升后，制作高模、低模两档，通过LODGroup功能按距离降档 ◆ SubShader • 高配和中配的光照模型切换，PBR切换Blinn-Phong ◆ 角色部件 • 部件在配置表中可配置为空，在低配时可以不显示背部挂件 ◆ 角色骨骼Skin Count • Skin支持的最大骨骼数降级2-Bones 1-Bones • DynamicBone基于物理系统的动态骨骼效果的开关 • Npc死亡动作基于物理系统的表现的开关 ◆ 同屏角色数量 • 不同画质设置不同的同屏显示角色上限 • 无逻辑任务的客户端NPC开关 3、特效相关 ◆ 天气系统 • 制作不同数量级的粒子系统 ◆ 脚步特效 • 开启和关闭脚步特效，可对玩家自身和其他玩家分开控制 ◆ 掉落特效 • 复杂特效可以制作简化版本 以上三种特效都可以通过简单的配置表功能完成。 ◆ 技能特效 • 通过配置表，在不同画质调用不同的Prefab ◆ 技能特效的标准 • 制作阶段控制特效的性能开销 • 工具辅助生成*_Low.Prefab • 严格控制*_Low.Prefab的性能开销 4、其他模块 ◆ 渲染分辨率 • 使用不同的渲染分辨率，且限制最高分辨率为1080p ◆ 开放视角 • 视角上下左右旋转和 • 摄像机最远最近距离 ◆ 脚本控制 特殊情况下需要制作可以在四种画质下进行切换的配置，通过脚本控制： • Prefab切换 • Material切换 5、不适合LOD的系统模块 ◆ UI • UI上的图素和特效不适合做显隐和优化 • UI场景不适合做烘焙和实时光的切换 • UI角色可以根据界面的重要程度选择使用高级或低级 • 管理好加载和卸载后，可以考虑替换高低配UI资源 ◆ 剧情 感谢文雅@UWA问答社区提供了回答 欢迎大家转至社区进行进一步交流： https://answer.uwa4d.com/question/5acc208b425802635474fc7d 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站（answer.uwa4d.com）上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com 官方技术QQ群：793972859（仅限技术交流） 本文封面图来源： https://www.artstation.com/artwork/nWlL6
本期聚集话题：Texture2DArray、远距离物体的物理模拟、粒子系统的CPU/GPU开销、如何读取StreamingAssets目录下的压缩包... 新年伊始，随着大家紧锣密鼓地开始了工作，UWA每周推送的知识型栏目《厚积薄发 | 技术分享》在节后的首个工作日迎来了第100篇！今天，我们继续为大家精选了5个和开发、优化相关的问题，建议阅读时间15分钟，认真读完必有收获。如果您有任何独到的见解或者发现也欢迎联系我们，一起探讨。 UWA QQ群：793972859 UWA 问答社区：answer.uwa4d.com 粒子系统 Q1：请问粒子系统的消耗如何区分呢？哪部分是在CPU哪部分在GPU呢？一个空粒子（禁用所有模块，包括Render）会有消耗吗？如果有消耗，会体现在哪部分呢？ 就目前的Unity引擎的原生功能而言，粒子系统中的状态更新（粒子的位置、朝向、触发事件等）均为CPU端开销，具体表现在ParticleSystem.Update和ParticleSystem.EndUpdateAll等函数中。具体可以查看UWA性能报告中的这两张图： 相关链接 相关链接 同时，粒子系统在渲染时也需要在CPU端进行准备，比如粒子系统的Culling和Draw Call操作等等，这些也都属于CPU端开销，如下图所示。 而GPU端的耗时则与其他Mesh渲染无差，主要和ALU、Bandwidth和Overdraw相关。但与其他一般场景渲染不同的是，粒子系统在GPU端的压力往往在Overdraw中进行体现，如下图所示： 上图来自《极无双》的性能详细报告，大家可以参考完整数据来加深这部分的理解： 一个空粒子其自身开销理论上很小，但UWA这边暂时没有做过测试，题主可自行做一些测试，绝知此事要躬行。 此问答来自于UWA 问答社区，如您对该问题仍有疑问，欢迎转至社区进行进一步交流。 资源管理 Q2：我用的是Unity 5.5 版本，由于项目采用LZ4格式的AssetBundle，如果直接放进StreamingAssets下，包就太大了。现在想把所有的AssetBundle打成一个压缩包，再放进StreamingAssets，让玩家在第一次安装时，拷贝到PersistentDataPath目录下再解压。但目前不知道怎么读取Android平台下的StreamingAssets目录下的压缩包，希望能得到指导，感谢！ 我们是直接使用的C实现的unzip库，启动游戏的时候就常驻打开Apk，后面都直接通过unzip库解需要的文件到缓存目录来读取，读完了，不用就定期做一些清理。 感谢Lujian提供了以上回答 在Unity的API里应该就只有WWW（新版本里对应UnityWebRequest）能访问非Bundle文件了，但这个接口是异步的，另外.bytes操作会产生较高的堆内存分配，容易撑高堆内存峰值。所以，有的做法是通过Android的Java API来读取，可以参考下雨松MOMO的这篇文章里用的Java接口：https://www.tuicool.com/articles/fAnYJ3I 此问答来自于UWA 问答社区，如您对该问题仍有疑问，欢迎转至社区进行进一步交流。 资源管理 Q3：我用的是Unity 2017.2f版本，我们Prefab里Image元素直接引用的是Sprite Atlas文件夹里的图片，现在把Prefab和Sprite Atlas分别打到两个不同的AssetBundle里，并且去掉了Sprite Atlas的include build的选项。拿工具查看Prefab和Sprite Atlas的Bundle发现，Prefab所在的Bundle会把Image元素引用的那张Sprite图也打进来，而Sprite Atlas所在的Bundle里不但包括了这个Atlas图，并且原图都在这个Bundle里。这是为什么呢？ UWA测试发现Unity 2017.2和2017.3都存在题主说的问题，不过也都能通过一些操作绕过这个问题。先说一下个人认为正常的现象： include in build不应该影响Bundle的打包行为，且Prefab的Bundle中始终不应该有SpriteAtlas存在。但是如果不开启include in build，正常加载完两个Bundle，且实例化Prefab后，Sprite不会显示，因为需要做一下late binding。 接下来说一下碰到的这个Bug： 如果在第一次（打之前清空Bundle目录）打Bundle时，include in build开启了，那么atlas会被打包进prefab的bundle中（即题主的问题）； 但如果在第一次打Bundle时，include in build没开启，那么就是正常现象（Atlas不在prefab的Bundle中）； 有意思的是，在第二次打Bundle时，不论include in build有没有开，结果都和上一次打包一样。所以，绕过去的方法是： 先清空Bundle目录，关闭include in build打一次Bundle；再开启include in build打第二次。得到的结果就是Atlas不在Prefab的Bundle里，同时Atlas的include in build是开启状态的。 题主可以按这个步骤做个尝试。 此问答来自于UWA 问答社区，如您对该问题仍有疑问，欢迎转至社区进行进一步交流。 渲染 Q4：我从文档上来看，GLES3 Metal 已经可以支持了。最容易想到的就是地形的splat层，如果是四层splat，那么就可以用一个Texture2DArray来代替，好处是减少了bind消耗。相关的知乎链接，其中根据龚大的意思来看，似乎还可以减少splat采样次数，但根据自己的测试和理解来看并不行，因为Texture2DArray 的slice之间并不会 blending，不知是不是我理解问题。 我使用一个Texture2DArray来代替4层的Splat地形，从XCode上看开销，并没有任何的减小，那目前来看这么做地形的意义就不是很大了。AssetStore上有一个插件叫MegaSplat就是使用Texture2DArray来达到很多层混合。目前想到的Texture2DArray还有一个可以利用的地方是场景贴图。比如场景用到了多张1024的贴图，或者多张lightmap，原来由于贴图不同导致StaticBatching无法合并，现在就可以使用Texture2DArray来做了。还有比如UI上的icon之类的，可以合并到一个Texture2DArray来达到DrawCall的合并。 目前Texture2DArray最不利的地方是：只能通过代码来创建，没有编辑器的支持。如果要离线制作Texture2DArray，就需要为不同的平台准备多份资源了。请问大家有没有Texture2DArray的经验可以分享呢? UWA研究了Texture2DArray，看起来它是将多个2D的Texture组合起来变成一个对象，所以在使用的时候只需要绑定一次，就可以采样多个2D Texture。的确如题主所说，采样的时候还是一次只能采样一个指定的slice，blend也需要另外的Shader代码来完成。 题主说了：其中根据龚大的意思来看，似乎还可以减少splat采样次数，但根据自己的测试和理解来看并不行 我感觉他的意思可能是：并不是每次采样必须把所有的slice都采了，而是可以只采其中一部分。估计原本想表达的是一次采样只采一个slice。 我使用一个Texture2DArray来代替4层的Splat地形，从xcode上看开销，并没有任何的减小，那目前来看这么做地形的意义就不是很大了。 如果单从性能上看，Texture2DArray比Texture2D的确只是减少纹理绑定的开销，然后在游戏引擎中可能会对合批产生影响。其他的做法跟普通Texture2D是一样的。Texture2DArray比Texture3D在LOD处理上不同，Texture3D会减少slice，这并不是渲染Terrain时想要的。然后Texture2DArray在filter的时候只会在U,V上做，而Texture3D还会在d上做，所以这部分Texture2DArray也比Texture3D性能更好。综合这些因素可能是推荐渲染Terrain使用Texture2DArray的原因吧。题主通过实验说明Texture2DArray渲染地形没有减少开销，也有可能是因为一般一个场景就一个地形，从绑4张纹理变成绑1张就少了几毫秒，而且也不是每帧都绑，所以从整体效果上看不大出来。 UWA做了个简单实验验证了一下，下图分别是用Texture2D和Texture2DArray渲染地形结果和GLES API调用。实验设备为三星S6。 Texture2D： Texture2DArray： 其中，GLES API调用图中红色框表示纹理绑定的API调用，绿色框表示渲染API调用。WT表示了该API调用的耗时，单位为纳秒。从Texture2D图中可知一共有5次纹理绑定，分别对应于材质中的5张纹理绑定，而Texture2DArray图中只有两次调用，分别是splat纹理和四层混合纹理的绑定。从图中可知一次glBindTexture的耗时大约为1000~10000ns，即最多0.01ms。因此，三次glBindTexture加上三次glActiveTexture也最多0.06ms。所以看不出来。 此问答来自于UWA 问答社区，如您对该问题仍有疑问，欢迎转至社区进行进一步交流。 物理 Q5：我们正在开发一款超大地形的MMO游戏，场景中的角色或者怪物较多，请问Unity引擎是否会对远处（500m或1km外）的GameObject进行物理模拟？是否有设置可以降低这部分GameObject的物理模拟开销？ Unity引擎确实会对远处的GameObject进行物理计算，只要该物体是Active、挂载Rigidbody（Character Controller）组件并且没有Sleeping的。Unity引擎并没有原生功能可以根据距离的远近来调整物理系统的计算，不过题主可以自行来进行控制，比如通过Culling Group或者自行计算GameObject的远近来控制Rigidbody组件的Active和Deactive，从而降低整体的物理系统耗时。 此问答来自于UWA 问答社区，如您对该问题仍有疑问，欢迎转至社区进行进一步交流。 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com 官方技术QQ群：793972859（仅限技术交流）