鴻蒙加載3D圖形

最近很火的Remy大家有沒有體驗,平面的2D圖片已經不能滿足用戶,未來可能會更多的相機支持拍攝3D照片。今天來了解一下鴻蒙的3D圖形展示。我找了個汽車的3D模型資源,看一下展示效果。由于能力有限,本文只實現(xiàn)修改相機旋轉角度。

演示.gif

ArkGraphics 3D(方舟3D圖形)基于輕量級的3D引擎以及渲染管線為開發(fā)者提供基礎3D場景繪制能力,供開發(fā)者便捷、高效地構建3D場景并完成渲染。

一個3D場景通常由光源、相機、模型三個關鍵部分組成。

光源:為整個3D場景提供光照,使得3D場景中的模型變得可見。與真實物理場景一致,沒有光源場景將變得一片漆黑,得到的渲染結果也就是全黑色。
相機:為3D場景提供一個觀察者。3D渲染本質上是從一個角度觀察3D場景并投影到2D圖片上。沒有相機就沒有3D場景的觀察者,也就不會得到渲染結果。
模型:3D場景中的模型用于描述對象的形狀、結構和外觀,一般具有網格、材質、紋理、動畫等屬性。一些常見的3D模型格式有OBJ、FBX、glTF等。
模型加載后,可以通過ArkUI的Component3D渲染組件呈現(xiàn)給用戶。

Component3D(sceneOptions?: SceneOptions)

Component3D組件配置選項 SceneOptions

名稱 說明
scene 3D模型資源文件
modelType 3D場景顯示合成方式

設置場景 Scene

屬性

名稱 說明
environment 環(huán)境對象
animations 動畫數(shù)組
root 3D場景樹根結點

方法

名稱 說明
load 待加載的模型文件資源路徑
getNodeByPath 通過路徑獲取結點
getResourceFactory 獲取場景資源工廠對象
destroy 銷毀場景
importNode 從其他場景導入結點
importScene 導入其他場景
renderFrame 控制渲染幀率
createComponent 在指定節(jié)點上創(chuàng)建新的組件
getComponent 獲取對應的組件實例
getDefaultRenderContext 當前對象關聯(lián)的渲染上下文

創(chuàng)建3D場景資源 SceneResourceFactory

名稱 說明
createCamera 根據(jù)結點參數(shù)創(chuàng)建相機
createLight 根據(jù)結點參數(shù)和燈光類型創(chuàng)建燈光
createNode 創(chuàng)建結點
createMaterial 根據(jù)場景資源參數(shù)和材質類型創(chuàng)建材質
createEnvironment 根據(jù)場景資源參數(shù)創(chuàng)建環(huán)境
createGeometry 根據(jù)場景結點參數(shù)和網格數(shù)據(jù)創(chuàng)建幾何對象
createEffect 根據(jù)特效參數(shù)創(chuàng)建特效對象

相機類型,Camera繼承自Node

Node屬性

名稱 類型 說明
position Position3 結點位置
rotation Quaternion 結點旋轉角度
scale Scale3 結點縮放
visible boolean 結點是否可見
nodeType NodeType 結點類型
layerMask LayerMask 結點的圖層掩碼
path string 結點路徑
parent Node 結點的父結點
children Container<T> 結點的子結點

Camera屬性

名稱 說明
fov 視場,取值在0到π弧度之間
nearPlane 近平面,取值大于0
farPlane 遠平面,取值大于nearPlane
enabled 是否使能相機
postProcess 后處理設置
effects 應用于相機輸出的后處理特效
clearColor 將渲染目標(render target)清空后的特定顏色
renderingPipeline 控制渲染管線

3D空間中旋轉的數(shù)學結構 Quaternion(四元數(shù))

用于表示3D空間中旋轉的數(shù)學結構。與傳統(tǒng)的歐拉角相比,四元數(shù)在數(shù)值穩(wěn)定性和避免萬向節(jié)鎖方面具有優(yōu)勢。
四元數(shù)的形式是 (x, y, z, w),由1 個實部(w)+ 3 個虛部(x/y/z) 組成,核心對應 3D 旋轉的兩個關鍵信息:
x/y/z:表示旋轉軸的方向(比如繞 Y 軸旋轉時,x=0、z=0,y≠0);
w:表示繞這個軸旋轉的角度(具體是 w = cos(θ/2),θ 是旋轉的總角度,單位弧度)
旋轉 = 繞 (x,y,z) 這個方向的軸,旋轉 2×arccos(w) 度

坐標系.png

實現(xiàn)源碼

import {
  Scene,
  Camera,
  Node,
  SceneResourceFactory,
  Quaternion
} from '@kit.ArkGraphics3D';

@Entry
@ComponentV2
struct GSNodeTest {
  @Local sceneOpt: SceneOptions | null = null;
  @Local scene: Scene | null = null;
  @Local cam: Camera | null = null;
  @Local node: Node | null | undefined = null;
  @Local cameraZ: number = 10
  @Local rotationX: number = 0
  @Local rotationY: number = 0
  @Local rotationZ: number = 0

  aboutToAppear(): void {
    this.init();
  }
  init(): void {
    if (this.scene == null) {
      // 加載場景資源,支持.gltf和.glb格式,路徑和文件名可根據(jù)項目實際資源自定義
      Scene.load($rawfile("glbs/car.glb"))
        .then(async (result: Scene) => {
          this.scene = result;
          let rf: SceneResourceFactory = this.scene.getResourceFactory();
          // 創(chuàng)建相機
          this.cam = await rf.createCamera({ "name": "Camera" });
          // 設置合適的相機參數(shù)
          this.cam.enabled = true;
          // 設置相機的位置
          this.cam.position.z = this.cameraZ;

          this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;

          this.node = this.scene.root!.children.get(0)
        }).catch((error: Error) => {
        console.error('Scene load failed:', error);
      });
    }
  }
  eulerToQuaternion(xDeg:number, yDeg:number, zDeg:number):Quaternion {
  // 步驟1:角度轉弧度(Math.cos/sin要求弧度制)
  const xRad = xDeg * Math.PI / 180; // 繞X軸旋轉弧度
  const yRad = yDeg * Math.PI / 180; // 繞Y軸旋轉弧度
  const zRad = zDeg * Math.PI / 180; // 繞Z軸旋轉弧度

  // 步驟2:計算半角的正弦/余弦(簡化公式)
  const cx = Math.cos(xRad / 2);
  const sx = Math.sin(xRad / 2);
  const cy = Math.cos(yRad / 2);
  const sy = Math.sin(yRad / 2);
  const cz = Math.cos(zRad / 2);
  const sz = Math.sin(zRad / 2);

  // 步驟3:按XYZ旋轉順序計算四元數(shù)(標準歐拉角轉四元數(shù)公式)
  const w = cx * cy * cz + sx * sy * sz;
  const x = sx * cy * cz - cx * sy * sz;
  const y = cx * sy * cz + sx * cy * sz;
  const z = cx * cy * sz - sx * sy * cz;

  return { x, y, z, w };
}
  build() {
    Column() {
      Row() {
        Column() {
          if (this.sceneOpt) {
            // 通過Component3D呈現(xiàn)3D場景
            Component3D(this.sceneOpt)
          } else {
            Text("Loading···")
          }
        }.width('100%')
      }.height('60%')

      Column() {
        Row({ space: 10 }) {
          Text('相機高度:' + this.cameraZ)
          Slider({
            value: this.cameraZ,
            min: 1,
            max: 30,
            style: SliderStyle.OutSet
          }).width('50%')
            .onChange((value: number) => {
              this.cameraZ = value;
              this.cam!.position.z = value
            })
        }

        Row({ space: 10 }) {
          Text('X軸旋轉:' + this.rotationX)
          Slider({
            value: this.rotationX,
            min: 0,
            max: 360,
            style: SliderStyle.OutSet
          }).width('50%')
            .onChange((value: number) => {
              this.rotationX = value;
              this.node!.rotation = this.eulerToQuaternion(this.rotationX,this.rotationY,this.rotationZ)
            })
        }
        Row({ space: 10 }) {
          Text('Y軸旋轉:' + this.rotationY)
          Slider({
            value: this.rotationY,
            min: 0,
            max: 360,
            style: SliderStyle.OutSet
          }).width('50%')
            .onChange((value: number) => {
              this.rotationY = value;
              this.node!.rotation = this.eulerToQuaternion(this.rotationX,this.rotationY,this.rotationZ)
            })
        }
        Row({ space: 10 }) {
          Text('Z軸旋轉:' + this.rotationZ)
          Slider({
            value: this.rotationZ,
            min: 0,
            max: 360,
            style: SliderStyle.OutSet
          }).width('50%')
            .onChange((value: number) => {
              this.rotationZ = value;
              this.node!.rotation = this.eulerToQuaternion(this.rotationX,this.rotationY,this.rotationZ)
            })
        }
      }
    }
  }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容