UE5 中 3DGS 场景的帧率调优
第一步:定位瓶颈
调参之前必须先确认帧率下降是不是 3DGS 造成的。场景里的光照、阴影、后处理、常规模型、蓝图逻辑都可能是真正的瓶颈,此时无论怎么调 LCC 参数都不会有改善。
先确认瓶颈来自 3DGS
把场景中除 3DGS 以外的元素隐藏或移除,对比帧率变化:
- 记录当前帧率作为基准(
stat fps或stat unit)。 - 隐藏 LCC Actor,只留场景其余部分,记录帧率。
- 反过来只留 LCC Actor,隐藏或移除其他模型、光源、后处理体积、UI,记录帧率。
原场景里元素较多、不便逐个隐藏时,更干净的做法是新建一个空白关卡,只放一个 LCC Actor 加载同一份数据,在相同视角下记录帧率。这样排除了原场景的全部干扰,得到的就是 3DGS 自身的性能基线。
对比三组数据:
| 结果 | 结论 |
|---|---|
| 只留 LCC 时帧率仍然低 | 瓶颈在 3DGS,继续本篇后续步骤 |
| 隐藏 LCC 后帧率没有明显回升 | 瓶颈在场景其他部分,应先优化那部分 |
| 两者单独都正常,同时存在才低 | 总量超出硬件预算,两边都要压,或降低分辨率 |
先排除引擎默认场景元素
新建关卡时引擎会自带一批默认 Actor,其中有几项开销不小,容易被误当成 3DGS 的成本。最典型的是 VolumetricCloud(体积云):它逐帧做体积光线步进,即使画面里只露出一小片天空也持续消耗 GPU。
项目不需要天空效果时,直接从关卡里删除以下 Actor:
| Actor | 说明 |
|---|---|
| VolumetricCloud | 体积云,GPU 开销显著,纯 3DGS 场景通常用不到 |
| ExponentialHeightFog | 高度雾,与 3DGS 叠加后还会影响观感 |
| SkyAtmosphere | 大气散射,仅在需要天空时保留 |
3DGS 数据本身通常已包含环境信息,这些效果多数情况下不需要。删除后重新测帧率,再判断是否还需要调 LCC 参数。
测试时固定条件,否则数据无法比较:同一地图与出生点、同一摄像机位置或路径、同一分辨率与 Screen Percentage,Lumen、虚拟阴影贴图、后处理、SceneCapture 的开关状态保持一致。先跑一遍预热再记录,不要把首次 Shader 编译和首次磁盘读取混进稳定帧率。每次只改一类设置。
常用的观测命令:
stat fps // 帧率
stat unit // Frame / Game / Draw / GPU 四项帧时间
stat gpu // GPU Pass 细分
stat rhi // 绘制调用、图元、显存
ProfileGPU // 单帧 GPU Pass 明细
确认是 3DGS 后再用 LCC 统计
Actor 面板 Actions > Stats 可打开统计面板,也可在控制台输入 stat xgrids。

使用 stat xgrids 查看 LCC 实时性能统计
参数详细信息请查看性能优化参数说明:统计数据
重点看 Current Render Main Splats 与 Current Render Nodes:多数性能问题的直接原因就是同屏渲染点数过多。
同时打开 LOD 可视化
看统计数字的同时,用 Actor 面板 Actions > Debug Node Bound 打开节点 Level 可视化,直观确认当前视角的 LOD 分布。颜色越暖表示 Level 越低、细节越高。
优化的思路是在观感可接受的前提下尽可能减少同屏点数,重点看 Level 0 铺得有多远:
- 画面中暖色区域延伸到多远?这个距离上是否真的需要最高精度的数据?
- 把切换到更粗层级的距离提前,画面观感有没有可感知的下降?
多数场景里 Level 0 的作用距离都比实际需要的远,让它更早切换到粗层级,点数下降立竿见影,这是所有手段中收益最明显的一项。确认有压缩空间后进入第二步调 Level Factor。
第二步:调整 LOD
这是收益最明显的一步。多数场景里 Level 0 的作用距离都远超实际需要,让它更早切换到粗层级,同屏点数会立刻下降,而观感往往没有可感知的变化。先做这一步,再考虑后续手段。
LOD 有三个独立旋钮(Level Factor、Start Level、End Level)

配置 Level Factor、Start Level 与 End Level
2.1 先确定 Level Factor 的作用方式
两条管线选择 Level 的机制不同,Level Factor 的作用方式也不同,但方向一致:值越大,节点越早切换到低细节层级。
| 管线 | Level 判定依据 | Level Factor 的作用 |
|---|---|---|
| LCC | 全局 RangeForLevel 距离表 | 把距离表整体除以该值 |
| LCC2 | 屏幕空间误差(SSE) | 把算出的 SSE 除以该值,误差变小则不再细化 |
LCC2 管线:屏幕空间误差
LCC2 不使用 RangeForLevel,而是为每个节点计算屏幕空间误差:节点自身的几何误差投影到屏幕上占多少像素。误差超过阈值就细化到更精细的子节点,否则停在当前层级。
这个误差同时受节点距离、视野角度与屏幕分辨率影响,因此同一个 Level Factor 在不同分辨率或 FOV 下的实际切换距离并不相同。这也意味着 LCC2 的 LOD 分布无法用一张距离表预先算出,只能用 2.5 的 Debug Node Bound 实际观察。
调参方式与 LCC 管线一致,直接看 2.2 的测试阶梯。
LCC 管线:距离表
Level Factor 的作用是把全局 RangeForLevel 数组除以该值。值越大,每个 Level 的作用距离越短,节点越早进入低细节层。
RangeForLevel 默认值(11 项,ProjectSettings > Plugins > LCC4Unreal > Level):
Level: 0 1 2 3 4 5 6 7 8 9 10
米: 15 50 80 110 140 170 190 220 250 280 350
除以 Level Factor 后的实际作用距离(单位:米,非整数按四舍五入显示):
| Level Factor | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1.0(默认) | 15 | 50 | 80 | 110 | 140 | 170 | 190 | 220 | 250 | 280 | 350 |
| 1.25 | 12 | 40 | 64 | 88 | 112 | 136 | 152 | 176 | 200 | 224 | 280 |
| 1.5 | 10 | 33 | 53 | 73 | 93 | 113 | 127 | 147 | 167 | 187 | 233 |
| 2.0 | 7.5 | 25 | 40 | 55 | 70 | 85 | 95 | 110 | 125 | 140 | 175 |
怎么用这张表:Level Factor = 2 时,原来 15 米内使用最高细节,现在只有 7.5 米内使用最高细节。这比「降低一半细节」的说法精确得多——它改变的是每个 Level 的作用距离,不是点数比例。
Level 与距离的确切比较方式(取上界还是下界、开区间还是闭区间)属于内部实现,上表用于估算调参幅度,实际 Level 分布请用 2.5 的 Debug Node Bound 确认。这张表只适用于 LCC 管线。
距离超过 RangeForLevel 最后一项时,节点直接使用自身可用的最粗 Level,不存在找不到层级的情况。因此把 Max Distance 提高到超过 350 米不需要同步调整这张表。

在项目设置中配置 Range For Level 距离数组
2.2 Level Factor
取值与默认值见性能优化参数说明:Level Factor
这是 LOD 里首选的旋钮,因为它是渐进的,不会一次性砍掉整个细节层级。
测试阶梯:
1.0 → 1.25 → 1.5 → 2.0
优先检查:轮廓、细线、小物体、地面细节、Level 切换过渡区。这些位置最先暴露细节不足和 LOD 跳变。

调整 Level Factor 后的 LOD 变化
2.3 Start Level(起始等级)
取值与默认值见性能优化参数说明:Start Level
提高它会直接跳过编号最低(最精细)的 Level:
0 → 1 → 2
这比轻微调整 Level Factor 激进得多——它是整层丢弃,不是距离缩放。只在两个条件同时满足时使用:stat xgrids 显示 Level0 Splats 确实是主要成本,且近景画质允许变粗。近景明显发糊时应退回 0。

提高 Start Level 后跳过高细节层级的效果
2.4 End Level(一般不动)
取值与默认值见性能优化参数说明:End Level
降低它会限制可使用的最粗层级:即使数据里有更粗的层级也不会被使用,远处只能停留在 End Level 这一级,渲染点数反而比需要的更多。它不是提速按钮,除非已经理解数据的 Level 结构,保持默认值。
设置时必须满足 Start Level <= End Level,Setter 不会替你校验。

调整 End Level 后限制低细节层级的效果
2.5 用 Debug Node Bound 验证
Actor 面板 Actions > Debug Node Bound 可视化节点边界与 Level。颜色序列为红、橙、黄、绿、蓝、紫,红色为最低 Level(最高细节),白色代表最高 Level(最低细节)。
改完 LOD 参数后用它检查:近景是否仍是红/橙色(高细节),远景是否顺畅过渡到冷色,有没有出现同一距离上 Level 反复跳变的区域。

使用 Debug Node Bound 检查 LOD 分布
LOD 的验证要点
- 节点数与 Splat 数是否下降;
- 近景有没有过早变粗;
- 相机移动时有没有明显的 LOD 跳变;
第三步:最大渲染距离
取值与默认值见性能优化参数说明:Max Distance(m)

设置 Max Distance 最大渲染距离
它和 LOD 的区别
Max Distance 直接砍掉超出距离的数据,减少的是可渲染的空间范围;LOD 是在范围内降低精度。两者互补:LOD 调完仍有压力时,用它进一步收紧范围。
它可能同时减少候选节点和后续的遍历、加载、上传、排序、填充工作,但具体减少多少属于实现与数据相关行为,必须根据实际统计验证,不能假定固定的收益比例。
先勾内联开关,否则改了不生效
Performance 分类下每个参数左侧都有一个勾选框。不勾选时文本框里的数值不起作用,运行时使用插件内置默认值。调整没有效果时先检查这里。
距离的调节步骤
- 站在场景中最远需要看到模型数据的位置,测量实际需要的距离。
- 勾选参数左侧的开关。
- 设为「实际最大可见距离 + 必要余量」。
- 按阶梯逐步收紧,不要一次降到极低:
300 m(默认)→ 225 m → 150 m → 按项目实际距离细调
上面是测试阶梯,不是推荐值。室内、街区、大场景、飞行视角的合理值差异很大,必须按项目实测。

调整 Max Distance 后的渲染范围变化
距离的验证要点
Current Render Nodes、Current Render Splats是否下降;- GPU ms 或 Traversal Time 是否下降;
- 走到距离边界时有没有出现内容突然消失;
- 快速移动时是否更容易看到未加载区域;
第四步:设置点数硬上限
取值与默认值见性能优化参数说明:Max Splat Num
Max Distance 控制的是空间范围,但同一范围内的数据密度可能差异巨大。Max Splat Num 提供一道单帧工作量上限,防止走进高密度区域时点数突然飙升。它与 LOD、距离是互补关系,不能互相替代。
注意过大的值会被自动限制到 GPU 单帧可渲染的量,把面板值拉到上限并不能突破硬件能力,只是让这道上限形同失效。要让它真正起作用,必须设成低于当前视角实际点数的值。

设置 Max Splat Num 单帧点数上限
点数上限的调节步骤
- 勾选参数左侧的开关。
- 从默认值开始按比例下调,单位是万:
3000 → 2250 → 1500 → 1000
- 每次下调后,去高密度区域观察,不要只看空旷区域。

调整 Max Splat Num 后的点数与画面变化
点数上限的验证要点
- GPU ms 与
Current Render Splats是否随上限同步下降; - 有没有出现局部变稀、闪烁、透明结构断裂。
如果降低上限后点数和 GPU ms 都没变化,说明当前视角根本没有达到该上限,这一项不是当前瓶颈——回到第一步重新定位。
第五步:降低像素填充成本
5.1 SplatScale
参数详情见画面调节:SplatScale
减小它会降低单个 Splat 的屏幕覆盖面积,从而减少透明叠加。
做法:从当前画质值小步下降,在高分辨率、近景和物体轮廓处检查。降得过多会让表面出现孔洞或明显变薄。

调整 SplatScale 降低高斯点屏幕覆盖面积
5.2 Small Splat Threshold (px)【仅 LCC2 管线】
可用 CVar 即时测试,不必重启:
r.LCC2.SmallSplatThreshold 0
r.LCC2.SmallSplatThreshold 0.5
r.LCC2.SmallSplatThreshold 1
值越高潜在收益越大,远景颗粒感也越明显。只在 GPU 或 SH 计算确实是瓶颈时才继续提高;数据本身没有 SH时,跳过 SH 带来的收益有限。

设置 Small Splat Threshold 像素阈值
5.3 Quad Extent Threshold【仅 LCC2 管线】
增大该值产生更紧的 Quad,减少 overdraw,但可能裁掉半透明边缘。
r.LCC2.QuadExtentThreshold 0.006 → 0.008 → 0.01
出现轮廓变硬、边缘缺失、Splat 形状异常时回退。

设置 Quad Extent Threshold 控制 Splat Quad 范围
5.4 Frustum Cull Margin【仅 LCC2 管线】
取值与默认值见性能优化参数说明:Frustum Cull Margin
它控制的是裁剪余量,不是点数上限。
只有在屏幕边缘没有弹出的前提下,才尝试向 1.0 小步降低:
r.LCC2.FrustumMargin 1.2 → 1.1 → 1.0
快速转动相机时如果边缘出现内容闪进闪出,说明降过头了。

设置 Frustum Cull Margin 视锥裁剪边距
5.5 抗锯齿
抗锯齿的 GPU 成本不低,TSR 尤其明显。场景中只有 3DGS、没有常规网格与 UI 时,把对应设置改为 None 通常画质损失很小而帧率收益可观。
各管线的默认方法与取舍见性能优化参数说明:抗锯齿方法。

配置不同 LCC 管线的抗锯齿方法
第六步:关闭不需要的功能
这一步收益明确但都有功能代价,逐项确认项目是否真的不需要,而不是一律关掉。
| 功能 | 适用管线 | 何时可关 | 副作用 |
|---|---|---|---|
ReceiveShadows | 仅 LCC | 不需要接收阴影 | 失去接收阴影效果 |
LightMode = Lit | 通用 | 数据自身颜色已满足观感,或不需要场景灯光 | 场景灯光不再影响 LCC |
UseShcoef | 通用 | 只需基础颜色且观感可接受 | 失去视角相关的颜色变化 |
SingleLayerWater Support | 仅 LCC2 | 不使用单层水 | 3DGS 无法正确参与水体遮挡与折射 |
EnableCollision | 通用 | 场景不需要碰撞 | 没有碰撞效果,不能生成导航区域 |
第七步:排序与显存
透明排序和显存管理按管线分开处理:Sort Factor 与额外预加载只在 LCC1 上有,GPU 显存预算只在 LCC2 上有。
7.1 Sort Factor【仅 LCC 管线】
取值与默认值见性能优化参数说明:Sort Factor
它作用于全局 SortFrequencyForLevel 表,控制各 Level 的节点多久重新排序一次。值越大排序次数越少、性能越好,但相机或节点变化后透明顺序更新更慢,可能出现短暂的穿插错误。

设置 Sort Factor 调整 LCCActor 排序频率

配置各 LOD 层级的 Sort Frequency
7.2 额外预加载节点【仅 LCC 管线】
它的作用是缓解快速旋转时屏幕边缘的空洞,代价是增加节点工作量。
这一项有两层状态,容易搞错:
- 不勾选左侧开关时,回退到内部默认值,有效结果仍是开启;
- 要真正关闭,必须先勾选开关,再把值设为关闭。
只有当统计显示节点数和预加载确实是明显负担、且项目能接受快速转向时的边缘空洞,才关闭它。关闭后出现空洞就恢复——不要用增加线程数掩盖数据没准备好的问题。

配置额外预加载节点以缓解快速转向空洞
7.3 LCC2 GPU Memory Budget【仅 LCC2 管线】

设置 LCC2 GPU Memory Budget 显存预算
它只统计单个 LCC2 模型的位置、颜色和球谐数据。提高预算只在以下情况可能有帮助:
- GPU 仍有足够空闲显存;
- 卡顿主要发生在移动到新区域或多视口工作时。
它不会减少 Splat 数、排序量或像素覆盖。没有驱逐或碎片问题时,提高预算只会增加常驻显存而不提高帧率。
调节步骤:
- 先记录整机显存峰值,不只看 LCC 预算。
- 从默认值开始,按 1024 MB 阶梯增加。
- 每次修改后重启引擎。
- 复测最重视角、全部 SceneCapture / nDisplay 视图和长时间运行。
- 给 UE 场景纹理、Render Target、Nanite、Lumen、VSM、排序缓冲和操作系统留出显存。
不要在没有完整显存监控时直接设为 8192 MB。
日志还会打印显存占用分解,可用于核对预算是否合理:
GPU footprint: %u slots (%s), %.2f MB total - gaussian %.2f, SH %.2f, sort %.2f (1 view), other %.2f MB
其中 sort 项即排序缓冲,日志按 1 个视图计算,多视口需按视图数换算。
第八步:加载 线程 内存
8.1 不要先加线程【仅 LCC 管线】
取值与默认值见性能优化参数说明:线程池设置
增加线程可能提高 CPU 并发,也可能增加线程竞争、上下文切换、瞬时磁盘压力和上传峰值。Exporter 线程与运行时渲染无直接关系,调它不会改善帧率。
判定条件:只有当统计显示对应队列积压、同时 CPU 仍有可用核心时,才每次小幅增加 2~4 个线程并复测。CPU 已经满载时应该减少工作量,而不是继续加线程。
如果 Create Thread 这一项持续出现耗时,说明线程池在反复扩容,此时应调整预创建数量而不是上限。
8.2 Splat Number For Discard Per Node【仅 LCC 管线】

设置每个节点的稀疏 Splat 丢弃阈值
它的意图是忽略极稀疏的节点——某些数据里存在大量只含一两个 Splat 的节点,管理这些节点的开销超过了它们的画面贡献。
提高它可能减少低密度节点的管理成本,也可能删掉孤立细节。请在细线、树枝、栏杆、远处小物体处检查,不能只看整体帧率。改完记得重新加载数据,否则看到的还是旧结果。
8.3 内存与显存释放阈值

配置 CPU 与 GPU 内存占用和释放阈值
四项阈值的取值与默认值见性能优化参数说明:Usage 分类
这是内存压力下的释放策略,不是常规提帧旋钮:
- 阈值过低或释放比例过大 → 可能造成「释放—重新加载—再次释放」的抖动;
- 阈值过高 → 可能推迟释放,增加系统内存 / 显存压力;
- 只有通过长时间测试确认存在内存压力或反复驱逐时才调整。
判断依据是 CPU Occupy Percentage / GPU Occupy Percentage 与对应阈值的关系,以及 Released CPU Node Num / Released GPU Node Num 是否持续增长。截图中显存占用 86% 已超过 80% 阈值、显存侧释放数达 1943,就是典型的显存压力信号。
第九步:多视口 碰撞 水体
9.1 多相机与 SceneCapture
每增加一个实际参与渲染的视图,都可能增加遍历、渲染或排序工作;LCC2 还会按视图增加排序缓冲。
逐项检查:
- 是否存在未使用但仍每帧更新的 SceneCapture;
- Render Target 分辨率是否过高;
- 更新频率是否必须每帧;
ShowOnlyActors是否只包含需要的内容;- 小地图能否改用
PointCloud或更低分辨率; - nDisplay、分屏、MRQ 是否同时创建了额外视图。
用 stat xgrids 的 Camera Num(Render) 确认实际参与渲染的视图数是否与预期一致;Camera Num 是参与更新的相机数,两者可能不同。数值高于预期时,说明有视图在你不知情的情况下参与了渲染。
完全不用 SceneCapture 时可关闭 SceneCaptureComponent Support,详见性能优化参数说明。

配置 SceneCaptureComponent Support 多视口支持
9.2 遍历模式

选择 Frustum 或 Circle 默认遍历模式
Circle 适合需要周围方向数据的环幕、nDisplay、360 全景;Frustum 是单视口的默认基线。
普通单相机项目不要为了「防止任何方向缺块」默认切到 Circle——它会把相机周围整个圆形范围都纳入遍历,而视锥模式只处理看得见的那一部分。如果工作流必须用 Circle,应同步收紧 Max Distance、点数上限和多视口分辨率。
LCC2 管线不需要调整这一项,保持 Frustum 即可。
9.3 碰撞与导航
碰撞关闭时,不需要为碰撞距离调参。启用后,碰撞读取、异步物理烘焙和 NavMesh 更新可能增加 CPU、内存和卡顿——注意这类成本不一定体现在 FPS 上,需要单独看碰撞相关的统计项。
碰撞距离的取值与默认值见性能优化参数说明:Max Load Collision Distance(m)
- 纯展示项目直接关闭碰撞。
- 需要碰撞时,勾选参数左侧开关,把
Max Load Collision Distance(m)设为实际交互范围。

设置 Max Load Collision Distance 碰撞加载距离

调整碰撞加载距离后的动态碰撞范围变化
- 不要把渲染距离和碰撞距离机械设成相同值。远景可见不代表远处需要物理碰撞。
- 缩小碰撞距离会直接限制玩家交互与 AI 可用范围,改完必须做功能回归,不能只看帧率。
使用导航时策略相反,碰撞距离要尽可能覆盖玩家可到达的全部区域,让这些区域的碰撞一次性加载完成。碰撞是分块动态加载的,运行中新的碰撞块换入会触发导航数据重新构建,造成明显卡顿。
配套要点:
NavMeshBoundsVolume覆盖玩家活动范围,并确认该范围内的碰撞都已加载- 尽量使用 Static 烘焙,避免 Dynamic 动态烘焙——后者会在碰撞变化时持续重建
9.4 单层水【仅 LCC2 管线】
功能说明与完整配置见单层水支持,本节只讲性能取舍。
SingleLayerWater Support 在项目设置中是全局项,但其实现依赖 LCC2 的中值深度,因此对 LCC 管线的 Actor 不生效。
- 不使用单层水时保持关闭;
- 启用后必须重启才生效;
- 代价是水面像素会损失虚拟阴影贴图的阴影质量,这是功能取舍而非纯性能取舍;
- 只有在 3DGS 被单层水材质错误遮挡时才需要开启。