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

18 KiB
Raw Blame History

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

当前测试样本是:

  1. room.ply

当前已确认的事实:

  1. 3DGS-Unity 并不是运行时直接渲染 .ply,而是先把 .ply 转成更接近 GPU 消费形态的运行时资产。
  2. 它的导入工作流核心在 GaussianSplatAssetCreator.cs
  3. 它的 .ply 读取器 PLYFileReader.cs 本质上是一个偏工程化的快速路径,不是健壮的通用 PLY 解析器。
  4. 它的运行时资产 GaussianSplatAsset.cs 已经把数据拆成了 pos / other / sh / color / chunk 几类 GPU 资源。
  5. 它的运行时渲染 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 已确认包含如下字段:

  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 == 0artifact 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 = RGBA32FRGBA16F
  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

它将承担:

  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.plyEnsureArtifact 能稳定复用

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. 先把 .plyMesh 导入
  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 artifactGaussianSplatLoaderResourceManager 正式接管加载,再由独立的 GPU residency cache 提前完成资源预热,为后续 3DGS 渲染 pass 提供唯一、稳定、无旁路的正式输入。