18 KiB
3DGS 专用 PLY 导入器与 GaussianSplat 资源缓存正式化计划
日期:2026-04-10
1. 文档定位
这份计划只覆盖 3DGS 落地中的前两层基础设施:
3DGS 专用 PLY importerGaussianSplat 资源 / Artifact / ResourceManager / 缓存层
这份计划明确不讨论以下内容:
- 不实现最终的 3DGS 渲染 pass
- 不实现 editor 里的 3DGS 编辑工具
- 不实现 cutout、selection、导出、相机激活等 Unity 参考项目中的高级功能
- 不提前把 3DGS 强行塞进现有 mesh / volume 路径
这份计划的目标不是“先把 .ply 读出来”,而是把 3DGS 资产链正式纳入引擎现有的资源系统,使它从一开始就是一条可缓存、可复用、可异步、可测试、可长期维护的正式路径。
2. 当前参考与现状
当前参考工程是:
当前测试样本是:
当前已确认的事实:
3DGS-Unity并不是运行时直接渲染.ply,而是先把.ply转成更接近 GPU 消费形态的运行时资产。- 它的导入工作流核心在 GaussianSplatAssetCreator.cs。
- 它的
.ply读取器 PLYFileReader.cs 本质上是一个偏工程化的快速路径,不是健壮的通用 PLY 解析器。 - 它的运行时资产 GaussianSplatAsset.cs 已经把数据拆成了
pos / other / sh / color / chunk几类 GPU 资源。 - 它的运行时渲染 GaussianSplatRenderer.cs 依赖 compute、StructuredBuffer、RawBuffer、procedural draw、fullscreen composite。
对当前引擎的现状判断:
- 引擎已经具备
StructuredBuffer / RawBuffer / RWStructuredBuffer / RWRawBuffer级别的 shader 资源识别能力。 - 引擎已经具备 compute shader 与
Dispatch的 RHI 基础能力。 - 引擎已经具备 runtime material buffer binding 能力。
- 引擎已经具备
AssetDatabase -> Library/Artifacts -> ResourceManager的正式资源链。 - 引擎当前还没有
GaussianSplat这种正式资源类型。 - 引擎当前还没有 3DGS 专用 importer、artifact schema、loader、GPU residency cache。
3. 本轮最核心的架构决策
3.1 不允许运行时直接消费 .ply
正式方案必须是:
Assets/*.ply- 经过
GaussianSplatImporter - 生成
Library/Artifacts/.../main.xcgsplat - 运行时只加载
xcgsplat
不允许的错误方案:
- renderer 首次遇到
.ply时再现场解析 - component 里直接持有
.ply文件句柄 - 把
.ply读取逻辑塞进 render pass - 为了尽快出图先做“临时直接加载
.ply”然后以后再回收
原因很明确:
.ply是 source asset,不是 runtime-ready asset- 直接 runtime 解析会破坏
AssetDatabase / ResourceManager / Library体系 - 3DGS 数据量远大于普通 mesh,更不能把 source 解析和 GPU 上传压到 draw path 上
3.2 不做通用 PLY 导入器,先做 3DGS 专用 PLY 导入器
本轮 importer 的职责不是支持一切 PLY 变体,而是支持当前 3DGS 工作流所需的那类 PLY。
正式边界是:
- 支持
binary_little_endian - 只关心
element vertex - 通过属性名映射识别 3DGS 语义字段
- 对不支持的属性布局给出明确错误
不做的事:
- 不支持带面片索引的通用模型 PLY
- 不支持 ASCII PLY
- 不支持任意 list property
- 不支持“能读但语义不清晰”的模糊推断
这不是退让,而是边界明确。当前目标是 3DGS 正式化,不是通用点云 SDK。
3.3 运行时正式资源类型命名为 GaussianSplat
建议引入:
ResourceType::GaussianSplatResources::GaussianSplatGaussianSplatLoaderGaussianSplatImporter
不建议把运行时资源叫:
GaussianSplatAssetGaussianAssetPLYAsset
原因:
- 引擎当前
ResourceType命名体系都是运行时资源名,不是 editor 资产名 Asset更适合出现在导入流程和文档语义中,不适合塞进正式 runtimeResourceType
3.4 Artifact 采用单文件主 artifact,而不是 Unity 式多 TextAsset 拼装
建议正式主 artifact 为:
main.xcgsplat
不建议照抄 Unity MVS 的多文件拆分为:
*_pos.bytes*_other.bytes*_sh.bytes*_col.bytes*_chk.bytes
Unity 那样做是受 Unity 资产模型约束。我们自己的引擎不需要跟着它的工程妥协走。
本轮更合理的正式方案是:
- 单个
xcgsplat文件包含 header、section table、payload - loader 一次读入 metadata,按 section 定位各 payload
- 后续如果要做分段流式或 memory mapping,再在 schema 上扩展,不先把文件形态做碎
这样做的好处:
- artifact 边界清晰
ArtifactDB记录简单- 依赖跟踪简单
- reimport 稳定
- 不会出现多 sidecar 丢失或部分过期的问题
4. 对参考 MVS 的正式吸收方式
mvs/3DGS-Unity 里真正值得吸收的是流程,不是实现细节原样照搬。
本轮吸收的内容:
- 输入语义
- 数据重排思路
- 运行时数据拆分维度
chunk概念- 颜色纹理化而不是纯 buffer 化
- 未来 compute 排序与 view-data 预处理对资源格式的需求
本轮不直接照搬的内容:
- Unity 的
TextAsset资产组织方式 - 依赖
UnsafeUtility.SizeOf<InputSplatData>() == vertexStride的固定内存布局导入 - editor 窗口与工具链
- HDRP/URP feature 接入方式
- 所有编辑态 GPU buffer
简单说:
- 流程借鉴
- 数据语义借鉴
- 工程架构不照抄 Unity 的壳
5. room.ply 的正式支持目标
当前基线样本 room.ply 已确认包含如下字段:
x y znx ny nzf_dc_0..2f_rest_0..44opacityscale_0..2rot_0..3
本轮 importer 至少必须把这类文件稳定导入。
本轮导入结果必须包含:
- splat 数量
- bounds
- position
- rotation
- scale
- opacity
- color / dc0
- SH 数据
- 供后续 chunk 化、排序、view-data 预计算所需的稳定 layout
本轮不要求:
- 用 room.ply 直接出图
- 完成 chunk 压缩优化后的最终视觉验证
但必须做到:
- room.ply 可以稳定导入成正式 artifact
- runtime 可以正式加载该 artifact
- 资源缓存与异步链路已经为后续渲染阶段准备好
6. 目标资源模型设计
6.1 Resources::GaussianSplat
建议 GaussianSplat 运行时资源至少包含:
splatCountboundsMin / boundsMaxdataFormatVersionpositionFormatotherFormatcolorFormatshFormatchunkCountcameraInfoCount- 各 section 的只读数据视图
这里的 section 建议为:
positionsothercolorshchunkscameras
6.2 other 的语义边界
建议 other 区域承担:
- rotation
- scale
- opacity
- 可选 SH 索引或 chunk 相关辅助字段
原因:
- 这与参考 MVS 的消费模型更接近
- compute 阶段天然会把位置和其它数据拆开消费
- 将来做压缩时,位置和其它数据的量化策略也不同
6.3 color 保持纹理友好布局
建议 color section 不是简单“每 splat 一行 float4”,而是直接按运行时纹理消费友好的布局存储。
原因:
- 参考 MVS 最终就是把颜色上传成纹理
- 对 3DGS 而言,颜色作为纹理读取是合理路径
- 如果导入期就固化好 texel 布局,运行时不必再做一次昂贵重排
6.4 chunks 作为正式字段预留
即使第一阶段先允许 chunkCount == 0,artifact schema 也要正式留出 chunk 区域。
因为:
- chunk 数据不是可有可无的小优化,它影响后续压缩、解码和排序输入
- 后面一旦渲染 pass 接上,就会很自然依赖 chunk
- 现在先把 schema 打对,比后面再迁移 artifact 版本更划算
7. Importer 设计
7.1 引入 GaussianSplatImporter
AssetDatabase 对 .ply 的识别不应直接复用 ModelImporter。
建议规则:
.ply不作为通用模型格式挂进ModelImporter- 本轮把
.ply明确识别为GaussianSplatImporter - 后续如果将来要支持“通用点云 PLY”,再单独扩展,不污染当前 3DGS 主线
7.2 Header 解析不能依赖固定顺序
正式解析流程必须是:
- 读取 header
- 收集
element vertex - 收集每个
property的名字、类型、偏移 - 建立 3DGS 语义字段到 property 的映射
- 校验必需字段是否完整
不允许的方案:
- 直接假定
InputSplatData与文件二进制布局完全一致 - 直接假定
f_rest_*顺序永远固定且不校验 - 因为 room.ply 能过就默认所有训练器导出的 PLY 都一样
7.3 importer 输出的是 cooked runtime layout,不是 source mirror
导入器的正式职责不是把 .ply 原样搬进 artifact,而是做以下转换:
- 按语义解包 source data
- 生成规范化内部 splat 记录
- 计算 bounds
- 可选做 Morton reorder
- 可选做 chunk 构建
- 输出运行时友好的 section layout
这一步就是 source -> cooked artifact,而不是 source -> source copy。
7.4 关于压缩策略
本轮计划分两步:
- 第一阶段先实现无损或近无损基础 cooked 布局
- 第二阶段再把参考 MVS 的压缩格式体系正式移植进来
原因:
- 先把 artifact、loader、cache 链路跑通
- 再叠加压缩和 chunking
- 避免 importer、artifact schema、runtime loader、未来 renderer 四件事同时出错
第一阶段允许:
position = float32other = float32 / uint32 packedcolor = RGBA32F或RGBA16Fsh = float32chunk = 0
第二阶段再引入:
Norm16 / Norm11 / Norm6BC7 / Norm8x4SH clusteringchunk正式压缩路径
8. Artifact 设计
8.1 主文件
主文件建议:
main.xcgsplat
8.2 文件内容建议
建议 xcgsplat 文件包含:
- 文件头
- schema version
- source metadata snapshot
- splat metadata
- section table
- payload blob
section table 至少描述:
- section type
- byte offset
- byte size
- element count
- element stride
- format enum
8.3 version 策略
建议单独引入:
kGaussianSplatArtifactSchemaVersion
不要复用其它 importer 的 schema version。
版本提升触发条件:
- section 布局改变
- chunk 编码改变
- 颜色纹理布局改变
- SH 格式或 camera 区块布局改变
8.4 .meta 设计
即使本轮先不做完整 Inspector,也应该为后续 importer settings 预留正式字段。
建议至少预留:
reorderMortonbuildChunkspositionFormatotherFormatcolorFormatshFormatimportCameras
第一阶段如果先不开放 UI,也要把默认设置结构体和 hash 纳入 artifact key 计算。
9. Loader 与 ResourceManager 接入
9.1 GaussianSplatLoader
需要新增:
GaussianSplatLoader
职责:
- 读取
xcgsplat - 构建
Resources::GaussianSplat - 提供各 section 的稳定只读视图
9.2 ResourceManager 正式接入
正式链路应支持:
Load<GaussianSplat>("Assets/.../room.ply")AssetDatabase::EnsureArtifact(...)ResourceManager实际加载main.xcgsplat
也必须支持:
Load<GaussianSplat>("Library/Artifacts/.../main.xcgsplat")
9.3 不能把 GPU 上传塞进 loader
GaussianSplatLoader 只负责 CPU 运行时资源,不负责 GPU residency。
原因:
- loader 属于资源层
- GPU residency 属于渲染缓存层
- 如果在 loader 里直接创 GPU 资源,会重复 volume 这条链已经暴露过的架构问题
10. 缓存与预热设计
10.1 资源缓存层必须提前设计 GPU residency 状态机
即使本轮还不接最终 render pass,也必须把状态机设计写进正式方案:
UninitializedCpuReadyGpuUploadingGpuReadyFailed
后续 BuiltinGaussianSplatPass 只能消费:
GpuReady
不允许 draw path 现场把 CpuReady -> GpuReady 做完。
10.2 建议新增 CachedGaussianSplat
建议未来挂在 RenderResourceCache 或其正式拆分后的 GPU 资源缓存模块中。
它至少应持有:
posBufferotherBuffershBuffercolorTexturechunkBuffersortKeyBuffersortDistanceBufferviewDataBuffer- runtime-ready flag / state
本轮即使还不把所有 GPU 辅助 buffer 全建出来,也要把正式边界写清楚:
- asset static payload buffer/texture
- per-frame transient / reusable working buffer
10.3 首次可见前预热,而不是首次 draw 同步补做
这点必须作为硬约束写死:
GaussianSplat被场景反序列化后,CPU artifact 加载完成就进入 GPU 预热队列- GPU 上传在后台或明确的准备阶段完成
- 首次 draw 只允许跳过未 ready 对象,不允许同步创建大资源
原因:
- 3DGS 资产通常很大
- room.ply 这种样本数据量已经足够把 draw path 压垮
- 当前 volume 这条链已经证明“首次绘制再上传”不是可接受正式方案
10.4 warm cache 验收标准
本轮资源 / 缓存层至少要达到:
- 第二次加载 room.ply 时不重新解析 source
.ply - 直接命中
Library/Artifacts ResourceManager不会因为 cache hit 又走 source importer- 后续 GPU 预热可以稳定复用 artifact 输出
11. 测试计划
11.1 基线样本
统一使用:
它将承担:
- importer 基线
- artifact 基线
- cache hit 基线
- future renderer 接入基线
11.2 Unit Tests
本轮至少要补齐以下单测:
PLY header parser正确识别vertexCount / properties / offsetsGaussianSplatImporter能正确识别 room.ply 的必需字段- 缺字段时给出明确错误
- 非法格式时给出明确错误
xcgsplat写入 / 读取 roundtrip 正确GaussianSplatLoader能读取 artifact 并恢复 metadata 与 section viewResourceManager能从Assets/.../room.ply正式加载GaussianSplatAssetDatabase对.ply的EnsureArtifact能稳定复用
11.3 Integration Tests
本轮先做资源链集成测试,不做最终出图测试。
至少要有:
room.ply -> EnsureArtifact -> Load<GaussianSplat>全链通过- 二次加载命中 artifact,不触发 reimport
- 修改 source writeTime 后能触发 reimport
- 清库后能重建 artifact
11.4 为后续渲染阶段准备的 smoke test
虽然本轮不做 3DGS pass,但建议提前补一个 GPU 资源 smoke test:
- 读取
GaussianSplat - 构建最小 GPU cache entry
- 创建
pos/other/sh/chunkbuffer 与colortexture - 验证状态进入
GpuReady
这样后续 renderer 接入时,不会把“资源问题”和“渲染问题”混成一团。
12. 分阶段执行计划
Phase 1:资源类型与 artifact schema 落地
目标:
- 正式引入
ResourceType::GaussianSplat - 正式引入
Resources::GaussianSplat - 正式定义
xcgsplatartifact schema
任务:
- 扩展
ResourceType - 新增
GaussianSplat运行时资源类 - 设计 artifact header 与 section table
- 新增
WriteGaussianSplatArtifactFile / LoadGaussianSplatArtifact
验收标准:
xcgsplat可写可读- 资源元数据可稳定 roundtrip
Phase 2:3DGS 专用 PLY importer 正式化
目标:
- 把
.ply纳入GaussianSplatImporter
任务:
- 新增 header parser
- 新增 3DGS property mapping
- 读取 room.ply 并转换成规范化内部 splat 数据
- 输出基础 cooked artifact
验收标准:
- room.ply 可稳定导入
- 不依赖固定 struct stride == 文件 stride
- 错误路径有清晰日志
Phase 3:AssetDatabase / Library / ResourceManager 接入
目标:
- 把
GaussianSplat完整接进项目资源工作流
任务:
.ply -> GaussianSplatImporterEnsureArtifact(..., ResourceType::GaussianSplat)GaussianSplatLoaderLoad<GaussianSplat>()
验收标准:
- 可以通过
Assets/.../room.ply正式加载 - cache hit 时不重走 source parse
Phase 4:资源缓存与 GPU residency 预热骨架
目标:
- 正式建立 3DGS GPU 资源缓存的边界
任务:
- 设计
CachedGaussianSplat - 建立 GPU residency 状态机
- 实现最小 GPU 资源构建 smoke path
- 明确禁止 draw path 首次同步上传
验收标准:
- room.ply 对应的
GaussianSplat可以被 GPU cache 预热成 ready 状态 - 资源层与渲染层边界清晰
Phase 5:测试补齐与收口
目标:
- 让这条链路可回归、可持续演进
任务:
- 补全 importer / loader / cache hit / reimport 单测与集成测试
- 输出阶段性说明
- 为后续 renderer 接入保留唯一正式资源路径
验收标准:
- room.ply 全链路测试稳定
- 不存在“临时直接读 ply”的旁路
13. 明确不允许出现的临时方案
以下方案本轮禁止出现:
- 为了尽快出图,先在 render pass 里直接解析
.ply - 先做一个
BinaryResource包.ply内容,后面再说 - 先把
.ply当Mesh导入 - 把 3DGS 的 GPU buffer 直接挂在
Material资源本体上作为持久化资产 - 首次 draw 时同步创建
pos / other / sh / colorGPU 资源 - 把
room.ply单独写死成特判
这些做法都会把本该正式化的主线重新拉回临时方案。
14. 本轮完成标志
当以下条件同时成立时,这份计划才算完成:
.ply已正式被GaussianSplatImporter接管GaussianSplat已成为正式ResourceTyperoom.ply能稳定导入成xcgsplatResourceManager能正式加载GaussianSplat- 二次加载能稳定命中 artifact
- GPU residency cache 骨架已经建立,不允许首次 draw 同步补做
- 资源层与缓存层测试已经覆盖 room.ply 主路径
15. 一句话结论
这条主线的第一步不是“做一个 ply 读取器”,而是把 3DGS 正式升级为引擎里的 GaussianSplat 资源体系:
由 GaussianSplatImporter 把 .ply 转成 xcgsplat cooked artifact,由 GaussianSplatLoader 与 ResourceManager 正式接管加载,再由独立的 GPU residency cache 提前完成资源预热,为后续 3DGS 渲染 pass 提供唯一、稳定、无旁路的正式输入。