从拍照到建模:HarmonyOS 7 3DGS端侧重建完整实战指南

适用版本:HarmonyOS 7.0.0 (API 26) | DevEco Studio NEXT | 旗舰芯片真机

本文基于真实项目开发经验,完整记录从权限配置、图像采集、C++层重建、ArkTS渲染到性能优化的全流程,附可运行的工程代码与踩坑总结。


一、为什么端侧3D重建是空间计算的"入场券"

2026年的今天,我们谈论空间计算时已经不再停留在概念层面。大众点评用3DGS做实景探店、京东用它做商品3D展示、文旅App里你可以"走"进一座古建筑------这些体验的底层,都是同一项技术:3D Gaussian Splatting(3DGS,3D高斯溅射)

但真正改变游戏规则的,不是3DGS本身,而是它从云端走到了端侧

HarmonyOS 7把3DGS重建能力下放到手机NPU,意味着:

  • 隐私安全:原始照片不用上传服务器,全部计算在本地完成
  • 低延迟:室内、户外无网环境照样建模
  • 普惠化:不再需要专业扫描设备,一部旗舰手机就能搞定

我们团队最近做了一款空间重建Demo App,从立项到跑通完整链路踩了不少坑。这篇文章把完整过程写下来,希望能帮到同样在探索空间计算的开发者。


二、先搞清楚:重建在C层,ArkTS只负责"看"和"改"

这是最容易踩的第一个坑。

网上很多文章说"三行ArkTS代码实现3D重建",但如果你去翻官方API文档,会发现 Spatial Recon Kit 的 ArkTS 接口只有两个模块

模块 职责 起始版本
spatialRender 3DGS模型的加载与渲染,含滤镜效果 6.0.1 (API 21)
spatialEdit 3DGS模型的选择、上色、删除、导出 7.0 (API 26)

注意,这里面没有"重建"模块。

重建管线是纯 C/C++(NDK)的,官方指南标题就叫「重建三维场景(C/C++)」。整个架构是分层的:

复制代码
┌─────────────────────────────────────────────┐
│          ArkTS 层(UI / 交互 / 渲染)        │
│  ┌─────────────┐  ┌──────────────────────┐  │
│  │ spatialRender│  │     spatialEdit      │  │
│  │  加载+渲染   │  │   选择/上色/导出     │  │
│  └──────┬──────┘  └──────────┬───────────┘  │
│         └─────────┬──────────┘               │
│                   │ ArkGraphics 3D            │
└───────────────────┼───────────────────────────┘
                    │ NAPI 桥接
┌───────────────────┼───────────────────────────┐
│    C/C++ 层(重建核心)                        │
│  ┌────────────────────────────────────────┐   │
│  │  HMS_SpatialRecon_* 系列 C API         │   │
│  │  检测→建会话→喂帧→重建→查进度→保存     │   │
│  └────────────────────────────────────────┘   │
└───────────────────────────────────────────────┘

这个认知非常关键------你不可能只用ArkTS完成端侧重建。但凡看到"纯ArkTS生成3D模型"的示例,先去官方API总览核对模块名。


三、第一关:设备门槛,三重限制

写第一行代码之前,先确认你的设备"配不配跑"。官方给了三重限制:

限制维度 具体要求
地域 仅中国境内(不含港澳台)
设备形态 Phone、Tablet、PC/2in1、TV
芯片 仅保证旗舰芯片(Kirin 9020/9030S/9030/9030 Pro 及以后)
模拟器 不支持

芯片这一条官方说得很克制但很明确:由于空间重建对性能开销较大,当前仅保证旗舰芯片上的用户体验。其他芯片即使接口返回"支持",也无法保证重建耗时和质量。

翻译一下:"支持"是三态,不是布尔值。

所以能力检测要这样写:

cpp 复制代码
// spatial_gate.h
#include "spatial/spatial_recon_interface.h"

enum class ReconGate {
    UNSUPPORTED,  // 设备根本不支持
    RISKY,        // 接口说支持,但芯片不在保证名单,可跑但别承诺效果
    READY         // 支持且芯片在保证名单
};

ReconGate checkReconGate() {
    HMS_SpatialReconStatus ret =
        HMS_SpatialRecon_IsSupport(SPATIAL_RECON_MODEL_TYPE_GS);
    if (ret != SPATIAL_RECON_STATUS_SUCCESS) {
        return ReconGate::UNSUPPORTED;
    }
    // 芯片分档由业务层结合机型名单判断
    return isFlagshipChip() ? ReconGate::READY : ReconGate::RISKY;
}

对于 RISKY 级别的设备,我们的策略是:允许使用但给出提示,同时默认降低高斯点上限(从50万降到20万),优先保证速度和稳定性。


四、工程搭建:从module.json5到CMakeLists

4.1 权限与系统能力声明

entry/src/main/module.json5 中需要声明相机、媒体读写权限和两项关键系统能力:

json 复制代码
{
  "requestPermissions": [
    {
      "name": "ohos.permission.CAMERA",
      "reason": "用于采集物体图像进行3D重建",
      "usedScene": { "abilities": ["EntryAbility"] }
    },
    {
      "name": "ohos.permission.READ_MEDIA_IMAGES",
      "reason": "读取图片用于3D重建"
    },
    {
      "name": "ohos.permission.WRITE_MEDIA_FILES",
      "reason": "保存生成的3D模型文件"
    }
  ],
  "abilities": [
    {
      "name": "EntryAbility",
      "systemCapabilities": [
        "SystemCapability.Graphics.SpatialRecon",
        "SystemCapability.Graphics.SpatialRender"
      ]
    }
  ]
}

⚠️ 重点提醒SpatialReconSpatialRender 两大系统能力缺一不可。少声明任何一个,API调用都会直接返回失败,而且报错信息不一定明显------我在这里卡了两个小时。

4.2 Native工程配置

entry/build-profile.json5 中配置CMake构建:

json 复制代码
{
  "apiType": "stageMode",
  "buildOption": {
    "externalNativeOptions": {
      "path": "./src/main/cpp/CMakeLists.txt",
      "arguments": "-v -DCMAKE_VERBOSE_MAKEFILE=ON",
      "cppFlags": ""
    }
  }
}

CMakeLists.txt 核心配置:

cmake 复制代码
cmake_minimum_required(VERSION 3.4.1)
project(spatialrecon_napi)

set(CMAKE_CXX_STANDARD 17)

add_library(spatialrecon_napi SHARED
    spatial_recon_native.cpp
    spatial_recon_napi.cpp
)

target_link_libraries(spatialrecon_napi
    ace_napi
    hilog_ndk
    spatial_recon_ndk   # 空间重建 NDK 库
)

五、核心实现:C++层重建管线

这是整个3DGS能力的"发动机"。我把重建过程封装成了一个 ReconstructionManager 类,管理从创建会话到保存结果的完整生命周期。

5.1 完整重建流程

复制代码
创建会话 → 推送帧数据 → 启动重建 → 监听进度 → 保存结果 → 销毁会话
     ↓           ↓           ↓          ↓          ↓
  CreateSession PushFrame StartRecon  Progress  SaveResult

5.2 创建会话

cpp 复制代码
bool ReconstructionManager::createSession(SpatialReconMode mode,
                                          SpatialReconQuality quality,
                                          int maxGaussianCount) {
    HMS_SpatialReconConfig config;
    config.mode = mode;                     // 物体/空间模式
    config.quality = quality;               // 质量等级
    config.maxGaussianCount = maxGaussianCount; // 高斯点上限
    config.enableDensification = true;      // 开启稠密化

    HMS_SpatialReconStatus ret =
        HMS_SpatialRecon_CreateSession(&config, &session_);

    return ret == SPATIAL_RECON_STATUS_SUCCESS;
}

参数选择建议

场景 模式 质量 高斯点上限 预计耗时
小物件(杯子、手办) OBJECT HIGH 50万 1~3分钟
中等物体(椅子、雕塑) OBJECT BALANCE 30万 2~4分钟
室内场景(房间、办公室) SPACE BALANCE 100万 5~10分钟

5.3 推送帧数据

从相机获取的NV21格式帧,直接推送给重建引擎:

cpp 复制代码
bool ReconstructionManager::pushFrame(const uint8_t* imageData,
                                      int width, int height,
                                      int64_t timestamp) {
    HMS_SpatialReconFrame frame;
    frame.data = imageData;
    frame.width = width;
    frame.height = height;
    frame.timestamp = timestamp;
    frame.format = SPATIAL_RECON_IMAGE_FORMAT_NV21;

    HMS_SpatialReconStatus ret =
        HMS_SpatialRecon_PushFrame(session_, &frame);

    if (ret == SPATIAL_RECON_STATUS_FRAME_DROPPED) {
        // 帧被丢弃是正常的------系统会自动选取关键帧
        return true;
    }
    return ret == SPATIAL_RECON_STATUS_SUCCESS;
}

💡 关键帧无需手动筛选 。调用 PushFrame 时系统会自动选取关键帧,开发者不需要自己做去重或采样。这是鸿蒙这套方案非常省心的一点。

5.4 启动重建与进度监听

帧采集完毕后,调用 StartReconstruction 启动高斯优化:

cpp 复制代码
bool ReconstructionManager::startReconstruction() {
    HMS_SpatialReconStatus ret = HMS_SpatialRecon_StartReconstruction(
        session_,
        // 进度回调
        [](float progress, void* userData) {
            auto* mgr = static_cast<ReconstructionManager*>(userData);
            if (mgr->progressCallback_) {
                mgr->progressCallback_(progress, "reconstructing");
            }
        },
        // 完成回调
        [](HMS_SpatialReconStatus status, void* userData) {
            auto* mgr = static_cast<ReconstructionManager*>(userData);
            mgr->setState(status == SPATIAL_RECON_STATUS_SUCCESS
                          ? ReconState::COMPLETED
                          : ReconState::FAILED);
        },
        this
    );
    return ret == SPATIAL_RECON_STATUS_SUCCESS;
}

5.5 保存结果

支持三种输出格式:PLY、MP4、GLB。

cpp 复制代码
bool ReconstructionManager::saveResult(const std::string& outputPath,
                                       SpatialReconOutputFormat format) {
    return HMS_SpatialRecon_SaveResult(
        session_,
        outputPath.c_str(),
        format,
        progressCallback,
        completionCallback,
        this
    ) == SPATIAL_RECON_STATUS_SUCCESS;
}

格式选择建议

  • PLY:通用格式,文件大但兼容性好,适合导出到PC端进一步处理
  • MP4:鸿蒙特有格式,文件最小(有压缩),加载最快,推荐移动端使用
  • GLB:glTF格式,适合需要和现有3D管线集成的场景

六、ArkTS层:渲染与交互

重建完成后,模型文件就生成了。接下来要在UI层展示它------这部分完全用ArkTS就能搞定。

6.1 加载3DGS模型

SpatialReconKitArkGraphics 3D 模块的扩展,加载3DGS模型需要先加载插件:

typescript 复制代码
import { spatialRender } from '@kit.SpatialReconKit';
import { Scene, RenderContext } from '@kit.ArkGraphics3D';

async function loadGSModel(xComponentId: string, modelUri: string) {
  // 1. 获取渲染上下文
  const renderContext = Scene.getDefaultRenderContext();
  if (!renderContext) return null;

  // 2. 加载 GSPlugin 插件
  renderContext.loadPlugin(spatialRender.GSPlugin.PLUGIN_ID);

  // 3. 创建场景
  const scene = await Scene.create(xComponentId);

  // 4. 加载 3DGS 模型
  const gsNode = await spatialRender.GSPlugin.loadGSNode(
    scene,
    { uri: modelUri, offset: 0 },
    scene.root
  );

  return { scene, gsNode };
}

6.2 支持的模型格式

格式 文件大小 加载速度 编辑能力 适用场景
MP4 ⭐⭐⭐ 最小 ⭐⭐⭐ 最快 一般 移动端展示
PLY ⭐ 较大 ⭐⭐ 中等 ⭐⭐⭐ 最强 跨平台、二次编辑
GLB ⭐⭐ 中等 ⭐⭐ 中等 ⭐⭐ 中等 3D管线集成

6.3 相机与交互

加载模型后,需要设置相机和用户交互。3DGS模型的浏览体验和传统3D模型类似,支持旋转、缩放、平移:

typescript 复制代码
// 设置透视相机
const cameraNode = scene.root.createChild('main_camera');
const camera = cameraNode.createComponent(CameraType.CAMERA_TYPE_PERSPECTIVE);
camera.setIsMain(true);
camera.setFOV(60);       // 视场角
camera.setNear(0.1);     // 近裁剪面
camera.setFar(1000);     // 远裁剪面
cameraNode.translate(0, 0, 5); // 相机初始位置

6.4 风格化滤镜

GSPlugin 支持对3DGS渲染结果做风格化处理,比如卡通、素描、怀旧等效果。这是一个很容易出亮点的功能,在文旅、教育类应用中特别受欢迎。


七、沉浸光感:让UI也"空间化"

空间计算不只是3D模型的事,UI层面的"空间感"同样重要。HarmonyOS 7的HDS(HarmonyOS Design System)组件提供了沉浸光感材质能力,让导航栏、底部页签直接获得系统级的通透质感。

7.1 基础用法

typescript 复制代码
import { HdsNavigation, HdsTabs, hdsMaterial } from '@kit.UIDesignKit';

// 底部悬浮页签 + 沉浸光感
HdsTabs({ controller: this.tabsController }) {
  // ... TabContent
}
.barFloatingStyle({
  barBottomMargin: 28,
  // 开启沉浸光感:自适应模式,系统根据设备性能自动调整
  systemMaterialEffect: {
    materialType: hdsMaterial.MaterialType.ADAPTIVE,
    materialLevel: hdsMaterial.MaterialLevel.ADAPTIVE
  }
})

7.2 两种模式怎么选

  • 自适应模式(推荐默认)ADAPTIVE + ADAPTIVE,系统自动匹配设备能力。效果稳定,不会因为性能不够导致掉帧
  • 自定义模式 :先调用 hdsMaterial.getSystemMaterialTypes() 查询设备支持情况,如果返回包含 IMMERSIVE,就可以使用 EXQUISITE(精美/高耗能)或 GENTLE(轻柔)档位

⚠️ 不要在未查询设备能力的情况下直接强开 EXQUISITE,低端机会出现明显掉帧和发热。

7.3 自定义蒙版颜色

默认的渐变蒙版是系统预设的,如果需要匹配应用的品牌色,可以自定义:

typescript 复制代码
.barFloatingStyle({
  gradientMask: {
    maskColor: '#66007AFF',   // 蒙版颜色+透明度
    maskHeight: 166            // 渐变高度
  },
  systemMaterialEffect: {
    materialType: hdsMaterial.MaterialType.ADAPTIVE,
    materialLevel: hdsMaterial.MaterialLevel.ADAPTIVE
  }
})

在我们的3DGS查看器中,我用沉浸光感做了上下两层控制栏------顶部导航栏和底部滤镜选择栏,都是半透明毛玻璃效果。模型在后面旋转,控制栏浮在上面,层次感一下子就出来了。


八、性能优化:让端侧重建"跑得动、跑得稳"

3DGS重建对算力的要求很高,性能优化是能否落地的关键。以下是我们实践中验证有效的优化手段:

8.1 重建前:合理设置参数

优化项 做法 效果
高斯点上限 根据设备性能分档设置(旗舰50万/中端20万) 减少内存占用与计算量
稠密化开关 物体细节要求不高时关闭 重建速度提升约30%
质量模式 快速预览用 FAST,最终生成用 HIGH 平衡效率与质量

8.2 重建中:精细化功耗管理

订阅温控事件,设备过热时自动暂停重建:

typescript 复制代码
// 订阅电池温控事件
commonEventManager.subscribe(
  'usual.event.THERMAL_LEVEL_CHANGED',
  (err, data) => {
    const level = data?.parameters?.level ?? 0;
    if (level >= 4) {
      // 温度过高,暂停重建
      reconManager.pause();
    } else if (level <= 2) {
      // 温度回落,恢复重建
      reconManager.resume();
    }
  }
);

前后台模式切换,应用切后台时自动降为低功耗模式:

cpp 复制代码
// 前台高性能
HMS_SpatialRecon_SetRunningMode(session,
    SPATIAL_RECON_RUNNING_MODE_FOREGROUND);

// 后台低功耗
HMS_SpatialRecon_SetRunningMode(session,
    SPATIAL_RECON_RUNNING_MODE_BACKGROUND);

8.3 渲染时:大场景用Tiled分块加载

对于空间场景(100万+高斯点),一次性加载全量数据会导致:

  • 加载时间长(5~10秒)
  • 内存占用高(500MB+)
  • 低端设备直接OOM

TiledGS分块方案把模型切成一块块"瓦片",视野内的才加载,能显著降低内存峰值。HarmonyOS 7的MP4格式原生支持分块LOD。

8.4 一个真实的优化案例

我们做过一个室内客厅场景的对比测试:

指标 优化前 优化后 提升
重建耗时 8分30秒 5分10秒 ↓39%
内存峰值 720MB 410MB ↓43%
加载耗时 6.8s 1.2s ↓82%
平均帧率 28fps 55fps ↑96%

主要优化手段:高斯点从150万降到80万 + Tiled分块加载 + 关闭非必要的稠密化迭代。


九、工程结构总览

我们的Demo App工程结构如下,供参考:

复制代码
entry/src/main/
├── ets/
│   ├── entryability/
│   │   └── EntryAbility.ets          # 入口Ability
│   ├── pages/
│   │   ├── IndexPage.ets             # 首页(功能入口)
│   │   ├── CapturePage.ets           # 图像采集页
│   │   ├── ViewerPage.ets            # 模型查看器
│   │   ├── GalleryPage.ets           # 模型库
│   │   └── SettingsPage.ets          # 设置页
│   ├── service/
│   │   └── SpatialReconService.ets   # 重建服务封装
│   └── utils/
│       └── DeviceCapability.ets      # 设备能力检测
├── cpp/
│   ├── spatial_recon_native.h        # C++重建管理器头文件
│   ├── spatial_recon_native.cpp      # C++重建管理器实现
│   ├── spatial_recon_napi.cpp        # NAPI桥接层
│   └── CMakeLists.txt                # CMake构建配置
└── resources/
    ├── base/media/                   # 图标资源
    └── rawfile/                      # 3D模型示例文件

十、踩坑总结:这些坑我替你踩过了

10.1 "为什么接口调用一直失败?"

→ 先查 module.json5 里的 systemCapabilities 是否声明了 SpatialReconSpatialRender。漏声明会导致Kit加载失败,但报错信息往往不够明确。

10.2 "为什么模拟器上跑不起来?"

→ 3DGS不支持模拟器,必须用真机调试。而且需要旗舰芯片设备才能获得流畅体验。

10.3 "重建过程中应用切后台就崩了"

→ 需要在Ability的onBackground回调中调用 SetRunningMode(BACKGROUND),让系统降低算力分配。直接切后台可能因资源抢占被系统杀掉。

10.4 "保存的PLY文件特别大,怎么减小?"

→ 推荐用MP4格式,鸿蒙对MP4做了专门的压缩优化,同等质量下体积只有PLY的1/3到1/5。如果必须用PLY,可以降低高斯点数量。

10.5 "同一时刻能有多个重建会话吗?"

→ 不行。同一时刻仅支持一个session进行重建或保存MP4,避免并发操作。如果需要处理多个任务,用队列串行执行。

10.6 "拍摄时有什么技巧?"

→ 三个关键:

  • 重叠率 > 60%:相邻帧之间要有足够重叠,系统才能匹配特征点
  • 匀速移动:不要忽快忽慢,不要突然转向
  • 光线充足:逆光、暗光环境下重建质量会明显下降

十一、未来展望:端侧3D重建的"平民化"时代

做这个项目的过程中,我最深的感受是:3D建模正在从专业工作室走向每个人的口袋

五年前,给一个物体做3D模型需要专业扫描仪 + 建模师 + 数天工时。今天,一个普通消费者掏出手机绕一圈,几分钟就能得到一个可以360°旋转查看的高质量3D模型。

HarmonyOS 7把3DGS做成系统级能力,大大降低了开发门槛------你不需要懂CUDA、不需要搭服务器、不需要研究深度学习模型,只要调用几个API,就能在自己的应用里加入空间重建功能。

接下来可以想象的空间很大:

  • 电商:用户拍一下自己的房间,直接试摆家具
  • 教育:历史文物、生物标本的三维数字化
  • 社交:把真实空间变成3D模型分享给朋友
  • 房产:二手房、租房的3D实景带看

技术的价值,最终要落到体验上。当3D重建像拍照一样简单时,空间计算才真正走进了普通人的生活。而鸿蒙在做的,就是把这个门槛再往下压一压。


参考资料

  1. Spatial Recon Kit 简介 --- HarmonyOS 开发者官网
  2. 加载3DGS模型 --- HarmonyOS 开发者官网
  3. HDS 组件沉浸光感材质 --- 华为开发者联盟
  4. ArkGraphics 3D 简介 --- HarmonyOS 开发者官网
  5. 应用审核指南 --- 华为应用市场
相关推荐
TechEdu2026061 小时前
[人工智能]人工智能硬件与芯片的历史与演进:全球视角下的架构、制造、存储、软件生态与系统经济性
人工智能·ai
单向箔1 小时前
03|AGENTS.md:给 AI 的项目说明书
人工智能·ai编程
Token掘金室1 小时前
OpenAI Realtime API语音对话开发入门
人工智能·ai
资讯综合1 小时前
自费出书哪家公司靠谱 全流程服务能力评估标准参考
人工智能
江润舟1 小时前
万物 | 炼器 从零手搓工业级旋转目标检测网络 · 卷2 —— 计算图、梯度与反向传播(五)
人工智能·深度学习
李游Leo1 小时前
HarmonyOS 7 实战开发 04:适配手机、折叠屏与大屏布局
ios·harmonyos
科技每日热闻1 小时前
企业级大模型 API 统一接入平台该如何选型?
ai
湘美书院--湘美谈教育1 小时前
湘美书院随笔:AI时代的生活经济学
大数据·人工智能·安全·自动化·生活
桃西西呀1 小时前
文件监控 Agent 为什么总在关键时刻掉链子
人工智能·llm·agent