HarmonyOS 6 实战:使用 ArkGraphics3D 加载 GLB 模型与 Scene 初始化全流程

文章目录

    • 前言
    • [一、环境准备与 API 导入](#一、环境准备与 API 导入)
      • [1.1 Kit 导入说明](#1.1 Kit 导入说明)
      • [1.2 组件状态设计](#1.2 组件状态设计)
    • [二、Scene 异步加载流程](#二、Scene 异步加载流程)
      • [2.1 使用 Scene.load 加载 GLB 文件](#2.1 使用 Scene.load 加载 GLB 文件)
      • [2.2 GLB 文件放置位置](#2.2 GLB 文件放置位置)
    • 三、相机配置详解
      • [3.1 相机参数说明](#3.1 相机参数说明)
      • [3.2 背景类型对比](#3.2 背景类型对比)
    • 四、生命周期管理
      • [4.1 资源释放](#4.1 资源释放)
    • 总结

前言

随着 HarmonyOS 6 的正式推出,@kit.ArkGraphics3D 为开发者带来了原生 3D 渲染能力。无论是工业产品展示、虚拟试穿还是 AI 生成模型的预览,3D 内容已经成为移动端应用的重要组成部分。本文将以一个完整的 GLB 模型加载项目为例,深入讲解 Scene 的异步加载流程相机创建与配置节点树遍历 以及生命周期管理,帮助你在 HarmonyOS 应用中快速接入 3D 渲染。

本文代码来源于一个真实项目------在 HarmonyOS 设备上实时预览 Hunyuan 3D 生成的 GLB 模型,所有代码片段均可直接运行。

运行效果如下:

一、环境准备与 API 导入

1.1 Kit 导入说明

HarmonyOS 6 将 3D 图形相关 API 整合进 @kit.ArkGraphics3D,使用前只需一行导入:

typescript 复制代码
import { Animation, Scene, Camera, Node, EnvironmentBackgroundType, SceneResourceFactory } from '@kit.ArkGraphics3D';
import { AnimatorResult } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
导入符号 作用
Scene 3D 场景容器,承载所有节点、相机、动画
Camera 相机对象,控制视角和渲染参数
Node 场景节点,承载变换(位置/旋转/缩放)信息
Animation 场景内置动画轨道
SceneResourceFactory 资源工厂,用于创建相机、灯光等运行时资源
EnvironmentBackgroundType 背景类型枚举
AnimatorResult ArkUI 帧驱动动画控制器

提示:@kit.ArkGraphics3D 从 HarmonyOS 12(API Level 12)起支持,确保 build-profile.json5compileSdkVersion 不低于 12。

1.2 组件状态设计

场景加载是异步操作,UI 需要根据加载状态动态切换,因此将 sceneOpt 设计为可为 null 的响应式变量:

typescript 复制代码
@Entry
@Component
struct Index {
  @State sceneOpt: SceneOptions | null = null;
  @State isAnimPlaying: boolean = false;
  scene: Scene | null = null;
  cam: Camera | null = null;
  anim: Animation | null = null;
  modelNode: Node | null = null;

@State sceneOpt 初始为 null,加载完成后赋值,UI 层通过 if (this.sceneOpt) 条件渲染,实现加载态与渲染态的平滑切换。scenecamanimmodelNode 不需要触发 UI 更新,故使用普通成员变量。

二、Scene 异步加载流程

2.1 使用 Scene.load 加载 GLB 文件

Scene.load 是加载 3D 模型的核心 API,支持 GLB/GLTF 格式,接受 $rawfile() 资源引用:

typescript 复制代码
initScene(): void {
  if (this.scene !== null) {
    return;
  }
  Scene.load($rawfile('82.glb'))
    .then(async (result: Scene) => {
      if (!result) {
        hilog.error(0x0000, TAG, 'Scene.load returned null');
        return;
      }
      this.scene = result;
      this.scene.environment.backgroundType = EnvironmentBackgroundType.BACKGROUND_NONE;

      let rf: SceneResourceFactory = this.scene.getResourceFactory();
      this.cam = await rf.createCamera({ name: 'Camera1' });
      this.cam.enabled = true;
      this.cam.position.z = 5;
      this.cam.clearColor = { r: 0.05, g: 0.05, b: 0.05, a: 1.0 };

      // 获取根节点下第一个子节点作为旋转目标
      if (this.scene.root && this.scene.root.children.count() > 0) {
        this.modelNode = this.scene.root.children.get(0);
        hilog.info(0x0000, TAG, 'modelNode: %{public}s', this.modelNode?.name ?? 'null');
      }

      if (this.scene.animations && this.scene.animations.length > 0) {
        this.anim = this.scene.animations[0];
        this.anim.enabled = true;
      }

      this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;
      // 启动帧驱动
      this.frameAnimator?.play();
      hilog.info(0x0000, TAG, 'Scene init done');
    })
    .catch((reason: string) => {
      hilog.error(0x0000, TAG, 'Scene.load error: %{public}s', reason);
    });
}

加载流程分为以下几个关键步骤:

  1. 幂等保护if (this.scene !== null) return; 防止重复加载(页面刷新或 aboutToAppear 多次触发时)
  2. 背景设置BACKGROUND_NONE 关闭环境贴图背景,由相机的 clearColor 填充纯色背景
  3. 相机创建 :通过 SceneResourceFactory.createCamera 异步创建,设置 position.z = 5 将相机置于模型前方
  4. 节点获取 :通过 scene.root.children.get(0) 获取模型的根节点,作为后续旋转的操作目标
  5. 最后赋值sceneOpt 必须在所有初始化完成后才赋值,确保 Component3D 创建时场景已就绪

提示:sceneOpt 赋值顺序非常关键。若在相机创建前就赋值,Component3D 会以无相机状态开始渲染,导致黑屏。始终将 this.sceneOpt = ... 放在 .then() 回调的最后一行。

2.2 GLB 文件放置位置

GLB 文件需放在 entry/src/main/resources/rawfile/ 目录下,通过 $rawfile('文件名.glb') 引用:

复制代码
entry/
  src/
    main/
      resources/
        rawfile/
          82.glb       <- 模型文件放这里

rawfile 目录的文件不经过资源编译,以原始二进制形式打包进 HAP,适合存放体积较大的二进制资源。

三、相机配置详解

3.1 相机参数说明

typescript 复制代码
this.cam = await rf.createCamera({ name: 'Camera1' });
this.cam.enabled = true;
this.cam.position.z = 5;
this.cam.clearColor = { r: 0.05, g: 0.05, b: 0.05, a: 1.0 };
属性 说明
enabled true 激活相机,场景必须有至少一个 enabled 的相机才能渲染
position.z 5 相机在 Z 轴正方向距离原点 5 个单位,从正面看向模型
clearColor {r:0.05, g:0.05, b:0.05, a:1.0} 深灰色背景,避免纯黑背景与模型边缘混淆

提示:position.z 的合适值取决于模型的实际尺寸。GLB 模型单位通常为米,若模型过小或过大,可通过调整 position.z 或模型节点的 scale 属性修正视野。

3.2 背景类型对比

背景类型 效果 适用场景
BACKGROUND_NONE 使用 clearColor 纯色填充 产品展示、AR 合成底层
BACKGROUND_IMAGE 使用指定图片作为背景 场景渲染、全景展示
BACKGROUND_CUBEMAP 使用立方体贴图 写实渲染、IBL 照明
BACKGROUND_EQUIRECTANGULAR 全景 equirectangular 图 360 度场景

四、生命周期管理

4.1 资源释放

3D 场景占用 GPU 显存,必须在页面销毁时主动释放:

typescript 复制代码
aboutToAppear(): void {
  this.initAnimator();
  this.initScene();
}

aboutToDisappear(): void {
  this.frameAnimator?.cancel();
  if (this.scene) {
    this.scene.destroy();
    this.scene = null;
  }
  this.cam = null;
  this.anim = null;
  this.modelNode = null;
}

aboutToDisappear 中的释放顺序很重要:

  1. 先停止 frameAnimator(避免帧回调继续访问已销毁的场景)
  2. 再调用 scene.destroy() 释放 GPU 资源
  3. 将所有引用置 null,帮助 GC 回收 JS 层对象

提示:若不调用 scene.destroy(),切换页面后 GPU 显存不会释放,多次进出页面将导致内存持续增长,最终触发系统 OOM。

总结

本文详细讲解了在 HarmonyOS 6 中使用 @kit.ArkGraphics3D 加载 GLB 模型的完整流程:通过 Scene.load 异步加载模型文件,使用 SceneResourceFactory 创建相机,通过节点树 API 获取模型节点,以及在生命周期钩子中正确管理资源。核心要点是初始化顺序sceneOpt 最后赋值)和资源释放aboutToDisappear 中调用 scene.destroy()),掌握这两点可以避免绝大多数黑屏和内存泄漏问题。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!

需要源码的记得私聊哦

相关推荐
li99yo2 小时前
3DGS的复现
图像处理·pytorch·经验分享·python·3d·conda·pip
王码码20352 小时前
Flutter for OpenHarmony 实战之基础组件:第五十二篇 ListWheelScrollView — 打造极致丝滑的 3D 滚轮选择器
flutter·3d·harmonyos
qq_553760326 小时前
鸿蒙图片上传工具开发全解析及踩坑指南
华为·harmonyos·鸿蒙
轻口味6 小时前
HarmonyOS 6 NDK开发系列1:音视频播放能力介绍
华为·音视频·harmonyos
Navicat中国8 小时前
Navicat Premium Lite 正式登录鸿蒙应用市场
数据库·华为·harmonyos·navicat
瀚高PG实验室8 小时前
同架构大数据量HGDB到HGDB数据迁移
架构·瀚高数据库
唐骁虎9 小时前
Claude Code 全景架构指南——三大核心支柱及四大关键扩展组件
ai·架构·ai编程·claude code
启山智软9 小时前
【启山智软智能商城系统技术架构剖析】
java·前端·架构