1e41f4b71Sopenharmony_ci# ArkGraphics 3D场景搭建以及管理
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci一个3D场景通常由光源、相机、模型三个关键部分组成。
4e41f4b71Sopenharmony_ci- 光源:为整个3D场景提供光照,使得3D场景中的模型变得可见。与真实物理场景一致,没有光源场景将变得一片漆黑,得到的渲染结果也就是全黑色。
5e41f4b71Sopenharmony_ci- 相机:为3D场景提供一个观察者。3D渲染本质上是从一个角度观察3D场景并投影到一张2D图片上。没有相机就没有3D场景的观察者,也就不会得到渲染结果。
6e41f4b71Sopenharmony_ci- 模型:3D场景中的模型用于描述对象的形状、结构和外观,一般具有网格、材质、纹理、动画等属性。一些常见的3D模型格式有OBJ、FBX、glTF等。
7e41f4b71Sopenharmony_ci
8e41f4b71Sopenharmony_ci模型加载后,可以通过ArkUI的[Component3D](../reference/apis-arkui/arkui-ts/ts-basic-components-component3d.md)渲染组件呈现给用户,Component3D也可以对3D模型做自定义渲染。开发者也可以使用ArkTS API对相机和光源进行调节,获得合适的观察角度和光照效果。ArkTS API可通过napi调用AGP中由C++实现的相应能力。
9e41f4b71Sopenharmony_ci
10e41f4b71Sopenharmony_ci![3D场景显示流程](./figures/scene.PNG)
11e41f4b71Sopenharmony_ci
12e41f4b71Sopenharmony_ci## 模型的加载及呈现
13e41f4b71Sopenharmony_ci模型的格式多种多样,目前ArkGraphics 3D仅支持glTF模型的加载,glTF是一种对于3D场景描述的格式,glTF作为一种开源3D场景格式在业界被广泛采用。关于glTF的介绍可以参照[glTF-2.0](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html)。
14e41f4b71Sopenharmony_ci
15e41f4b71Sopenharmony_ci一个glTF模型可以包含光源、相机、模型等3D场景关键要素,如果一个glTF模型中包含相机,使用ArkGraphics 3D提供的接口加载glTF就可以直接完成该相机视角下3D场景的渲染。如果不包含相机,也可以利用ArkGraphics 3D创建一个相机完成渲染。由于3D模型往往数据量很大,通常采用异步方式进行加载,加载成功后将返回一个scene对象,通过该对象可对整个3D场景进行编辑。
16e41f4b71Sopenharmony_ci
17e41f4b71Sopenharmony_ciglTF模型可用Scene提供的[load](../reference/apis-arkgraphics3d/js-apis-inner-scene.md#load)接口加载,示例代码如下:
18e41f4b71Sopenharmony_ci```ts
19e41f4b71Sopenharmony_ciimport { Image, Shader, MaterialType, Material, ShaderMaterial, Animation, Environment, Container, SceneNodeParameters,
20e41f4b71Sopenharmony_ci  LightType, Light, Camera, SceneResourceParameters, SceneResourceFactory, Scene, Node } from '@kit.ArkGraphics3D';
21e41f4b71Sopenharmony_ci
22e41f4b71Sopenharmony_cifunction loadModel() : void {
23e41f4b71Sopenharmony_ci  // 加载模型
24e41f4b71Sopenharmony_ci  let scene: Promise<Scene> = Scene.load($rawfile("gltf/DamagedHelmet/glTF/DamagedHelmet.gltf"));
25e41f4b71Sopenharmony_ci  scene.then(async (result: Scene) => {});
26e41f4b71Sopenharmony_ci}
27e41f4b71Sopenharmony_ci```
28e41f4b71Sopenharmony_ci
29e41f4b71Sopenharmony_ci模型加载成功后,可通过SceneResourceFactory实例创建相机、光源等,再对相机和光源的参数做调节,调整观察角度和光照效果。最后,将Scene实例和ModelType作为SceneOptions传给Component3D组件显示到屏幕。
30e41f4b71Sopenharmony_ci
31e41f4b71Sopenharmony_ci模型显示的示例代码如下:
32e41f4b71Sopenharmony_ci```ts
33e41f4b71Sopenharmony_ciimport { Image, Shader, MaterialType, Material, ShaderMaterial, Animation, Environment, Container, SceneNodeParameters,
34e41f4b71Sopenharmony_ci  LightType, Light, Camera, SceneResourceParameters, SceneResourceFactory, Scene, Node } from '@kit.ArkGraphics3D';
35e41f4b71Sopenharmony_ci
36e41f4b71Sopenharmony_ci@Entry
37e41f4b71Sopenharmony_ci@Component
38e41f4b71Sopenharmony_cistruct Model {
39e41f4b71Sopenharmony_ci  scene: Scene | null = null;
40e41f4b71Sopenharmony_ci  @State sceneOpt: SceneOptions | null = null;
41e41f4b71Sopenharmony_ci  cam: Camera | null = null;
42e41f4b71Sopenharmony_ci
43e41f4b71Sopenharmony_ci  onPageShow(): void {
44e41f4b71Sopenharmony_ci    this.Init();
45e41f4b71Sopenharmony_ci  }
46e41f4b71Sopenharmony_ci
47e41f4b71Sopenharmony_ci  Init(): void {
48e41f4b71Sopenharmony_ci    if (this.scene == null) {
49e41f4b71Sopenharmony_ci      // 加载模型,将gltf文件放置到相关路径,加载时以实际路径为准
50e41f4b71Sopenharmony_ci      Scene.load($rawfile('gltf/DamagedHelmet/glTF/DamagedHelmet.gltf'))
51e41f4b71Sopenharmony_ci      .then(async (result: Scene) => {
52e41f4b71Sopenharmony_ci        this.scene = result;
53e41f4b71Sopenharmony_ci        let rf:SceneResourceFactory = this.scene.getResourceFactory();
54e41f4b71Sopenharmony_ci        // 创建相机
55e41f4b71Sopenharmony_ci        this.cam = await rf.createCamera({ "name": "Camera" });
56e41f4b71Sopenharmony_ci        // 设置合适的相机参数
57e41f4b71Sopenharmony_ci        this.cam.enabled = true;
58e41f4b71Sopenharmony_ci        this.cam.position.z = 5;
59e41f4b71Sopenharmony_ci        this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;
60e41f4b71Sopenharmony_ci      })
61e41f4b71Sopenharmony_ci      .catch((reason: string) => {
62e41f4b71Sopenharmony_ci        console.log(reason);
63e41f4b71Sopenharmony_ci      });
64e41f4b71Sopenharmony_ci    }
65e41f4b71Sopenharmony_ci  }
66e41f4b71Sopenharmony_ci
67e41f4b71Sopenharmony_ci  build() {
68e41f4b71Sopenharmony_ci    Row() {
69e41f4b71Sopenharmony_ci      Column() {
70e41f4b71Sopenharmony_ci        if (this.sceneOpt) {
71e41f4b71Sopenharmony_ci          // 通过Component3D呈现3D场景
72e41f4b71Sopenharmony_ci          Component3D(this.sceneOpt)
73e41f4b71Sopenharmony_ci        } else {
74e41f4b71Sopenharmony_ci          Text("loading ...")
75e41f4b71Sopenharmony_ci        }
76e41f4b71Sopenharmony_ci      }.width('100%')
77e41f4b71Sopenharmony_ci    }.height('60%')
78e41f4b71Sopenharmony_ci  }
79e41f4b71Sopenharmony_ci}
80e41f4b71Sopenharmony_ci```
81e41f4b71Sopenharmony_ci
82e41f4b71Sopenharmony_ci## 相机的创建及管理
83e41f4b71Sopenharmony_ci
84e41f4b71Sopenharmony_ci相机作为3D场景中的重要部分,决定了整个3D场景向2D图片的投影过程,相机的近远平面、Fov角等关键参数也会对整个3D渲染产生重要的影响。开发者可以通过对于相机参数的设置。控制这个渲染过程,得到开发者想要的渲染效果。
85e41f4b71Sopenharmony_ci
86e41f4b71Sopenharmony_ci相机相关控制的示例代码如下:
87e41f4b71Sopenharmony_ci```ts
88e41f4b71Sopenharmony_ciimport { Image, Shader, MaterialType, Material, ShaderMaterial, Animation, Environment, Container, SceneNodeParameters,
89e41f4b71Sopenharmony_ci  LightType, Light, Camera, SceneResourceParameters, SceneResourceFactory, Scene, Node } from '@kit.ArkGraphics3D';
90e41f4b71Sopenharmony_ci
91e41f4b71Sopenharmony_cifunction createCameraPromise() : Promise<Camera> {
92e41f4b71Sopenharmony_ci  return new Promise(() => {
93e41f4b71Sopenharmony_ci    let scene: Promise<Scene> = Scene.load($rawfile("gltf/CubeWithFloor/glTF/AnimatedCube.gltf"));
94e41f4b71Sopenharmony_ci    scene.then(async (result: Scene) => {
95e41f4b71Sopenharmony_ci      let sceneFactory: SceneResourceFactory = result.getResourceFactory();
96e41f4b71Sopenharmony_ci      let sceneCameraParameter: SceneNodeParameters = { name: "camera1" };
97e41f4b71Sopenharmony_ci      // 创建相机
98e41f4b71Sopenharmony_ci      let camera: Promise<Camera> = sceneFactory.createCamera(sceneCameraParameter);
99e41f4b71Sopenharmony_ci      camera.then(async (cameraEntity: Camera) => {
100e41f4b71Sopenharmony_ci        // 使能相机节点
101e41f4b71Sopenharmony_ci        cameraEntity.enabled = true;
102e41f4b71Sopenharmony_ci
103e41f4b71Sopenharmony_ci        // 设置相机的位置
104e41f4b71Sopenharmony_ci        cameraEntity.position.z = 5;
105e41f4b71Sopenharmony_ci
106e41f4b71Sopenharmony_ci        // 设置相机Fov参数
107e41f4b71Sopenharmony_ci        cameraEntity.fov = 60 * Math.PI / 180;
108e41f4b71Sopenharmony_ci
109e41f4b71Sopenharmony_ci        // 可以参照此方式设置相机很多其他的参数
110e41f4b71Sopenharmony_ci        // ...
111e41f4b71Sopenharmony_ci      });
112e41f4b71Sopenharmony_ci      return camera;
113e41f4b71Sopenharmony_ci    });
114e41f4b71Sopenharmony_ci  });
115e41f4b71Sopenharmony_ci}
116e41f4b71Sopenharmony_ci```
117e41f4b71Sopenharmony_ci
118e41f4b71Sopenharmony_ci
119e41f4b71Sopenharmony_ci## 光源的创建及管理
120e41f4b71Sopenharmony_ci
121e41f4b71Sopenharmony_ci3D场景的光源是对于物理世界中光源的一种数据建模,模拟物理世界的光源对于3D场景中的物体产生影响。
122e41f4b71Sopenharmony_ci
123e41f4b71Sopenharmony_ci光源具有很多的类型,比如平行光、锥形光。平行光即是用来模拟生活中的太阳光照,发出的光线处处平行且强度均匀。锥形光则像是我们使用的手电筒,以一个点向一个扇形区域发射光线,且发出的光线会随着距离而衰减。光源的颜色也会对场景中的物体最终的着色产生影响,光源颜色与物体颜色相互作用的计算与真实物理世界保持一致。ArkGraphics 3D提供了创建光源,修改光源各种参数的能力,支撑开发者通过对于光源属性的设置对于3D场景进行调整,得到期望的渲染效果。
124e41f4b71Sopenharmony_ci
125e41f4b71Sopenharmony_ci光源相关控制的示例代码如下:
126e41f4b71Sopenharmony_ci```ts
127e41f4b71Sopenharmony_ciimport { Image, Shader, MaterialType, Material, ShaderMaterial, Animation, Environment, Container, SceneNodeParameters,
128e41f4b71Sopenharmony_ci  LightType, Light, Camera, SceneResourceParameters, SceneResourceFactory, Scene, Node } from '@kit.ArkGraphics3D';
129e41f4b71Sopenharmony_ci
130e41f4b71Sopenharmony_cifunction createLightPromise() : Promise<Light> {
131e41f4b71Sopenharmony_ci  return new Promise(() => {
132e41f4b71Sopenharmony_ci    let scene: Promise<Scene> = Scene.load($rawfile("gltf/CubeWithFloor/glTF/AnimatedCube.gltf"));
133e41f4b71Sopenharmony_ci    scene.then(async (result: Scene) => {
134e41f4b71Sopenharmony_ci      let sceneFactory: SceneResourceFactory = result.getResourceFactory();
135e41f4b71Sopenharmony_ci      let sceneLightParameter: SceneNodeParameters = { name: "light" };
136e41f4b71Sopenharmony_ci      // 创建平行光
137e41f4b71Sopenharmony_ci      let light: Promise<Light> = sceneFactory.createLight(sceneLightParameter, LightType.DIRECTIONAL);
138e41f4b71Sopenharmony_ci      light.then(async (lightEntity: Light) => {
139e41f4b71Sopenharmony_ci        // 设置平行光的颜色属性
140e41f4b71Sopenharmony_ci        lightEntity.color = { r: 0.8, g: 0.1, b: 0.2, a: 1.0 };
141e41f4b71Sopenharmony_ci
142e41f4b71Sopenharmony_ci        // 可以参照此方式设置光源很多其他的参数
143e41f4b71Sopenharmony_ci        // ...
144e41f4b71Sopenharmony_ci      });
145e41f4b71Sopenharmony_ci      return light;
146e41f4b71Sopenharmony_ci    });
147e41f4b71Sopenharmony_ci  });
148e41f4b71Sopenharmony_ci}
149e41f4b71Sopenharmony_ci```
150e41f4b71Sopenharmony_ci
151e41f4b71Sopenharmony_ci## 相关实例
152e41f4b71Sopenharmony_ci
153e41f4b71Sopenharmony_ci对于模型、相机和光源更加综合的使用可以参考以下实例:
154e41f4b71Sopenharmony_ci- [3D引擎接口示例(ArkTS)(API12)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Graphics/Graphics3d) 
155e41f4b71Sopenharmony_ci
156