我原本把 3DGS 端侧重建理解成“拍一圈照片,等模型出来”。真正把采集、重建、加载、渲染和结果验收串起来以后,我才发现它更像一条完整的空间数据流水线:前面的每一次采集抖动,都会在后面的模型细节里留下痕迹;而最终能不能流畅浏览,又取决于渲染端有没有把模型加载、分块和资源管理做好。

一、真正开始做 3DGS,我先把“重建成功”这个目标拆掉了

刚接触 Spatial Recon Kit 时,我给自己定的目标非常简单:做一个“旧工作室数字留存”Demo。用户围着房间拍一圈,应用把场景还原成 3DGS 模型,最后可以在手机里自由转动视角查看。

听起来是一条直线,但第一轮做完,我马上发现“重建成功”其实是一个很模糊的结果。

模型文件生成了,算成功吗?如果桌腿缺了一截、显示器边缘漂浮、墙角出现大片空洞,这种结果很难说可用。反过来,有时采集耗时更长,模型体积也更大,但实际浏览时卡顿明显,用户仍然会觉得功能“不太行”。

所以我把目标重新拆成四个阶段:

  • 采集阶段:画面是否稳定、覆盖是否完整、关键帧是否足够;
  • 重建阶段:任务是否能持续执行,进度与异常是否可见;
  • 渲染阶段:模型能否正确加载,视角移动是否顺畅;
  • 验收阶段:几何完整度、细节、模型体积和浏览体验是否同时过关。

HarmonyOS 当前的 Spatial Recon Kit 围绕 3D Gaussian Splatting 提供重建、渲染与编辑能力。官方新能力介绍也把 3DGS 放在空间化能力里,适合空间建模、商品展示、文旅展陈等场景。对我来说,最有价值的不是“终于能在手机上看 3D”,而是这套能力开始把原来很重的空间建模流程,拆成开发者可以真正接入应用的工程链路。

二、采集页不要急着做得漂亮,先把“能不能用”暴露出来

我第一次写采集页时,做得特别像普通相机:预览画面、拍摄按钮、进度提示,功能能跑。但真正绕着房间走一圈后,问题全出来了。

用户根本不知道自己拍得够不够。

他不知道已经采了多少帧,不知道有多少帧真正进入关键帧集合,也不知道自己是不是移动太快。结果就是明明采了很久,模型出来以后仍然缺角。

后面我把采集页改成了“工作台”思路。相机预览仍然是主体,但下面增加三组实时信息:

  • 已采集帧数;
  • 关键帧数量;
  • 当前存储占用。

当模型构建已经启动时,再明确显示当前阶段和进度。这样用户至少能知道,自己现在是在“采集素材”,还是已经进入“特征匹配 / 模型构建”。

这张图里我专门把 320 个关键帧 和 72% 重建进度 标出来。这里不是为了让界面看起来专业,而是因为这两个值真的会改变用户下一步动作。

如果关键帧数量明显偏少,继续拍比立刻重建更合理;如果已经进入构建阶段,就不该再让用户反复改变采集策略。

我后来还给采集页面增加了三条很朴素的提示:慢一点移动、尽量覆盖不同角度、不要只围着主体拍同一高度。这些提示看起来不“技术”,实际比多加一个炫酷动效有用得多。

三、Native 侧要先建立会话边界,不要把每一帧都当成孤立调用

Spatial Recon 的重建链路在 Native 侧工作。官方能力提供了设备能力检测、创建重建会话、输入数据帧、启动会话、查询进度以及保存结果等接口。

我在实现时没有把这些接口散在页面事件里,而是先把它们收进一个 ReconSession 包装层。原因很简单:重建并不是“调用一次函数”,而是一个有明确生命周期的任务。

这段代码解决的是重建会话从创建到保存结果的边界问题。下面是我按官方接口整理的生命周期示意,具体参数结构和错误码仍应以当前 SDK 为准:

bool ReconSession::start(const std::string& outputPath) {
    if (!HMS_SpatialRecon_IsSupport()) {
        reportError("DEVICE_NOT_SUPPORT");
        return false;
    }

    session_ = HMS_SpatialRecon_CreateSession(&config_);
    if (session_ == nullptr) {
        reportError("CREATE_SESSION_FAILED");
        return false;
    }

    // PushFrame / PushARFrame 在采集阶段持续输入
    HMS_SpatialRecon_StartSession(session_);

    while (!isFinished()) {
        auto progress = HMS_SpatialRecon_GetProgress(session_);
        notifyProgress(progress);
    }

    HMS_SpatialRecon_SaveResultToFile(session_, outputPath.c_str());
    return true;
}

这层封装解决了一个我早期经常犯的错误:页面看见“开始重建”按钮,就直接在 UI 代码里堆 Native 调用。这样一旦页面退出、任务取消或出现设备不支持,清理逻辑很难收口。

我更愿意把页面理解成任务的观察者。页面发起开始、暂停或取消;真正的 Session 层负责状态、进度、错误和资源释放。

会话里我最关心的三个状态

我最后没有设计十几个状态,只保留了三个核心节点:

  • COLLECTING:接收图像或 AR 帧;
  • RECONSTRUCTING:停止继续扩充数据,开始重建;
  • READY:结果文件已保存,可以进入渲染和验收。

异常则单独落到 ERROR,并记录发生阶段。这样日志一眼就能看出来问题是在采集、构建,还是结果保存。

四、DevEco 里真正值得盯的是“进度有没有和模型文件对上”

3DGS 项目调试时,我不会只看画面是否出现。我通常把 DevEco Studio 分成四块看:

左边看 ReconService、ReconState 和预览组件有没有分层;中间看当前模型加载和状态更新代码;右边看完成后的模型卡片;底部只盯重建会话日志。

这一步我最怕一种假成功:页面进度已经 100%,但结果文件还没稳定落盘,UI 就提前把“查看模型”按钮开放了。

所以我的判断条件不是 progress === 100 就结束,而是至少确认:

  1. 重建状态已经完成;
  2. 结果文件路径有效;
  3. 渲染插件已经可用;
  4. 模型节点真正加载成功。

只有这四件事都满足,页面才切到可验收状态。

五、重建完成以后,ArkTS 侧的核心工作才刚开始

模型文件出来以后,我原来以为剩下就是“显示出来”。实际渲染端同样有自己的生命周期。

HarmonyOS 的 spatialRender 模块主要用于 3DGS 数据渲染与场景展示。官方文档中,GSPlugin 提供 3DGS 模型加载能力;使用前需要先加载对应插件,普通模型可以使用 loadGSNode,面向大场景的分块模型则可以使用 loadTiledGSNode。

这段代码解决的是把结果模型真正挂进 ArkGraphics3D 场景:

import { Scene, RenderContext } from '@kit.ArkGraphics3D'
import { spatialRender } from '@kit.SpatialReconKit'

private async loadResultModel(uri: string) {
  const renderContext: RenderContext | null = Scene.getDefaultRenderContext()
  if (!renderContext) {
    throw new Error('RenderContext unavailable')
  }

  renderContext.loadPlugin(spatialRender.GSPlugin.PLUGIN_ID)

  const scene = await Scene.load()
  const node: spatialRender.GSNode = await spatialRender.GSPlugin.loadGSNode(
    scene,
    { uri, offset: 0 },
    scene.root
  )

  this.gsNode = node
  this.renderReady = true
}

这里有一个很容易忽略的顺序:先加载插件,再加载模型节点。官方 API 文档也明确提示,调用相关 API 前需要加载插件 ID,否则行为未定义。

这个顺序我专门写进服务层,不让页面自己记。因为这种“必须先做 A 再做 B”的要求,最适合被封装掉。

六、大模型不是简单“换一个 API”,Tiled 的价值在浏览阶段才体现出来

我的 Demo 最开始用的是单个结果文件。房间规模不大时没有明显问题,后来把场景扩大到办公室一整层,就开始遇到首屏加载时间和内存占用问题。

这时我才真正理解 TiledGSNode 为什么重要。

官方 API 在 API 26 中提供了 TiledGSImportSettings,可以通过 manifest 描述分块模型,并用 loadTiledGSNode 加载。它的工程意义不是“API 名字多了一个 Tiled”,而是大场景不必在第一帧把所有数据一次性压进内存。

这段代码解决的是大场景结果加载:

private async loadTiledScene(manifestUri: string) {
  const renderContext = Scene.getDefaultRenderContext()
  if (!renderContext) {
    return
  }

  renderContext.loadPlugin(spatialRender.GSPlugin.PLUGIN_ID)
  const scene = await Scene.load()

  this.tiledNode = await spatialRender.GSPlugin.loadTiledGSNode(
    scene,
    { uri: manifestUri },
    scene.root
  )

  console.info(`TiledGSNode ready: ${manifestUri}`)
}

我的经验是,小场景不用为了“新能力”强行分块;场景足够大、模型体积上来以后,再考虑 Tiled 加载才有意义。技术选型不能只看接口高级不高级,要看真实负载。

七、验收页我不看“有没有模型”,而看四类证据

做到这里,最容易出现的情况是开发者自己觉得“挺好的”,但测试人员不知道该怎么验收。

我后来专门做了一页模型验收状态,把原本藏在日志里的信息提出来:模型大小、采集帧数、关键帧数量、插件加载、Tiled 节点状态和自由视角浏览状态。

我最终保留四类验收证据。

1. 数据证据

模型文件是否存在、体积是否异常、manifest 能否被读取。

2. 重建证据

采集帧与关键帧数量有没有明显不合理。这里没有一个对所有场景通用的“神奇阈值”,但同类场景可以做版本间对比。

3. 渲染证据

插件加载是否完成、模型节点是否 Ready、相机移动时有没有明显卡顿或整块缺失。

4. 视觉证据

墙面、桌椅边缘、薄物体和反光区域是否存在明显漂浮、重影、孔洞。这个部分不能完全靠日志替代,仍然需要人工浏览。

我把“帧数和模型体积”“Tiled 节点已就绪”用红圈标出来,就是为了让测试人员知道这张页面不是普通结果页,而是验收证据页。

八、这次最值得记下来的几个问题,不在 API 文档里

做完第一版以后,我把问题按开发阶段整理了一遍。

采集太快:模型能生成,但边缘和小物体更容易出现漂移。解决方式不是提高进度条速度,而是直接在采集页给移动速度反馈。

只围主体转一圈:主体很好看,周边空间却缺失。对空间场景来说,“覆盖范围”比“某一个物体拍得特别多”更重要。

进度和结果文件不同步:UI 先显示完成,用户立即进入预览,读取到半成品或尚未可用的文件。状态收口必须以结果可读取为准。

模型加载全部堆到首屏:小 Demo 没问题,大场景会暴露内存与首帧延迟。Tiled 的价值就在这里。

只测一个视角:正面看很好,一转到背面就出现空洞。验收最好设计一条固定浏览路线,保证每版都用近似路径对比。

这些问题让我意识到,3DGS 的“工程化”不是给算法套一个 UI,而是要把采集指导、会话状态、模型加载和验收标准都补齐。

九、我现在会把 3DGS 项目当成一条空间流水线

回头看这次旧工作室 Demo,我最大的变化是:不再把 3DGS 看成一个神奇的“照片转 3D”按钮。

我更愿意把它拆成:

输入质量 → 会话管理 → 端侧重建 → 结果保存 → 3DGS 加载 → Tiled 渲染 → 交互浏览 → 验收。

这条链里任何一段做得太随意,最后都会在用户看到模型的那一刻暴露出来。

HarmonyOS 7 把 3DGS 端侧重建放到空间化能力里,本身已经提供了从重建到渲染的关键基础能力。开发者真正需要补上的,是面向自己业务的采集策略、状态组织、结果管理和验收标准。

如果后面继续做,我会再补两块:一块是对采集质量做更实时的反馈,减少“拍完才知道不好”的情况;另一块是把多个模型结果做版本管理,方便同一场景重复采集后的 A/B 对比。

到那个阶段,这个 Demo 才真正从一次技术尝试,变成可以放进真实产品链路里的空间内容能力。

参考资料

  • HarmonyOS 7 新能力:3DGS 端侧重建:https://developer.huawei.com/consumer/cn/features/
  • Spatial Recon Kit / spatialRender API:https://developer.huawei.com/consumer/en/doc/harmonyos-references/spatial-recon-spatialrender
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐