实战:用 ArkGraphics 3D 做 3D 产品展示

电商 APP 里越来越多地使用 3D 产品展示——用户可以 360 度旋转查看商品,比静态图片更直观。今天我们用 ArkGraphics 3D 来实现一个 3D 产品查看器。

3D产品展示整体流程

下面是3D产品展示的完整工作流程:

拖动

双指缩放

无操作

创建3D场景Scene.load

设置相机位置和朝向

添加光照效果

加载glTF模型

设置模型属性

启动自动旋转

用户交互?

停止自动旋转

调整模型大小

继续自动旋转

更新旋转角度

限制缩放范围0.5-3倍

更新模型姿态

3D 产品展示的功能

  1. 加载 3D 模型:加载商品的 glTF 模型
  2. 旋转查看:用户可以拖动旋转模型
  3. 缩放:双指缩放
  4. 自动旋转:不操作时自动旋转展示

第一步:创建场景和相机

import { Scene, RenderContext, Node, Camera } from '@kit.ArkGraphics3D';
import { common } from '@kit.AbilityKit';

导入需要的模块。

@Entry
@Component
struct ProductViewer {
  @State status: string = '加载中...';
  @State rotationY: number = 0;
  @State scale: number = 1;

  private scene: Scene | null = null;
  private productNode: Node | null = null;
  private camera: Camera | null = null;
  private isAutoRotating: boolean = true;

定义状态变量。

  async aboutToAppear() {
    await this.loadModel();
  }

  async loadModel() {
    try {
      // 创建场景
      this.scene = await Scene.load();

Scene.load() 创建一个新的 3D 场景。

      // 设置相机
      this.camera = this.scene.mainCamera;
      this.camera.position = { x: 0, y: 1, z: 3 };  // 相机在模型前方 3 米
      this.camera.lookAt({ x: 0, y: 0, z: 0 });     // 看向原点

相机放在模型前方 3 米、上方 1 米的位置,看向原点。这样可以俯视模型,看到更好的展示效果。

第二步:设置光照

      // 设置光照
      let lightNode = this.scene.root.createChild('Light');
      lightNode.light.type = 'directional';
      lightNode.light.color = { r: 1, g: 1, b: 1 };
      lightNode.light.intensity = 1;
      lightNode.position = { x: 2, y: 3, z: 1 };

方向光(directional)模拟太阳光,从一个方向均匀照射。你也可以用点光源(point)或聚光灯(spot)来实现不同的光照效果。

第三步:加载 3D 模型

      // 加载 3D 模型
      let uri = "OhosRawFile://assets/gltf/product.glb";
      this.productNode = await this.scene.load(uri, 0, this.scene.root);

OhosRawFile:// 指向项目的 rawfile 文件夹。.glb 是 glTF 的二进制格式,比 .gltf 更紧凑。

      // 设置模型属性
      this.productNode.position = { x: 0, y: 0, z: 0 };
      this.productNode.scale = { x: 1, y: 1, z: 1 };
      this.productNode.visible = true;

      this.status = '加载成功';

设置模型的位置、缩放、可见性。

第四步:自动旋转

      this.startAutoRotation();

    } catch (err) {
      this.status = `加载失败: ${err}`;
    }
  }

  startAutoRotation() {
    // 自动旋转
    setInterval(() => {
      if (this.isAutoRotating && this.productNode != null) {
        this.rotationY += 1;
        this.productNode.rotation = {
          x: 0,
          y: Math.sin(this.rotationY * Math.PI / 180),
          z: 0,
          w: Math.cos(this.rotationY * Math.PI / 180)
        };
      }
    }, 16);  // 约 60fps
  }

每 16ms 更新一次旋转角度(约 60fps)。旋转用四元数表示,绕 Y 轴旋转的四元数是 (0, sin(θ/2), 0, cos(θ/2))

第五步:用户交互

  onDrag(deltaX: number, deltaY: number) {
    // 用户拖动时停止自动旋转
    this.isAutoRotating = false;

    if (this.productNode != null) {
      this.rotationY += deltaX * 0.5;
      this.productNode.rotation = {
        x: Math.sin(deltaY * Math.PI / 360),
        y: Math.sin(this.rotationY * Math.PI / 180),
        z: 0,
        w: Math.cos(this.rotationY * Math.PI / 180)
      };
    }
  }

用户拖动时停止自动旋转,根据拖动距离更新旋转角度。

  onPinch(scale: number) {
    if (this.productNode != null) {
      this.scale = Math.max(0.5, Math.min(3, this.scale * scale));
      this.productNode.scale = {
        x: this.scale,
        y: this.scale,
        z: this.scale
      };
    }
  }

双指缩放时更新模型大小,限制在 0.5-3 倍之间。

  resetView() {
    this.rotationY = 0;
    this.scale = 1;
    this.isAutoRotating = true;

    if (this.productNode != null) {
      this.productNode.rotation = { x: 0, y: 0, z: 0, w: 1 };
      this.productNode.scale = { x: 1, y: 1, z: 1 };
    }
  }

重置视角。

第六步:换肤功能

  changeColor(r: number, g: number, b: number) {
    if (this.productNode == null) return;

    // 遍历模型的所有材质,修改颜色
    let meshCount = this.productNode.getChildCount();
    for (let i = 0; i < meshCount; i++) {
      let child = this.productNode.getChild(i);
      if (child != null && child.material != null) {
        child.material.baseColorFactor = { r, g, b, a: 1 };
      }
    }
  }

修改材质的基础颜色(baseColorFactor),可以实现"换色"功能。这在电商 APP 中很常见——同一个商品,用户可以切换不同颜色查看效果。

第七步:页面布局

  build() {
    Column() {
      Text('3D 产品展示')
        .fontSize(20)
        .margin({ top: 20 })

      Text(this.status)
        .fontSize(14)
        .fontColor('#999999')
        .margin({ top: 10 })

      // 3D 渲染区域
      // 实际项目中,这里会放一个 Scene 组件
      // 支持拖动旋转和双指缩放

      // 操作提示
      Text('拖动旋转 | 双指缩放')
        .fontSize(12)
        .fontColor('#cccccc')
        .margin({ top: 20 })

显示状态和操作提示。

      // 控制按钮
      Row() {
        Button('重置视角')
          .margin(10)
          .onClick(() => this.resetView())

        Button('自动旋转')
          .margin(10)
          .onClick(() => {
            this.isAutoRotating = !this.isAutoRotating;
          })
      }
      .margin({ top: 20 })

添加重置和自动旋转按钮。

      // 颜色选择(换肤功能)
      Text('选择颜色')
        .fontSize(14)
        .margin({ top: 20 })

      Row() {
        Button()
          .width(40)
          .height(40)
          .backgroundColor('#ff4081')
          .margin(5)
          .onClick(() => this.changeColor(1, 0, 0))

        Button()
          .width(40)
          .height(40)
          .backgroundColor('#2196f3')
          .margin(5)
          .onClick(() => this.changeColor(0, 0.5, 1))

        Button()
          .width(40)
          .height(40)
          .backgroundColor('#4caf50')
          .margin(5)
          .onClick(() => this.changeColor(0, 0.8, 0))

        Button()
          .width(40)
          .height(40)
          .backgroundColor('#ff9800')
          .margin(5)
          .onClick(() => this.changeColor(1, 0.6, 0))
      }
      .margin({ top: 10 })
    }
    .width('100%')
    .height('100%')
  }

添加颜色选择按钮。

  aboutToDisappear() {
    if (this.scene != null) {
      this.scene.destroy();
    }
  }
}

页面销毁时释放场景资源。

换肤功能实现流程

通过修改材质实现产品换色功能:

用户选择颜色

获取模型子节点数量

遍历所有子节点

子节点有材质?

修改baseColorFactor

跳过该节点

设置新的RGB值

更新材质颜色

渲染新颜色效果

实际应用中的优化

1. 模型优化

  • 减面:3D 模型的面数越少,加载和渲染越快。电商产品模型一般控制在 10 万面以内
  • 压缩纹理:使用压缩纹理格式(如 KTX2),减少内存占用

2. 交互优化

  • 惯性旋转:松手后模型继续旋转,逐渐减速
  • 边界限制:限制旋转角度,防止模型翻转到看不到的位置

3. 加载优化

  • 预加载:APP 启动时就预加载常用模型
  • 缓存:加载过的模型缓存到本地

优化方向总结

3D产品展示需要在多个方面进行优化:

3D产品展示优化

模型优化

交互优化

加载优化

减面控制10万面以内

压缩纹理KTX2格式

惯性旋转效果

边界角度限制

APP启动预加载

本地缓存机制

提升渲染性能

提升用户体验

减少等待时间

小结

3D 产品展示的核心:

  1. Scene.load() 创建场景
  2. scene.load(uri) 加载 glTF 模型
  3. 设置相机位置和光照
  4. 通过 Node.rotation 实现旋转
  5. 通过 Node.scale 实现缩放
  6. 通过 material.baseColorFactor 实现换色

ArkGraphics 3D 提供了完整的 3D 场景管理能力,做产品展示绰绰有余。实际项目中,你需要重点关注模型优化和交互体验。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐