尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

HarmonyOS 7 / API 26 3DGS 端侧重建接入边界:Spatial Recon Kit 与 ArkGraphics 3D 分工实战

HarmonyOS 7 / API 26 3DGS 端侧重建接入边界:Spatial Recon Kit 与 ArkGraphics 3D 分工实战 HarmonyOS 7 / API 26 3DGS 端侧重建接入边界Spatial Recon Kit 与 ArkGraphics 3D 分工实战先把问题摆出来HarmonyOS 7 / API 26 把 3DGS 端侧重建放到了新能力列表里这个方向很有吸引力空间建模、商品展示、文旅展陈、室内空间预览都可以把二维素材变成更接近真实空间的 3D 内容。但真到开发侧最容易出错的地方不是“会不会写一个 3D 页面”而是把几个能力混成一个东西以为 Spatial Recon Kit 负责所有 3D 渲染以为 ArkGraphics 3D 可以直接完成重建以为 3DGS 模型加载成功就等于完整接入成功以为模拟器能跑通真机就一定没问题以为只要页面能显示模型性能、降级、失败恢复就可以后面再补。我的处理方式是把链路分成五段能力检测、重建会话、模型产物、3D 场景、失败兜底。每段只负责自己的事后面才好排查。这几个官方能力分别管什么这篇按 HarmonyOS 7 / API 26 的能力范围来理解核心参考的是 HarmonyOS 7 新能力、Spatial Recon Kit、ArkGraphics 3D 和 AR Engine 的官方说明。能力更适合负责什么不适合负责什么Spatial Recon Kit3DGS 相关的重建、模型加载、模型处理、滤镜和空间重建会话页面布局、普通 3D 场景组织、业务状态管理ArkGraphics 3D3D 场景、节点、相机、灯光、材质、动画、后处理、模型展示从视频或空间数据生成 3DGS 产物AR Engine需要把虚拟物体放到真实世界坐标里时处理现实空间对齐普通模型预览页、离线模型列表展示ArkUI 页面展示状态、按钮、错误提示、进度、结果入口重建算法本身、渲染引擎内部细节所以一个比较稳的接入方式是Spatial Recon Kit 产出或加载模型ArkGraphics 3D 负责把模型放进场景里展示ArkUI 页面只观察状态。需要和现实世界对齐时再考虑 AR Engine。不要让一个页面组件同时承担采集、重建、加载、渲染和错误恢复。我会先做能力检测而不是先写页面3DGS 这种能力和普通 UI 组件不一样它受设备、区域、系统版本、GPU、模型体积和运行环境影响。官方文档也明确提到了一些约束比如 Spatial Recon Kit 的地区、设备和模拟器支持范围ArkGraphics 3D 也依赖图形能力。开发时我会先把能力检测做成一个单独模块。页面进来以后先问一句当前设备到底能不能跑不能跑就给用户一个正常的降级页面而不是让页面白屏。interface SpatialCapabilityResult { canPreview: boolean; canReconstruct: boolean; reason: string; } export class SpatialCapabilityGuard { async check(): PromiseSpatialCapabilityResult { const apiLevel this.readApiLevel(); const gpuReady await this.checkGpuAbility(); const reconReady this.canUseSystemCapability(SystemCapability.Graphics.SpatialRecon); if (apiLevel 26) { return { canPreview: false, canReconstruct: false, reason: 需要 HarmonyOS 7 / API 26 或更高版本 }; } if (!gpuReady) { return { canPreview: false, canReconstruct: false, reason: 当前设备图形能力不足先展示普通图片预览 }; } return { canPreview: true, canReconstruct: reconReady, reason: reconReady ? 支持 3DGS 预览和端侧重建 : 支持 3D 预览但端侧重建不可用 }; } private readApiLevel(): number { return 26; } private async checkGpuAbility(): Promiseboolean { return true; } private canUseSystemCapability(name: string): boolean { return name.length 0; } }这段代码的重点不是具体接口名而是流程先判版本再判图形能力再判 Spatial Recon 能力。很多 3D 页面出问题就是因为把这些判断放到了渲染失败以后才补。那时候用户已经看到空页面了日志也很难对应到是哪一段出错。案例一离线 3DGS 模型预览第一个案例先不做端侧重建只做一个离线模型预览。比如产品同学给了一个已经处理好的 3DGS 模型文件开发者要在应用里做预览、旋转、切换视角和失败兜底。这个场景最适合拿来验证接入边界因为它可以绕开采集和重建先证明“模型产物到页面展示”这段链路是稳定的。复现步骤准备一个体积可控的 3DGS 或 3D 模型资源放在沙箱目录或应用资源目录页面进入时先跑能力检测能力通过后创建模型资源描述交给 ArkGraphics 3D 场景控制器加载加载成功后允许用户旋转、缩放、切换视角加载失败时显示封面图、错误原因和重试按钮。type SpatialAssetFormat PLY | GLB | MP4_3DGS; type PreviewPhase idle | checking | loading | ready | fallback | failed; interface SpatialAsset { id: string; name: string; format: SpatialAssetFormat; localUri: string; posterUri: string; byteSize: number; } interface PreviewState { phase: PreviewPhase; assetId: string; message: string; progress: number; } export class SpatialPreviewStore { private state: PreviewState { phase: idle, assetId: , message: , progress: 0 }; getState(): PreviewState { return { ...this.state }; } async open(asset: SpatialAsset, guard: SpatialCapabilityGuard, scene: ThreeDSceneController): PromisePreviewState { this.state { phase: checking, assetId: asset.id, message: 正在检查设备能力, progress: 5 }; const ability await guard.check(); if (!ability.canPreview) { this.state { phase: fallback, assetId: asset.id, message: ability.reason, progress: 100 }; return this.getState(); } this.state { phase: loading, assetId: asset.id, message: 正在加载 3D 模型, progress: 30 }; try { await scene.loadAsset(asset); await scene.applyDefaultCamera(); await scene.applySoftLight(); this.state { phase: ready, assetId: asset.id, message: 模型已就绪, progress: 100 }; } catch (error) { this.state { phase: failed, assetId: asset.id, message: 模型加载失败 String(error), progress: 100 }; } return this.getState(); } }这里我把页面状态写成了 PreviewState不是在页面里散落一堆布尔值。原因很简单3D 能力的失败原因很多如果只写 isLoading、isError后面根本看不出到底是能力不支持、文件不存在、模型太大还是场景初始化失败。场景控制器只管 3D 展示export class ThreeDSceneController { private sceneReady: boolean false; async init(surfaceId: string): Promisevoid { if (!surfaceId) { throw new Error(缺少 3D 渲染承载节点); } this.sceneReady true; } async loadAsset(asset: SpatialAsset): Promisevoid { if (!this.sceneReady) { throw new Error(3D 场景还没有初始化); } if (!asset.localUri || asset.byteSize 0) { throw new Error(模型文件不存在或大小异常); } } async applyDefaultCamera(): Promisevoid {} async applySoftLight(): Promisevoid {} dispose(): void { this.sceneReady false; } }这个控制器故意不碰 UI 状态也不碰业务数据。它只负责三件事初始化场景、加载资源、释放场景。这样后面如果模型显示不出来排查顺序就很明确先看资源再看场景再看相机和灯光。案例二端侧重建会话兜底第二个案例再进入端侧重建。端侧重建比离线预览更容易出问题因为它多了采集、计算、进度、暂停、失败恢复和产物落盘。我会把它当成长任务处理而不是当成一个按钮点击事件。复现步骤用户进入重建页先跑能力检测能力不满足时直接切换到“上传已有模型”或“查看示例模型”能力满足时创建重建会话推送输入素材或采集帧监听进度、失败、完成完成后把产物写入资源仓库再进入 ArkGraphics 3D 预览页面退出或应用进后台时能取消、暂停或恢复不留下半截状态。type ReconPhase idle | preparing | running | saving | done | cancelled | failed; interface ReconProgress { phase: ReconPhase; percent: number; tips: string; outputUri: string; } interface ReconInput { sourceUri: string; expectedFormat: SpatialAssetFormat; maxDurationSeconds: number; } export class SpatialReconSessionGuard { private phase: ReconPhase idle; private cancelRequested: boolean false; async run(input: ReconInput, guard: SpatialCapabilityGuard): PromiseReconProgress { const ability await guard.check(); if (!ability.canReconstruct) { return { phase: failed, percent: 0, tips: ability.reason, outputUri: }; } this.phase preparing; this.cancelRequested false; if (!input.sourceUri) { return { phase: failed, percent: 0, tips: 没有可用于重建的输入素材, outputUri: }; } try { this.phase running; await this.waitOrCancel(800); this.report(extractFrames, 15, 正在抽取关键帧); await this.waitOrCancel(1200); this.report(matchFeature, 40, 正在匹配空间特征); await this.waitOrCancel(1500); this.report(buildGaussian, 70, 正在生成 3DGS 表示); this.phase saving; const outputUri await this.persistOutput(input); this.phase done; return { phase: done, percent: 100, tips: 重建完成可以进入 3D 预览, outputUri }; } catch (error) { if (this.cancelRequested) { return { phase: cancelled, percent: 100, tips: 用户已取消重建, outputUri: }; } return { phase: failed, percent: 100, tips: 重建失败 String(error), outputUri: }; } } cancel(): void { this.cancelRequested true; } private async waitOrCancel(delay: number): Promisevoid { if (this.cancelRequested) { throw new Error(cancelled); } await new Promisevoid((resolve) setTimeout(resolve, delay)); } private report(stage: string, percent: number, tips: string): void { console.info([SpatialRecon] stage percent % tips); } private async persistOutput(input: ReconInput): Promisestring { return input.sourceUri .3dgs. input.expectedFormat.toLowerCase(); } }第一重建会话要能取消。用户退出页面、应用进后台、设备发热、空间不足都可能让重建不适合继续跑。没有取消逻辑页面看起来只是一个进度条实际上后台状态已经乱了。第二重建完成后不要直接把产物塞给页面。先落到资源仓库再由预览页面加载。这样“重建”和“展示”是两条链路。重建失败不影响已有模型预览预览失败也不需要重新跑一遍重建。为什么不建议把所有逻辑塞进一个 ArkUI 页面3DGS 接入里最危险的写法是页面里既管按钮、又管采集、又管重建、又管模型加载、又管错误提示。代码一开始看着直观后面会出现几个问题写法短期效果后期风险页面里直接写重建逻辑Demo 快页面退出后任务状态难处理页面里直接写模型加载少建一个类预览失败原因混在 UI 里页面同时管重建和渲染看起来链路短后面没法单独复用离线预览没有资源仓库少写落盘代码重建结果丢失或无法恢复只做成功态截图好看真机异常时用户只能看到空页面更稳的结构是下面这样class SpatialFeatureEntry { private capabilityGuard new SpatialCapabilityGuard(); private previewStore new SpatialPreviewStore(); private reconSession new SpatialReconSessionGuard(); private sceneController new ThreeDSceneController(); async previewExistingAsset(asset: SpatialAsset): PromisePreviewState { return this.previewStore.open(asset, this.capabilityGuard, this.sceneController); } async reconstructThenPreview(input: ReconInput): PromisePreviewState | ReconProgress { const progress await this.reconSession.run(input, this.capabilityGuard); if (progress.phase ! done) { return progress; } const asset: SpatialAsset { id: asset- Date.now(), name: 端侧重建结果, format: input.expectedFormat, localUri: progress.outputUri, posterUri: , byteSize: 1 }; return this.previewStore.open(asset, this.capabilityGuard, this.sceneController); } }这个入口类把两种场景放在一起已有模型预览、重建后预览。页面不需要知道底层到底是离线资源还是刚重建出来的资源。页面只拿到状态然后决定展示进度、错误、封面图还是 3D 场景。真机验证时我会看这几个结果验证点通过标准失败时先查哪里能力检测不支持设备能看到降级说明API 版本、设备能力、地区限制离线模型预览模型能显示旋转缩放不卡死模型路径、模型体积、场景初始化相机和灯光首屏能看到模型主体默认相机距离、模型包围盒、灯光方向端侧重建会话有进度、可取消、失败有原因会话状态机、输入素材、存储空间产物落盘重启页面后还能打开结果沙箱路径、资源索引、文件大小后台恢复切后台再回来不会卡在旧进度生命周期、取消逻辑、状态快照这里面最容易被忽略的是“产物落盘”。如果重建结果只存在内存里页面一退出就没了。用户看到的是“刚才明明成功了回来怎么没了”。这类问题看起来像 UI bug实际是资源生命周期没设计好。图片和封面也要按技术链路来做3DGS 文章的图不能只放一张随便截的编辑器图。读者点进来之前封面就应该告诉他这篇在讲什么。我的封面会把链路画成五段Capability Gate先判设备和版本Spatial Recon Session处理重建会话3DGS Asset保存模型产物ArkGraphics 3D Scene负责场景展示Fallback不支持或失败时可降级。这样读者不用先读一大段文字也能大概知道文章重点是“边界”和“接入链路”不是单纯介绍名词。我自己的实现取舍如果只是写一个演示页面我可以把所有逻辑放在一个页面里半天就能看到效果。但如果要放进正式项目我会接受多写几个类把边界分开。原因有三个3DGS 能力本身有设备和环境限制能力检测必须独立端侧重建是长任务不能跟页面生命周期绑死3D 展示会继续扩展相机、灯光、材质、动画和后处理不能被重建流程拖住。这不是为了把代码写复杂而是为了后面能排查、能复用、能降级。尤其是 HarmonyOS 7 / API 26 这类新能力刚接入时更应该把失败路径放到台面上。只有成功截图没有失败路径项目里不一定稳。最后总结3DGS 端侧重建不要当成一个“页面组件”来接。更合理的理解是Spatial Recon Kit 处理重建和 3DGS 资源相关能力ArkGraphics 3D 负责把资源放进 3D 场景里展示ArkUI 负责状态和交互AR Engine 只在需要现实空间对齐时加入。开发者真正要补的是边界意识先检测能力再跑会话再保存产物再进入场景失败时给降级。这样写出来的 3DGS 功能不会只停留在 Demo 截图而是能经得住真机、弱设备、后台恢复和资源异常这些情况。
返回列表