3DGS 渲染机制与显存管理
说明数据以何种方式进入显存参与渲染,以及显存的分配规则与容量上限。画面参数的调节参考画面调节,加载操作参考快速入门。
术语约定:数据被切分后的最小加载与渲染单位称为节点(Node);每个节点的精度由 Level 标识,Level 0 精度最高,Level 越大精度越低。该定义与性能优化参数说明一致。
分块渲染与全量渲染
数据加载后有两种渲染方式:按需加载可见节点(分块渲染),或一次性加载整份数据(全量渲染)。默认行为按管线区分:
| 管线 | 格式 | 默认模式 |
|---|---|---|
| LCC2 | .lcc2 / .ply / .spz / .sog | 分块渲染 |
| LCC | .lcc | 全量渲染;数据量超过阈值时回退为分块渲染 |
分块渲染
数据被切分为空间节点,每个节点包含多个 Level。每帧的处理流程如下:
遍历(按相机位置与朝向筛选可见节点,为每个节点选定 Level)
│
▼
加载(将选中节点的数据读入内存)
│
▼
上传(将数据提交至显存)
│
▼
渲染
近处节点使用低 Level,远处节点使用高 Level,不可见节点不加载。离开视野的节点不会立即释放,而是保留在缓存中以便再次进入视野时直接复用;仅在显存不足时,才依据最后渲染时间等条件淘汰最久未使用的节点。显存占用主要取决于当前可见范围,因此可支撑大规模场景。
两条管线的绘制提交方式不同:
| 管线 | 绘制方式 |
|---|---|
| LCC | 每个节点单独提交一次绘制,节点数量增加时绘制调用同步增加 |
| LCC2 | 所有可见节点的数据汇总至统一 Buffer,仅提交一次绘制 |
分块渲染的固有代价:
- 相邻节点可能被分配到不同 Level,交界处的疏密与清晰度存在差异,可观察到节点边界
- 相机移动过程中 Level 持续切换,可能出现细节跳变
全量渲染
跳过按相机筛选节点的过程,将整份数据一次性加载并常驻显存,相机移动时不再重新遍历与选定 Level。
优势:
- 全场景使用统一 Level,不存在节点间的 Level 差异,无可见的节点边界
- 数据常驻显存,相机移动时无加载、释放与细节跳变
- 省去每帧的遍历与上传开销,帧率更稳定
代价是数据全部常驻显存,占用随场景规模线性增长,因此仅适用于小规模场景。
全量渲染的判定条件
是否启用全量渲染在加载时确定,取决于 Use Full Load 开关与 Level 0 的 splat 数量:
Load(数据文件)
│
▼
读取 Level 0 splat 数量
│
▼
Use Full Load 开启? ── 否 ──▶ 分块渲染
│ 是
▼
splat 数 ≤ Full Load Splat Number? ── 否 ──▶ 分块渲染
│ 是
▼
全量渲染
两个属性均位于 Actor 的 Details 面板 Performance 分类下:
| 属性 | 说明 |
|---|---|
| Use Full Load | 是否允许使用全量渲染 |
| Full Load Splat Number | 允许全量渲染的 Level 0 splat 数上限(单位:万,默认 1500) |
默认值按 Actor 类型区分:
| Actor | Use Full Load 默认值 | 依据 |
|---|---|---|
| ALCCActor | 开启 | 节点间 Level 差异明显、节点边界可见,且每个节点占用一次绘制调用,小场景采用全量渲染效果更优 |
| ALCC2Actor / APlyActor / ASpzActor / ASogActor | 关闭 | Level 切换按屏幕误差连续过渡、节点边界不明显,且仅需一次绘制调用,默认按需加载更节省显存 |
LCC2 手动开启全量渲染的场景
LCC2 管线默认关闭全量渲染,以下场景可手动开启:
- 场景规模有限、显存充足,需要画质与帧率同时稳定
- 相机快速移动或频繁传送,节点加载无法跟上,出现空洞或细节跳变
- 产品级展示、录屏、序列渲染等不接受加载过程中画面变化的场合
- 节点交界处的疏密差异仍然可见
开启后若显存吃紧或帧率下降,表明场景已超出全量渲染的适用范围,应改回分块渲染。
注:全量渲染仍受最大渲染距离限制,相机与数据的距离超出该值时不显示。
显存预算与容量上限
LCC2 管线将常驻的 splat 数据汇总至统一 Buffer 渲染,因此单个模型可加载的数据量取决于这些 Buffer 的容量上限。
Buffer 结构与单 splat 占用
数据分两份 Buffer 存放:
| Buffer | 每个 splat 占用 | 内容 |
|---|---|---|
| 高斯基础数据 Buffer | 40 字节 | 位置、旋转、颜色、缩放 |
| 球谐 Buffer | 48 字节(最高 3 阶) | 球谐系数,仅带球谐的数据才分配 |
单个 Buffer 的硬上限
Unreal 的 Buffer 大小以 32 位整数记录,理论上限 4 GB。插件实际取 4095 MB(比 4 GB 少 1 MB),保留的余量用于保证上传时的字节计算不越界。该上限对每份 Buffer 单独生效,无法通过设置突破。
显存预算设置
单个模型可用的显存由 ProjectSettings > Plugins > LCC4Unreal 中的 LCC2 GPU Memory Budget (MB) 控制:
| 项目 | 值 |
|---|---|
| 默认值 | 2048 MB |
| 可调范围 | 2048 ~ 8192 MB |
| 生效方式 | 修改后需重启编辑器 |
该预算为两份 Buffer 的总额,而非单份上限。因此带球谐的数据按每 splat 88 字节(40 + 48)计入预算,不带球谐的按 40 字节计入。
排序 Buffer 的额外开销
3DGS 渲染前需按 splat 到相机的距离排序,排序结果与视角相关,因此每个视角需单独一组排序 Buffer。该部分不计入上述预算,每个视角的每个常驻 splat 额外占用 16 字节(排序键与索引各两份,用于排序过程中的交替读写)。
以 2048 MB 预算、带球谐的数据为例,可常驻约 2440 万 splat,单视角的排序 Buffer 约 372 MB。
立体渲染的左右眼、分屏的每个玩家、SceneCapture 各计为一个视角。视角数增加时排序 Buffer 随之增长,且视角减少后不会回落,因此规划显存时需按出现过的最大视角数预留。
单文件格式的加载上限
单文件格式指 .ply / .spz / .sog,即整份数据保存在一个文件中、且不包含节点划分与 Level 信息的格式。这类数据无法拆分加载,插件只能将其作为单一节点一次性解码并全部常驻显存,因此存在明确的 splat 数上限。.lcc 与 .lcc2 在导出时已完成节点划分,不属于单文件格式。
上限的计算方式
加载前插件会计算上限并对超限数据进行拦截:
| 预算 | 不带球谐 | 带球谐 |
|---|---|---|
| 2048 MB(默认) | 约 5300 万 | 约 2440 万 |
| 4096 MB | 约 1.07 亿 | 约 4470 万 |
| 8192 MB | 约 1.07 亿 | 约 4470 万 |
上限取以下三个条件中的最小值,显存预算仅为其中之一:
| 条件 | 说明 |
|---|---|
| 显存预算 | 预算除以每 splat 字节数(不带球谐 40 B,带球谐 88 B) |
| 单 Buffer 上限 | 4095 MB 除以最大 stride(不带球谐 40 B,带球谐 48 B) |
| 球谐数组容量 | 仅带球谐时生效,解码结果使用 32 位索引,上限约 4470 万 |
因此预算超过 4096 MB 后数值不再增长:不带球谐时受限于单 Buffer 上限,带球谐时受限于球谐数组容量。此外,显卡实际可用显存会进一步压低可加载量。
注:已知问题。预算设置项允许填至 8192 MB,但单文件格式的可加载量在 4096 MB 时已达上限,继续调高不产生效果且不会给出提示。预算已为 4096 MB 仍超限时,应转换为
.lcc2或改用不带球谐的数据。该限制将在后续版本改善。
超出上限时的处理
单文件数据超限时加载会被拒绝,Output Log 中会输出实际 splat 数、当前上限,以及预算调至最大时可支持的 splat 数。处理办法按推荐顺序:
- 转换为
.lcc2,由插件按需加载而非一次性常驻。 - 将 LCC2 GPU Memory Budget 调至 4096 MB,重启后重试。仅在当前瓶颈为预算时有效,调至 8192 MB 对单文件格式无效。
- 改用不带球谐的数据,每 splat 计入预算的字节数由 88 降至 40。
改用 .sog 或 .spz 无助于突破上限:这类格式的压缩仅作用于磁盘文件,解码后每个 splat 在显存中的占用完全相同。
LCC2 格式不受此上限约束
.lcc2 将数据切分为多个节点,每个节点单独解码与上传,任何单个节点均远低于上述上限,因此数据总量没有硬性上限,可显著超出显存预算。受约束的是运行期的两个量:
| 约束对象 | 限制来源 | 超出时的行为 |
|---|---|---|
| 同时常驻的 splat 数 | 显存预算与单 Buffer 上限 | 按最久未使用淘汰,换入换出 |
| 单帧渲染的 splat 数 | 约 8900 万(4095 MB ÷ 48 B) | 按距离丢弃远处节点 |
单帧渲染量同时受 Actor 的 Max Splat Num 限制,两者取较小值。
显存管理与调优
显存不足时的自动释放
运行期插件持续监控显卡的实际显存占用。占用超过 ProjectSettings 中 Max GPU Usage Percentage For Release 设定的比例时,会按最后渲染时间、访问频率、数据大小与 Level 综合排序,优先释放最久未使用的节点。高 Level 节点受到保护,以避免快速转动视角时出现大面积空洞。
相关设置:
| 设置 | 说明 |
|---|---|
| Max GPU Usage Percentage For Release | 触发自动释放的显存占用比例(50 ~ 100%) |
| GPU Release Percentage | 每次触发时释放的数据比例(10 ~ 100%) |
| LCC2 GPU Memory Budget (MB) | 单个模型的 splat 数据显存预算 |
显存预算不足的判断与调整
预算偏小时数据不会加载失败,而是表现为反复换入换出。常见现象:
- 相机移动或转动时画面出现空洞,停止后逐渐补全
- 视角来回摆动时,同一区域反复变清晰再变模糊
- 局部区域始终偏模糊,靠近后仍不变清晰
- 帧率随视野内容量波动,视野开阔时明显下降
- 场景静止时表现正常,移动时出现卡顿
其中局部偏模糊容易被误判为数据本身质量不足。低 Level 数据来不及加载时,插件会先以显存中已有的高 Level 数据填充,保证画面不空缺,待低 Level 数据就绪后替换。显存不足导致低 Level 数据反复被淘汰时,该区域会持续停留在高 Level。判断依据是靠近后是否变清晰:正常情况下停留一至两秒即可补全,始终不变则说明低 Level 数据无法进入显存。
确认方法:查看 Output Log 中加载时输出的模型预计占用(predicted cost),与当前预算对比。若单个模型的预计占用接近或超过预算,说明常驻空间不足。也可通过 stat LCC 观察释放次数,数值持续增长表明正在频繁淘汰。
调高预算的前提是显卡有余量。预算仅为插件申请的上限,超出显卡实际可用显存后,驱动会将数据交换至系统内存,帧率下降比换入换出更严重。调整时按以下顺序判断:
- 确认显卡可用显存。预算加上排序 Buffer 的开销后,应为引擎自身与其他资源保留余量。
- 逐级调高预算(2048 → 4096 → 8192),每次重启后观察空洞与帧率是否改善。
- 改善不明显时说明瓶颈不在预算,转为降低数据量:调低 Max Splat Num、启用 Max Distance 限制,或改用不带球谐的数据。
注:预算按每个模型独立计算。场景中放置多个 LCC Actor 时,显存占用随模型数量累加,需相应下调单个模型的预算。