# 端侧重建一座园子 # 智绘鸿蒙·如 7 而至#

## 前言

立项时想做的事很清楚：带一团人逛苏州园林，走完把手机递过去——"刚才那座亭子，你手机上能看到三维的"。全部在端侧完成，不上传云端。

鸿蒙 7 这头对应的是 Spatial Recon Kit（空间建模服务）。名字里"重建"两个字让人乐观：既然叫空间建模服务，那重建应该是它的主业。

两周后我们把这件事停在了原型阶段。这篇复盘记三件事：**我们以为的可行性从哪来、真实探测跑出了什么、以及为什么"探测全绿"是最危险的一版结论。**

## 第一层：能力盘点，比想象中窄

先把 SDK 里的实际情况列出来，这是最容易查也最容易被跳过的一步。

| 位置 | 内容 |
| --- | --- |
| `@kit.SpatialReconKit` | 只导出 `spatialRender` 和 `spatialEdit` 两个命名空间 |
| `@hms.graphics.spatialRender.d.ts` | `GSPlugin`、`GSNode`、`loadGSNode`、`TiledGSImportSettings`（26.0.0 新增） |
| 3DGS **重建**相关接口 | 只在 `API参考/Spatial_Recon_Kit/​C_API` 下：`HMS_SpatialRecon_Session`、`spatial_recon_interface.h`，库 `libspatial_recon_ndk.z.so`，起始版本 6.1.0(23) |

结论一：**重建能力没有 ArkTS 接口，只有 C API。** 指南里那句"多视角与 AR 帧输入"意味着要自己写 NDK 层、自己喂 AR 帧。这不是"调用一个接口"的量级，是一个独立的图形/SLAM 集成项目。

而 ArkTS 侧能拿到的，只有渲染：把已经做好的 3DGS 模型画出来。也就是说，"云游一座园子"里我们能做的只有后半段，前半段要另找路径（离线重建、或者干脆用别人的模型）。

这一步如果一开始就做扎实，项目方向会完全不同。我们是在写完第一版 UI 之后才发现的。

## 第二层：探测跑下来，全绿

渲染侧我们做了一次完整探测，代码就是文档里那几行：

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

const ctx: RenderContext | null = Scene.getDefaultRenderContext();
ctx?.loadPlugin(spatialRender.GSPlugin.PLUGIN_ID);
const scene: Scene = await Scene.load();
const node: spatialRender.GSNode =
  await spatialRender.GSPlugin.loadGSNode(scene, { uri, offset: 0 }, scene.root);
```

MatePad Pro（HarmonyOS 7 / API 26）上的输出：

```
canIUse SystemCapability.Graphics.SpatialRender  -> true
canIUse SystemCapability.Graphics.SpatialEdit    -> true
canIUse SystemCapability.ArkUi.Graphics3D        -> true
GSPlugin.PLUGIN_ID   -> 1450021d-c57f-d9ff-7770-c24fb3f3321c
RETRO_EFFECT_ID      -> a30c8a84-fbdc-4dbc-9511-5918f2ccbe49
getDefaultRenderContext -> ok
loadPlugin(GSPlugin)    -> ok
Scene.load()            -> ok root=container
loadGSNode              -> ok name=gsNode
```

三项系统能力都在，插件加载成功，场景创建成功，模型节点加载成功。到这里，任何人都会汇报"渲染打通了"。

## 第三层：全绿是假的

我们当时没有模型文件——手上一个 `.ply` / `.glb` 都没有。所以上面那次 `loadGSNode` 传的 uri 是随手写的一个路径：

```
OhosRawFile://assets/garden.ply      ← 这个文件不存在
```

它返回了 `ok`。

于是加了两个对照：

```
loadGSNode(不存在的 .xyz)  -> ok name=gsNode
loadGSNode(空 uri)         -> 失败，但 code=undefined message=undefined
```

三条事实：

1. **不存在的模型文件不会报错**，接口照样返回一个名叫 `gsNode` 的节点。它是不是空的、有没有几何，从返回值上完全看不出来。
2. 空字符串会抛异常，但抛出的对象**没有 `code` 和 `message`**，不是文档里那套 `BusinessError` 形态。所以按错误码写的 catch 分支，在这里等于没写。
3. `Scene.root` 的类型是可空（`null`），不是 `undefined`。用 `!== undefined` 判空能编译通过、也永远为真——这是个静默的逻辑错误。

结论二：**探测全绿只能证明"接口存在且可调用"，不能证明"渲染会发生在屏幕上"。** 真正的验收标准只有一个：看到像素。要么截屏里出现模型，要么用 `XComponent` 绑上场景后确认有帧。我们停在了前者都没做到的位置。

## 一次合格的自检应该长什么样

把这次的教训翻译成可执行的四条，下次直接照做：

1. **准备一个正对照。** 一个已知能渲染出来的最小模型（哪怕是一个几 KB 的 `.glb` 立方体）。没有正对照，所有"成功"都无法区分"真的通了"和"接口宽容"。
2. **准备一个负对照。** 故意传空 uri、传错扩展名、传不存在的文件，看接口分别怎么反应。这次的三个关键事实全部来自负对照，成本不到二十分钟。
3. **验收看输出侧。** 绑定 `XComponent` 之后截屏比对，或者数帧。返回值只配当线索，不配当结论。
4. **异常要按最坏情况写。** 抛出来的对象可能没有 `code`、没有 `message`，甚至不是 `BusinessError`。日志里至少要把 `JSON.stringify(e)` 和 `typeof e` 一起打出来，否则线上只会看到一行 undefined。

## 为什么当时会判断错

复盘下来有两个原因，都值得记住。

**第一个是把"能力存在"当成"链路可用"。** `canIUse` 返回 true、`loadPlugin` 不抛异常，这些是系统能力清单层面的事实，和"我这台设备上能不能跑起来"之间还隔着模型文件、渲染循环、视图绑定三层。

**第二个是接口太宽容。** 一个对不存在的文件返回成功的 API，会让所有基于返回值的自检失效。这类接口只能配合"输出侧验证"用——截图、比对像素、或者拿一个已知能渲染的模型做正对照。我们这轮缺的就是正对照：手里没有一个确定能渲染的 `.glb`，所以连"渲染管线到底通不通"都无法判定。

## 如果继续做，路径是什么

按优先级排：

1. **先拿一个已知能渲染的模型**，把 `XComponent` + `Scene` 的显示链路打通，用截图确认像素出现。这一步不做，后面全是纸面推演。
2. **重建侧走离线**。端侧 C API 重建的工程量和风险都很高，先用桌面端 3DGS 工具产模型、端侧只负责渲染，验证体验是否成立。
3. **分包与流式**。`TiledGSImportSettings` 是 26.0.0 新加的瓦片化加载，说明官方也在解决大场景内存问题。真实园林场景一定会走到这条路，值得提前验证。
4. 最后才考虑端侧采集重建（多视角 + AR 帧输入）。

## 真机数据

| 项目 | 实测值 |
| --- | --- |
| 设备 | MatePad Pro（HarmonyOS 7 / API 26，序列号略） |
| 三项 syscap | 全部 `true` |
| `loadPlugin` / `Scene.load` | 均成功（未单独计时，整轮探测在 1 秒内完成） |
| `loadGSNode`（文件不存在） | 返回 ok，节点名 `gsNode` |
| `loadGSNode`（空 uri） | 抛异常，`code` / `message` 均为 undefined |
| ArkTS 侧重建接口 | 无（仅 C API，起始 6.1.0(23)） |

![能力探测结果，前三项 syscap 全为 true](assets/01-probe.png)

![loadGSNode 对不存在的文件同样返回 ok](assets/02-node.png)

## 总结

Spatial Recon Kit 的 ArkTS 侧现在只适合做一件事：**渲染别人产出的 3DGS 模型**。适合"离线重建 + 端侧展示"的场景，比如文旅、房产、展陈里预先做好的点位模型。

不适合"拍一段视频当场生成三维"。那条路目前要下到 C API 和 NDK，而且要先解决 AR 帧输入与算力预算。

这次最大的收获不是接口知识，是一条方法论：**接口不报错不等于事情成功了。凡是返回值不能自证的链路，验收就得从输出侧做。**

> 💡 **一句话**：`loadGSNode` 会为一个不存在的文件返回成功——所以判断 3DGS 通没通，别看日志里的 ok，去看屏幕上有没有像素。

## 参考文献

1. 加载 3DGS 模型（开发指南）：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/spatial-recon-load
2. spatialRender（ArkTS API 参考）：https://developer.huawei.com/consumer/cn/doc/harmonyos-references/spatial-recon-spatialrender
3. SpatialRecon（C API 参考）：https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-spatialrecon
4. Spatial Recon Kit 术语：https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/spatial-recon-glossary
