HarmonyOS APP《画伴梦工厂》开发第15篇:3D 模型渲染入门——@kit.ArkGraphics3D 初探
第2.7篇3D 模型渲染入门——kit.ArkGraphics3D 初探难度⭐⭐⭐ 高级前置知识2.5 视频播放器集成涉及源文件products/default/src/main/ets/pages/ModelViewerPage.ets前言在画伴梦工厂的绘画转动画流程中3D 模型展示是一个重要的视觉环节。HarmonyOS 提供了kit.ArkGraphics3D图形能力集让开发者可以轻松加载 glTF/GLB 格式的 3D 模型并在应用中渲染出精美的 3D 场景。本篇将首次探索 ArkGraphics3D 的核心概念——Scene场景、Camera相机、Light灯光、Node节点并一步步实现从 glTF 文件加载到 3D 场景渲染的完整流程。1. ArkGraphics3D 核心概念在动手编码之前先理清四个核心概念概念说明类比Scene场景3D 世界的容器管理所有节点、相机、灯光一个舞台Camera相机定义观察者的位置、视角范围、远近裁剪面摄影师的眼睛Light灯光为场景提供光照决定了物体的明暗和立体感舞台灯光Node节点场景中的基本元素可以是模型、空节点等舞台上的演员它们的关系是Scene 包含 Camera、Light、NodeCamera 决定用户看到什么Light 决定物体看起来什么样Node 承载具体的模型数据。2. 导入 ArkGraphics3D 模块首先从kit.ArkGraphics3D中导入需要的类型import{Camera,Color,Light,LightType,Node,Quaternion,RenderContext,RenderParameters,Scene,SceneNodeParameters,SceneResourceFactory}fromkit.ArkGraphics3D;这些类型涵盖了 3D 场景渲染的核心要素。注意SceneResourceFactory是用于创建相机和灯光的工厂类Quaternion用于处理旋转下篇文章会详细展开。3. 加载 GLB 模型Scene.load importScene加载一个标准 glTF/GLB 模型需要两步第一步从 rawfile 资源加载源场景sourceScene此时模型数据位于 sourceScene 中。第二步创建一个新的空场景scene然后通过importScene将 sourceScene 中的模型节点导入到目标场景中。privateasyncloadGlbModel():Promisevoid{try{this.status正在加载普通 GLB 模型;// 从 rawfile 加载源场景constsourceScene:SceneawaitScene.load($rawfile(assets/gltf/car.glb));// 创建空场景constscene:SceneawaitScene.load();// 将源场景中的模型导入到新场景的根节点下constpreviewRoot:Nodescene.importScene(PreviewModel,sourceScene,scene.root);previewRoot.visibletrue;// 设置模型位置和缩放previewRoot.position.x0;previewRoot.position.y0;previewRoot.position.z0;previewRoot.scale.x1;previewRoot.scale.y1;previewRoot.scale.z1;// 配置相机和灯光awaitthis.setupDebugView(scene);// 保存引用this.activeScenescene;this.modelRootpreviewRoot;// 应用初始旋转this.applyModelRotation();// 配置 SceneOptions 供 Component3D 使用constoptions:SceneOptions{scene:scene,modelType:ModelType.SURFACE};this.sceneOptionsoptions;this.status普通 GLB 模型加载成功;}catch(error){this.status普通 GLB 加载失败: this.getErrorMessage(errorasError);}}关键细节Scene.load()无参数调用创建一个空场景Scene.load($rawfile(...))从 rawfile 加载包含模型的场景。scene.importScene(name, sourceScene, parentNode)将 sourceScene 中的完整节点树导入到当前场景挂载到指定父节点下。ModelType.SURFACE表示使用表面渲染模式相对于点云、线框等。4. 设置相机和灯光SceneResourceFactory通过scene.getResourceFactory()获取工厂实例然后创建 Camera 和 Light。4.1 创建 CameraprivateasyncsetupDebugView(scene:Scene):Promisevoid{constfactory:SceneResourceFactoryscene.getResourceFactory();// 创建相机constcameraParams:SceneNodeParameters{name:DebugCamera};constcamera:Cameraawaitfactory.createCamera(cameraParams);constclearColor:Color{r:0.94,g:0.96,b:1,a:1// 浅蓝灰色背景};camera.enabledtrue;camera.fovMath.PI/4;// 45° 视场角camera.nearPlane0.1;// 近裁剪面 0.1camera.farPlane1000;// 远裁剪面 1000camera.clearColorclearColor;// 背景色camera.position.x0;camera.position.y1.2;// 略微仰视camera.position.z5;// 相机在 Z 轴正方向this.debugCameracamera;// ... 创建灯光}Camera 关键属性解析属性值说明fovMath.PI / 4垂直视场角 45°数值越大视野越宽nearPlane0.1近裁剪面小于此距离的物体不可见farPlane1000远裁剪面超出此距离的物体不可见clearColorColor渲染前的背景清除颜色position(0, 1.2, 5)相机在世界空间中的位置clearColor在这里设为浅蓝灰色(0.94, 0.96, 1.0)给场景一个柔和的背景色。4.2 创建 DIRECTIONAL 光源// 创建平行光constlightParams:SceneNodeParameters{name:DebugLight};constlight:Lightawaitfactory.createLight(lightParams,LightType.DIRECTIONAL);constlightColor:Color{r:1,g:1,b:1,a:1// 纯白光};light.enabledtrue;light.intensity8;// 光照强度light.colorlightColor;light.position.x0;light.position.y3;// 从上方照射light.position.z4;this.debugLightlight;}Light 关键属性解析属性值说明LightType.DIRECTIONAL平行光模拟太阳光所有光线平行intensity8光照强度值越大越亮color(1,1,1,1)纯白色position(0, 3, 4)光源位置对平行光而言是方向参考平行光DIRECTIONAL的特点是光线方向一致、没有衰减适合作为主光源照亮整个场景。5. Component3D 渲染 3D 场景模型加载完成后通过SceneOptions配置场景然后使用Component3D组件进行渲染// 构建 SceneOptionsconstoptions:SceneOptions{scene:scene,modelType:ModelType.SURFACE};this.sceneOptionsoptions;在 UI 中使用 Component3D// Builder// private RenderPanel() {// Stack() {// if (this.sceneOptions ! null) {// Component3D(this.sceneOptions)// .width(100%)// .height(100%)// } else {// Column() {// LoadingProgress().width(36).height(36)// Text(this.status).fontSize(14)// }// }// }// .width(90%).height(360).backgroundColor(#10131F)// .borderRadius(18).clip(true)// }Component3D是 ArkGraphics3D 提供的 ArkUI 组件传入SceneOptions即可渲染 3D 场景。加载过程中可以用LoadingProgress显示等待状态。6. requestRenderFrame 主动触发渲染当模型旋转或场景发生变化时需要主动请求渲染帧更新privaterequestRenderFrame():void{if(this.activeScenenull){return;}constparams:RenderParameters{alwaysRender:true};this.activeScene.renderFrame(params);}RenderParameters中的alwaysRender: true表示即使场景没有变化也强制渲染确保每次更新都能立即呈现。7. 模型文件与路径privatereadonlymodelPath:stringassets/gltf/car.glb;privatereadonlygsModelUri:stringOhosRawFile://assets/gltf/car.glb;modelPathrawfile 中的相对路径。gsModelUriOhosRawFile://协议 URI专用于访问 rawfile 资源。模型文件car.glb位于entry/src/main/resources/rawfile/assets/gltf/目录下。8. 完整流程总结┌─────────────────────────────────────────────────┐ │ 加载流程总览 │ ├─────────────────────────────────────────────────┤ │ │ │ Scene.load($rawfile) │ │ │ │ │ ▼ │ │ sourceScene含模型数据 │ │ │ │ │ Scene.load() → 空 scene │ │ │ │ │ scene.importScene(Preview, sourceScene, root) │ │ │ │ │ ▼ │ │ previewRoot模型节点 │ │ │ │ │ setupDebugView(scene) │ │ │ │ │ ├── factory.createCamera() │ │ └── factory.createLight(DIRECTIONAL) │ │ │ │ │ ▼ │ │ SceneOptions { scene, ModelType.SURFACE } │ │ │ │ │ ▼ │ │ Component3D(options) → 渲染 3D 场景 │ │ │ └─────────────────────────────────────────────────┘小结本篇我们完成了以下工作理解了Scene / Camera / Light / Node四者的角色与关系。使用Scene.load()加载 glTF 模型并通过importScene导入场景。使用SceneResourceFactory创建Camera并配置fov、nearPlane、farPlane、clearColor、position。使用SceneResourceFactory创建DIRECTIONAL 平行光并配置intensity、color、position。通过SceneOptionsComponent3D在 ArkUI 中渲染 3D 场景。使用renderFrame主动触发渲染更新。这是 ArkGraphics3D 的入门基础下篇将继续在本篇基础上实现手势拖拽旋转模型让 3D 模型动起来。下一篇预告第2.8篇手势交互——拖拽旋转 3D 模型