Files
XCEngine/docs/used/3DGS专用PLY导入器与GaussianSplat资源缓存正式化计划_完成归档_2026-04-10.md

694 lines
18 KiB
Markdown
Raw Blame History

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