尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Unity性能优化实战:遮挡剔除与LOD技术深度解析

Unity性能优化实战:遮挡剔除与LOD技术深度解析 1. 项目概述为什么你的Unity项目需要性能优化做Unity开发的朋友尤其是做移动端或者中大型场景的肯定都遇到过这样的场景场景里东西一多帧率就开始“跳水”手机发烫玩家抱怨卡顿。很多时候我们第一反应是去优化代码逻辑检查Draw Call这没错。但有一个更底层的、对性能影响巨大的“隐形杀手”常常被忽视那就是渲染负载。简单来说GPU在每一帧需要绘制的东西太多了。一个复杂的城市或森林场景成千上万的模型即使它们被其他建筑或山体完全挡住或者远到在屏幕上只是一个像素点Unity的默认渲染管线依然会忠实地尝试去处理它们。这种“无效劳动”是性能的巨大浪费。“遮挡剔除”和“LOD”就是专门用来解决这个问题的两把利剑。它们不是代码层面的优化而是渲染策略的优化属于“用巧劲”。遮挡剔除解决的是“看不见就不画”的问题而LOD解决的是“离得远就画简单点”的问题。这次分享我就结合自己踩过的坑和实战经验来深度聊聊如何在Unity里用好这两项技术特别是针对从SolidWorks等工业软件导入的复杂模型以及移动端性能敏感的场景。这不仅仅是打开几个开关里面有很多门道和细节处理好了帧率提升30%以上是常有的事。2. 核心原理与方案选型知其所以然在动手之前我们必须搞清楚这两项技术到底在做什么以及Unity提供了哪些方案。盲目开启功能有时反而会适得其反。2.1 遮挡剔除如何让引擎“学会”看不见遮挡剔除的核心思想是空间管理。想象一下你站在一个堆满杂物的房间门口你的视线只能看到门口附近的几件东西更深处的物品被前面的杂物挡住了。一个“笨”的渲染器会把房间里所有物品都画一遍而一个“聪明”的渲染器会先判断哪些物品在你的视线范围内且没有被挡住只画这些。Unity主要提供了两种遮挡剔除方案静态遮挡剔除这是最常用、效果也最显著的一种。它针对的是场景中位置固定不变的静态物体比如建筑、山体、大型摆设。其工作流程是“烘焙”原理编辑器模式下Unity会将场景空间划分为许多细小的单元格Cells然后从无数个潜在视角通常通过设置采样点向这些单元格发射射线计算并记录下从每个单元格看向其他单元格时哪些物体会被遮挡。结果生成一张“遮挡数据图”。运行时引擎会根据摄像机当前所在的单元格快速查表决定哪些物体需要渲染。优点运行时开销极低性能提升效果立竿见影。缺点只适用于静态物体。动态物体玩家、怪物、可移动的门无法参与遮挡其他物体但可以被静态物体遮挡。动态遮挡剔除这更像是一种实时计算的简化版。例如Unity的Occlusion Culling组件和Occlusion Area。它通过创建一些简单的包围体如盒子来动态地启用或禁用其内部物体的渲染。当摄像机看不到这个包围体时其内部所有物体都不渲染。适用场景适用于室内场景切换或者一大组需要同时显示/隐藏的动态物体如一个房间里的所有家具。注意这不是真正的逐物体精确剔除而是一种基于区域的粗粒度管理。对于绝大多数包含复杂静态场景的项目静态遮挡剔除是必选项。我们的优化实战也将围绕它展开。2.2 LOD技术细节的智慧取舍LOD的全称是Level of Detail即细节层次。它的理念非常直观一个模型在屏幕上很小的时候你用高精度模型那几万个三角形去渲染和用一个简化版只有几百个三角形的模型去渲染最终玩家看到的画面几乎没区别但GPU的压力天差地别。Unity的LOD Group组件让我们可以为一个物体设置多个不同精度的模型版本例如LOD0是高模LOD1是中模LOD2是低模LOD3可能只是一个Billboard面片。引擎会根据物体与摄像机的距离或其在屏幕上所占的像素比例自动切换不同的模型版本。这里的关键在于距离阈值的设置和模型制作的合理性。一个常见的误区是为了优化而盲目添加LOD结果中低模制作粗糙在切换距离附近出现明显的“跳变”或形变反而影响视觉体验。优秀的LOD优化应该是“无感”的玩家在正常游戏过程中几乎察觉不到模型的切换。2.3 方案选型的核心考量为什么优先选择静态遮挡剔除LOD的组合覆盖范围互补遮挡剔除解决了“同距离下被挡住的物体”的问题LOD解决了“同一物体在不同距离下”的问题。两者结合能从空间和细节两个维度全面压榨无效渲染。开销与收益比高静态遮挡剔除是预计算运行时零开销LOD的运行时开销仅仅是计算距离和切换渲染模型开销极小。但它们带来的性能收益尤其是顶点处理和像素填充方面的收益是巨大的。特别针对复杂模型对于从SolidWorks等工业软件导入的、拥有极多内部结构和不规则形状的精密模型其三角形数量可能异常之高。直接放入场景是灾难性的。我们必须通过LOD为其创建简化版本并通过合理的遮挡剔除避免其内部复杂结构在不需要的时候被渲染。3. 静态遮挡剔除实战从烘焙到调优理论说完了我们进入实战环节。我会以一个包含厂房、管道、设备假设部分由SolidWorks模型导入的中型工业场景为例讲解完整流程。3.1 场景准备打好地基在点击“Bake”按钮之前90%的问题都出在准备工作没做好。物体标记为Static这是最关键的一步。在Hierarchy中选中所有位置固定、不会移动的物体建筑、地面、静态设备在Inspector右上角勾选“Static”复选框。这不仅是为了遮挡剔除也影响光照烘焙、导航网格等。你可以批量选择后统一设置。注意对于从SolidWorks导入的复杂装配体在Unity中可能是一个包含许多子部件的父物体。你需要确保这个父物体是Static的或者将其子部件合理地拆分并分别设置为Static。一个常见的做法是将内部永远不可见的精密结构如设备内部的齿轮组单独做成一个子物体并放置在不会被摄像机看到的起始位置或为其设置专门的LOD。优化网格和材质合并静态物体使用Unity的Static Batching静态合批。对于使用相同材质的多个小静态物体如一堆螺丝、管道零件合批可以大幅减少Draw Call。在Player Settings中启用静态合批并将物体的Static标记包含“Batching Static”即可。检查材质球确保静态物体使用的材质球是合理的避免使用过于复杂的Shader。对于大量重复的物体共用材质球实例。Occluder和Occludee在Occlusion窗口的Object标签页你可以看到这两个参数。Occluder Static指能挡住其他物体的物体如厚墙、大山Occludee Static指能被挡住的物体如小石块、草丛。通常大型建筑物应同时勾选两者既能挡别人也能被别人挡而一些小物件可以只勾选Occludee。对于从SolidWorks导入的、内部结构复杂的设备模型建议将其设置为Occludee而非Occluder因为用其复杂外形去精确遮挡其他物体的计算成本很高且收益有限。3.2 烘焙参数详解平衡精度与效率打开Window - Rendering - Occlusion Culling切换到Bake标签页。这里的参数决定了烘焙结果的质量和效率。Smallest Occluder最小的遮挡物。单位是米。比如设置为1那么任何尺寸小于1米的物体将不会被当作遮挡物Occluder即使它勾选了Occluder Static。这是最重要的参数之一。设置得太小如0.1会产生大量细小的遮挡单元烘焙时间暴增数据量也大可能得不偿失。设置得太大一些本应起到遮挡作用的小墙柱、箱子就被忽略了剔除效果变差。我的经验是从场景中主要遮挡物如墙壁厚度、柱子宽度的典型尺寸开始尝试通常设置在0.5-2.0米之间。对于工业场景中的管道、细梁需要根据其实际遮挡作用谨慎调整。Smallest Hole最小的孔洞。单位是米。如果一个遮挡物上有洞比如窗户、栅栏这个参数决定了多小的洞会被忽略。小于这个值的洞会被认为是“实心”的能产生遮挡。这对于优化有大量门窗的建筑内部场景非常有用。通常可以设置为0.2-0.5米。Backface Threshold背面阈值。这是一个百分比0-100。在采样时如果从一个采样点看到某个物体的背面相对于其法线的面积比例超过这个阈值该物体将不会被当作遮挡物。这可以防止一些单面薄片如公告板被错误地用作遮挡物。一般保持默认值100即可除非你明确使用了单面物体且希望它参与遮挡。View Cell Size视图单元格大小。这是空间划分的粒度。值越小单元格越多遮挡判断越精确但运行时查找的数据量也越大。通常不需要修改默认值除非你的场景特别巨大或特别微小。烘焙操作参数设置好后点击底部的Bake按钮。这个过程可能耗时几分钟到几小时取决于场景复杂度和参数。烘焙完成后你会在场景文件同级目录下看到一个OcclusionCullingData文件。3.3 效果验证与调试烘焙完不是结束必须验证效果。在Scene视图验证在Scene视图左上方的下拉菜单中将Shading Mode切换到Occlusion Culling。然后移动场景中的摄像机。你会看到视野中被剔除的物体会以红色线框显示或根据你的设置显示。这是最直观的检查方式。检查漏剔除移动摄像机观察是否有些明明被完全挡住的物体仍然没有被标红即还在被渲染。这可能是Smallest Occluder设置过大或者该物体没有正确标记为Static或Occludee。检查过度剔除观察是否有物体在应该被看到的时候却被标红了即被错误剔除。这通常是因为遮挡物如薄墙被错误地判断为不透明或者Smallest Hole设置过小把窗户当成了实心墙。这是严重Bug会导致物体闪烁使用Occlusion窗口的Visualization在Occlusion窗口的Visualization标签页你可以更精细地查看摄像机当前所在单元格的可视集合。这对于调试复杂遮挡关系很有帮助。运行时数据监控在Game视图运行游戏打开Unity Profiler的Rendering区域关注SetPass Calls和Batches在摄像机移动时的变化。当你移动到建筑内部时这两个数值应该显著下降说明遮挡剔除生效了。实操心得烘焙参数没有银弹必须结合你的场景反复测试。一个高效的流程是先使用较保守的参数Smallest Occluder1.0快速烘焙一次验证基本逻辑正确。然后逐步调小Smallest Occluder观察剔除效果提升和烘焙时间/数据量增长的曲线找到一个性价比最高的平衡点。对于有大量细小结构如管道丛林的工业场景可能需要为这些区域单独设置更高的剔除精度。4. LOD技术实战为复杂模型“瘦身”遮挡剔除解决了“画不画”的问题LOD则解决“画多细”的问题。对于高模尤其是外部导入的精密模型LOD是救命稻草。4.1 为导入模型创建LOD组假设我们有一个从SolidWorks导入的“工业泵”模型面数高达5万三角面。模型简化最关键的一步你不能直接用这个高模作为LOD0然后指望Unity自动生成低模。你需要美术人员或使用专业工具如Simplygon、Maya/Blender的减面工具、Unity的第三方插件如Mesh Baker来创建中模LOD1约1万面、低模LOD2约2000面和极低模或面片LOD3一个十字面片或立方体。减面原则保持外形轮廓。减少内部和侧面的细节保留主要特征。对于工业设备要保留其主要的几何特征如泵体、法兰、接口而螺丝纹路、细小铭牌等可以去掉。UV和材质简化后的模型需要重新展UV或复用高模的UV并烘焙法线贴图、AO贴图等。将高模的细节信息以贴图形式转移到低模上这是保证低模视觉质量的关键。一个只有漫反射贴图的低模看起来会非常“平”和假。在Unity中设置LOD Group在场景中选中高模物体IndustrialPump_High。在Inspector中点击Add Component搜索并添加LOD Group组件。你会看到一个LOD百分比滑动条。默认有LOD0100%、LOD1和LOD2。添加LOD层级你可以点击LOD 1旁边的号添加更多层级比如LOD3。分配模型将创建好的不同精度模型拖拽到对应的Renderers列表里。LOD0拖入高模LOD1拖入中模以此类推。调整切换距离/百分比拖动滑动条上的分隔线可以调整每个LOD层级的切换阈值。这个阈值是物体在屏幕上占据的高度百分比默认。例如LOD0到LOD1的线在50%意味着当该物体在屏幕上高度占比大于50%时使用LOD0高模占比在10%到50%之间时使用LOD1中模。4.2 LOD参数调优策略切换阈值的设定这是艺术和科学的结合。不要凭感觉最好的方法是将游戏运行起来控制角色走近再走远观察模型切换的瞬间。切换点应该设置在玩家不易察觉的距离。对于角色、主要交互物体这个距离要设得更保守即更近才切换成低模。对于背景物体可以更激进。使用屏幕相对高度这比绝对距离更科学因为它能自适应不同的分辨率和视场角FOV。一个在4K屏幕上占比10%的物体在1080p屏幕上可能占比20%使用百分比能保证一致的视觉体验。针对移动端调整移动端屏幕小物体更容易达到较高的屏幕占比。可以考虑将LOD0的阈值适当调低比如从60%降到40%让中模更早介入以节省性能。LOD的Fade渐变在LOD Group组件中可以开启Fade Mode。它可以在两个LOD层级切换时进行一个短暂的交叉淡入淡出而不是硬切从而避免“ popping”突跳现象。但这会带来额外的渲染开销同时渲染两个模型在移动端需谨慎评估。对于性能极度敏感的项目通常关闭Fade转而通过精心设置切换阈值和制作平滑过渡的模型来避免 popping。Culled剔除滑动条最右边是Culled区域。当物体屏幕占比低于这个阈值如0.5%时它将完全不被渲染。合理设置这个值对于非常小或极远的物体及时剔除能省下宝贵的渲染资源。但要注意如果这个物体虽然小但很重要比如远处的任务目标点你可能需要用一个始终渲染的Billboard图标来代替它。实操心得LOD模型的制作成本不低。一个实用的策略是分级优化对场景中数量少但面数极高的核心物体主角、BOSS、关键设备制作完整的LOD链高-中-低-面片。对大量重复的中等面数物体如树木、石头制作1-2级LOD高-低。对大量重复的低面数物体小草、碎石则不做LOD依靠合批和遮挡剔除来优化。对于从SolidWorks导入的、作为背景的复杂设备可以制作一个非常简化的“外壳”低模用于中远距离完全舍弃内部结构。5. 移动端专项优化与陷阱规避移动平台的GPU和带宽资源远逊于PC因此优化策略需要更加激进和精细。5.1 移动端遮挡剔除的特殊设置更激进的烘焙参数可以适当增大Smallest Occluder如2.0米以减少遮挡数据的大小和运行时查找开销。移动端CPU性能同样宝贵。谨慎使用Occlusion Area动态遮挡剔除在移动端可能带来不可预测的CPU开销。如果要用确保Occlusion Area的数量尽可能少体积尽可能匹配物体组。关注包体大小烘焙生成的OcclusionCullingData文件会增大包体。在最终发布前检查其大小是否可接受。对于超大型开放世界可能需要按场景区块分割遮挡数据。5.2 移动端LOD的黄金法则面数目标要更低移动端LOD模型的面数需要比PC版再降一档。例如PC版LOD2是2000面移动端可能就需要降到800面。减少LOD层级考虑只使用2-3级LOD高-低或高-中-低去掉极低模的面片层级因为面片在移动端Shader中可能因为Overdraw过度绘制带来额外开销。使用Impostor替身对于非常远的复杂物体如远处的山体、建筑群可以考虑使用Impostor技术——即从几个角度预先渲染该物体的图片然后用一个始终面向摄像机的面片来显示。这比任何3D模型都要省资源。一些Asset Store插件如Impostor Baker可以自动化这个过程。Shader LOD别忘了Unity的Shader本身也有LOD。在Graphics Settings中可以设置全局的Shader LOD级别或者为每个Shader编写LOD代码。在移动端可以强制使用更简单的Shader变体。例如将Maximum LOD Level设置为200或更低以禁用非常复杂的高端Shader。5.3 性能分析与瓶颈定位优化离不开数据。在移动端进行性能分析时使用Unity Profiler真机连接这是最强大的工具。重点关注Rendering区域SetPass Calls和Batches是否过高开启遮挡剔除和LOD后是否显著下降CPU区域Rendering线程和Scripts线程的耗时。遮挡剔除的计算主要在Rendering线程如果这里出现峰值可能是遮挡数据太复杂或动态遮挡计算过多。GPU区域观察Vertex Processing和Fragment Processing耗时。开启LOD后顶点处理耗时应有明显下降。使用Frame Debugger逐帧查看Draw Call的调用顺序和状态。你可以清晰地看到哪些物体被渲染了从而判断遮挡剔除或LOD是否生效。如果发现一个本应被剔除的物体出现在了Draw Call列表中就可以针对性排查。6. 常见问题排查与进阶技巧即使按照流程操作也难免会遇到问题。这里记录一些我踩过的坑和解决方法。6.1 遮挡剔除常见问题速查表问题现象可能原因解决方案物体闪烁时隐时现1. 过度剔除物体被错误判定为不可见。2. 摄像机位于两个View Cell边界剔除判断不稳定。1. 检查该物体和遮挡物的Static标记、Smallest Hole参数。适当调大Smallest Hole或检查遮挡物是否为双面渲染。2. 尝试稍微增大View Cell Size或在摄像机移动逻辑中加入微小延迟/平滑。剔除没有效果1. 物体未标记为Occludee Static。2. 摄像机移动范围未覆盖烘焙区域。3.Smallest Occluder设置过大场景缺乏有效遮挡物。1. 确认物体Inspector右上角的Static复选框已勾选且包含Occlusion Static。2. 在Occlusion窗口的Object标签页检查摄像机的Is View Volume是否勾选烘焙时需要勾选以计算其路径。3. 减小Smallest Occluder或手动为场景添加一些大的遮挡物如雾效体积、不可见碰撞体标记为Occluder。烘焙时间过长或卡死场景过大或Smallest Occluder等参数设置过小。1. 尝试分区块烘焙。将大场景分成多个小场景分别烘焙遮挡数据。2. 先用较大的参数快速烘焙查看效果再逐步细化需要重点优化的区域。烘焙后数据文件巨大Smallest Occluder过小或场景划分过于精细。增大Smallest Occluder和View Cell Size。对于空旷区域可以放置大的、简单的Occlusion Area来替代精细剔除。6.2 LOD相关陷阱LOD切换导致的材质闪烁不同LOD层级的模型如果使用不同的材质球实例切换时可能会因为材质属性或渲染队列不同导致闪烁。确保所有LOD层级使用同一个材质球实例。碰撞体不匹配LOD切换的只是渲染模型碰撞体不会自动切换。如果你的高模和低模形状差异巨大可能导致玩家在远处可以“穿模”。解决方案要么使用一个简化的、统一的碰撞体如胶囊体、盒子适用于所有LOD要么使用脚本根据LOD层级动态启用/禁用不同的碰撞体组件但这会增加复杂度。SkinnedMeshRenderer的LOD对于带骨骼动画的角色SkinnedMeshRendererLOD Group同样适用但模型简化需要更小心要保证骨骼权重在减面后依然正确。通常角色的LOD层级会比静态物体少。性能反优化为一个本身只有几百个面的简单物体添加LOD Group是得不偿失的。LOD系统本身有管理开销。只为那些面数较高建议2000面以上或对性能有关键影响的物体添加LOD。6.3 结合其他优化手段遮挡剔除和LOD不是孤立的它们应该与Unity的其他优化手段协同工作GPU Instancing对于大量相同的静态物体如草地、树木开启GPU Instancing可以极大降低Draw Call。注意被GPU Instancing渲染的物体其每个实例的LOD计算是独立的这很好。但它们通常不参与标准的静态遮挡剔除烘焙。你需要确保它们被放置在合理的Occlusion Area中或者依赖其他大物体来遮挡它们。渲染层Rendering Layers与摄像机裁剪对于极远距离的背景如远山、天空盒直接使用摄像机的Far Clip Plane将其裁剪掉比依赖LOD Culling更高效。代码层面控制对于非常重要的物体如主角你可能需要写脚本更精细地控制其LOD切换逻辑或者完全禁用其被遮挡剔除以确保它永远不会意外消失。最后性能优化是一个迭代和权衡的过程。没有一劳永逸的设置。我的习惯是在项目早期就建立好场景的静态结构并初步烘焙遮挡在模型资源制作规范中就明确LOD级别要求。然后在项目开发的中后期针对性能Profiler中发现的瓶颈场景进行集中的、精细化的遮挡剔除参数调整和LOD模型优化。记住目标是在玩家察觉不到画质损失的前提下榨取出每一帧的宝贵性能。当你看到开启优化后复杂场景的帧率从40fps稳定到60fps那种成就感就是对我们技术人最好的回报。
返回列表