1# 创建并使用材质资源 2<!--Kit: ArkGraphics 3D--> 3<!--Subsystem: Graphics--> 4<!--Owner: @zzhao0--> 5<!--Designer: @zdustc--> 6<!--Tester: @zhangyue283--> 7<!--Adviser: @ge-yafang--> 8 9材质(Material):材质是用于定义物体表面视觉效果的重要资源。材质决定了物体如何与光线交互,从而影响其最终的渲染效果,如颜色、金属感、粗糙度等外观属性。 10 11ArkGraphics 3D采用基于物理的渲染(PBR, Physically-Based Rendering)模型,其材质实现遵循通用的PBR原理。开发者既可以使用标准材质快速实现真实感效果,也可以通过自定义Shader材质灵活控制渲染逻辑。 12 13## 基本概念 14 15着色器(Shader):着色器是GPU上可以执行的一段程序,可以控制GPU执行哪些并行计算操作。AGP引擎提供的默认着色器实现了PBR渲染,开发者只需要指定对应的参数就可以完成不同的PBR渲染。 16 17ArkGraphics 3D支持开发者创建自定义的着色器,开发者可以通过自定义着色器自定义渲染计算过程,完全控制渲染计算流程,比如控制某物体不受某光源的影响、自定义边缘描边、高亮效果等个性化视觉呈现。 18 19着色器通常配合MaterialType.SHADER材质使用,是实现个性化渲染的重要手段。其创建依赖名称及沙箱路径,创建后可绑定至材质上,替代默认的渲染行为。 20 21## 材质类型(MaterialType) 22ArkGraphics 3D中的材质类型通过[MaterialType](../reference/apis-arkgraphics3d/js-apis-inner-scene-resources.md#materialtype)枚举指定,目前支持以下两种类型: 23 24- MaterialType.SHADER:基于Shader(着色器)的材质类型,支持绑定自定义着色器,开发者可通过自定义渲染程序实现个性化的视觉表现,适用于高级图形渲染需求。 25 26- MaterialType.METALLIC_ROUGHNESS:基于金属-粗糙度模型的标准PBR材质类型,符合glTF材质规范,适合快速构建真实感渲染效果,支持设置基础色、金属度、粗糙度、法线贴图等常见属性。 27 28材质的创建一般通过[SceneResourceFactory.createMaterial()](../reference/apis-arkgraphics3d/js-apis-inner-scene.md#creatematerial)方法完成,需指定材质名称和类型。不同类型的材质支持不同的参数配置,开发者可按需选择以实现预期的渲染效果。 29 30 31## 材质的属性 32材质的属性定义了其在渲染时的视觉表现行为,例如颜色、金属感、粗糙度、光照响应以及透明度控制等。通过设置这些属性,开发者可以精准控制物体在场景中的外观效果。 33 34在ArkGraphics 3D中,材质属性设计既提供了统一的基础能力,也根据材质类型提供了差异化扩展,满足从基础场景搭建到高级视觉定制的多样需求。 35 36 37### 通用属性 38所有材质均具备以下基础属性(如materialType、shadowReceiver、blend等)可通过设置[Material](../reference/apis-arkgraphics3d/js-apis-inner-scene-resources.md#material)类型对象实现,用于控制材质的类型及其渲染基础行为: 39 40- materialType:材质类型,标识该材质是标准PBR材质还是基于Shader的自定义材质。 41 42 适用场景:当需要快速使用预设真实感材质时选择PBR材质,需实现个性化或特殊渲染效果时选择Shader材质。 43 44- shadowReceiver:材质是否可以接收场景中的阴影投射,true表示可以接收,false表示不能接收,默认为false。 45 46 适用场景:需要表现阴影效果的物体,如地面、墙体、角色等开启;纯发光物体或不参与阴影计算的可关闭以优化性能。 47 48- cullMode:剔除模式,决定是否剔除背面几何体,默认值为BACK,即剔除背面。 49 50 适用场景:普通实体模型一般开启剔除背面提升渲染效率;透明或双面材质(如树叶、布料)需要禁用剔除以显示完整模型。 51 52- blend:是否启用材质的透明效果模式。true表示开启透明,false表示关闭透明,默认值为false。 53 54 适用场景:表现透明或半透明材质时开启,如玻璃、水面、烟雾、透明塑料等。 55 56- alphaCutoff:透明度阈值,取值范围[0,1],默认值为1。像素的alpha值低于该阈值时不进行渲染,用于实现透明裁剪效果。 57 58 适用场景:需要硬透明裁剪的材质,比如树叶、铁丝网、布料边缘等带有透明区域但无半透明渐变的模型。通过设置阈值,可以快速裁剪掉透明部分像素。 59 60- renderSort:渲染排序设置,用于控制材质在渲染队列中的渲染顺序,确保透明或特殊效果材质正确叠加显示。 61 62 适用场景:多重透明材质、叠加特效、UI元素等需要严格渲染顺序的场景。 63 64### PBR材质属性 65符合glTF标准的基于物理渲染(PBR)的金属-粗糙度材质,通过设置[MetallicRoughnessMaterial](../reference/apis-arkgraphics3d/js-apis-inner-scene-resources.md#metallicroughnessmaterial20)实现,其中各项属性采用[MaterialProperty](../reference/apis-arkgraphics3d/js-apis-inner-scene-resources.md#materialproperty20)类型封装,支持绑定纹理和设置因子(factor)值。具体属性包括: 66 67- baseColor:基础颜色和透明度,包含纹理及对应因子,用于定义材质表面的主色调。 68 69 适用场景:所有材质的基本颜色设置,适用于任何物体表面,尤其是需要表现颜色和透明度的模型。 70 71- normal:法线贴图,用于模拟表面细节凹凸,增强光照效果的真实感。 72 73 适用场景:需要表现表面细节如石材纹理、皮肤毛孔、木纹凹凸等,提升真实感的模型。 74 75- material:金属度、粗糙度与反射率参数,描述材质表面的光学特性。 76 77 适用场景:区分金属和非金属表面,控制材质的光滑或粗糙程度,常用于金属件、塑料、橡胶等多种材质。 78 79- ambientOcclusion:环境光遮蔽贴图,提升材质细节处的阴影层次和真实感。 80 81 适用场景:增强模型细节阴影效果,适合复杂结构或细节丰富的物体,如建筑物裂缝、机械零件缝隙。 82 83- emissive:自发光颜色及纹理,表达材质自发光的效果。 84 85 适用场景:灯光、屏幕、发光标志、荧光材料等需要表现光源或自发光效果的材质。 86 87- clearCoat:清漆层强度,模拟车漆等具有透明反光层的材质。 88 89 适用场景:汽车车身、家具表面等有光泽涂层的材质,表现透明光泽和反射。 90 91- clearCoatRoughness:清漆层的粗糙度,控制清漆层的反光细节。 92 93 适用场景:配合clearCoat使用,调节涂层表面的光滑度或粗糙感。 94 95- clearCoatNormal:清漆层法线贴图,增强清漆层的光照变化。 96 97 适用场景:清漆层带有细节纹理时使用,增加涂层的真实光照反馈。 98 99- sheen:微纤维漫反射层,用于表现布料、织物的光泽感。 100 101 适用场景:衣物、窗帘、沙发等纺织品,表现柔和的光泽和细腻质感。 102 103- specular:镜面反射属性,控制非金属材质的高光反射强度。 104 105 适用场景:玻璃、水面、塑料等非金属材质的高光表现,增强材质的镜面反射效果。 106 107 108## 开发步骤 109 1. 获取资源工厂。 110 111 使用场景对象Scene或其他接口,获取资源工厂SceneResourceFactory实例,用于创建材质资源。 112 113 ```ts 114 // 加载场景资源,支持.gltf和.glb格式,路径和文件名可根据项目实际资源自定义 115 let scenePromise: Promise<Scene> = Scene.load($rawfile("gltf/ExampleModel.glb")); 116 scenePromise.then(async (scene: Scene) => { 117 if (!scene.root) { 118 return; 119 } 120 let rf: SceneResourceFactory = scene.getResourceFactory(); 121 // 后续操作可基于rf进行 122 }); 123 ``` 124 125 2. 创建材质并指定材质类型。 126 127 调用createMaterial()接口,指定材质类型(如MaterialType.METALLIC_ROUGHNESS或MaterialType.SHADER),异步创建材质对象。 128 129 - 可创建基于Shader的材质,当开发者需要自定义渲染效果时,可通过自定义Shader实现。示例代码如下: 130 131 ```ts 132 let shaderMaterialPromise = await rf.createMaterial({ name: "shaderMat" }, MaterialType.SHADER); 133 let shaderMat = shaderMaterialPromise as ShaderMaterial; 134 // 绑定自定义Shader代码示例(创建shader资源,路径和文件名可根据项目实际资源自定义) 135 let shader = await rf.createShader({ name: "MyShader", uri: $rawfile("shaders/my_shader.shader") }); 136 shaderMat.colorShader = shader; 137 ``` 138 139 - 也可创建基于PBR的材质,当开发者需要快速构建真实感渲染效果时,可使用预设的参数快速实现。示例代码如下: 140 141 ```ts 142 let pbrMaterialPromise = await rf.createMaterial({ name: "pbrMat" }, MaterialType.METALLIC_ROUGHNESS); 143 let pbrMat = pbrMaterialPromise as MetallicRoughnessMaterial; 144 // 设置基础颜色贴图和因子 145 // 加载图片资源,路径和文件名可根据项目实际资源自定义,但PBR材质贴图类型必须与材质属性匹配 146 let baseColorImage = await rf.createImage({ name: "baseColorTex", uri: $rawfile("textures/baseColor.png") }); 147 pbrMat.baseColor.image = baseColorImage; 148 pbrMat.baseColor.factor = { x: 1, y: 1, z: 1, w: 1 }; 149 ``` 150 151 3. 设置材质属性。 152 153 - 设置材质通用属性。示例代码如下: 154 155 ```ts 156 material.shadowReceiver = true; // 启用阴影接收 157 material.cullMode = CullMode.BACK; // 剔除背面 158 material.blend = { enabled: true }; // 启用透明混合 159 material.alphaCutoff = 0.5; // 透明裁剪阈值 160 material.renderSort = { renderSortLayer: 32, renderSortLayerOrder: 1 }; // 渲染排序 161 ``` 162 163 - 当创建的材质类型为PBR材质时,还可以设置PBR材质属性。示例代码如下: 164 165 不同属性的贴图内容通常不同,需分别创建;如多个材质共用相同贴图,可复用同一个Image实例以节省内存。所有材质属性中的factor各分量取值范围均为[0, 1]。 166 167 ```ts 168 // 图片路径和文件名可根据项目实际资源自定义,但PBR材质贴图类型必须与材质属性匹配 169 // 设置基础颜色贴图和颜色因子(支持透明通道) 170 let baseColorImage = await rf.createImage({ name: "baseColorTex", uri: $rawfile("textures/baseColor.png") }); 171 pbrMat.baseColor.image = baseColorImage; 172 pbrMat.baseColor.factor = { x: 1, y: 1, z: 1, w: 1 }; // xyz分量控制颜色,w为透明度(0~1) 173 // 设置法线贴图(控制细节凹凸感) 174 let normalImage = await rf.createImage({ name: "normalTex", uri: $rawfile("textures/normal.png") }); 175 pbrMat.normal.image = normalImage; 176 pbrMat.normal.factor.x = 0.6; // x分量:法线强度(0~1) 177 // 设置金属度、粗糙度和反射率属性(可共享一张贴图),通常使用y, z, w分量分别控制 178 let metallicRoughnessImage = await rf.createImage({ name: "materialTex", uri: $rawfile("textures/material_texture.png") }); 179 pbrMat.material.image = metallicRoughnessImage; 180 pbrMat.material.factor.y = 1.0; // y 分量:粗糙度(Roughness, 0~1),0表示光滑,1表示粗糙 181 pbrMat.material.factor.z = 0.5; // z 分量:金属度(Metallic, 0~1),0表示非金属,1表示金属 182 pbrMat.material.factor.w = 0.5; // w 分量:反射率(Specular, 0~1) 183 // 设置环境光遮蔽贴图,常用x分量控制遮蔽强度 184 let ambientOcclusionImage = await rf.createImage({ name: "ambientOcclusionTex", uri: $rawfile("textures/ambientOcclusion.png") }); 185 pbrMat.ambientOcclusion.image = ambientOcclusionImage; 186 pbrMat.ambientOcclusion.factor.x = 1.0; // 遮蔽强度(0~1) 187 // 设置自发光贴图和颜色因子(用于夜光效果) 188 let emissiveImage = await rf.createImage({ name: "emissiveTex", uri: $rawfile("textures/emissive.png") }); 189 pbrMat.emissive.image = emissiveImage; 190 pbrMat.emissive.factor = { x: 1.0, y: 1.0, z: 0.8, w: 1 }; // xyz分量控制发光颜色,w为强度(0~1) 191 // 设置clearCoat强度与粗糙度 192 pbrMat.clearCoat.factor.x = 1.0; // x分量:clearCoat强度(0~1) 193 pbrMat.clearCoatRoughness.factor.y = 0.5; // y分量:clearCoat粗糙度(0~1) 194 // 设置clearCoat法线贴图,常用x,y,z控制方向 195 let clearCoatNormalImage = await rf.createImage({ name: "clearCoatNormalTex", uri: $rawfile("textures/clearCoatNormal.png") }); 196 pbrMat.clearCoatNormal.image = clearCoatNormalImage; 197 pbrMat.clearCoatNormal.factor.x = 1.0; 198 pbrMat.clearCoatNormal.factor.y = 1.0; 199 pbrMat.clearCoatNormal.factor.z = 1.0; 200 // 更多PBR属性可按需设置,如sheen、specular等。 201 // sheen:控制光泽强度 202 // pbrMat.sheen.factor = { x: 1.0, y: 0.5, z: 0.3, w: 1.0 }; // xyz控制光泽的颜色,w为强度(0~1) 203 204 // specular:控制高光 205 // pbrMat.specular.factor = { x: 1.0, y: 1.0, z: 1.0, w: 0.7 }; // xyz为高光颜色,w为高光强度(0~1) 206 ``` 207 208 - 针对Shader材质,需绑定自定义Shader资源以实现个性化渲染效果。具体使用方式可参考[创建Shader材质并设置属性](#创建shader材质并设置属性)。 209 210 4. 绑定材质到网格。 211 212 将创建的材质赋值给几何体(Geometry)的material或subMeshes[i].material字段,使其生效。常见绑定方式有两种: 213 214 - 通过资源工厂新建网格和几何体,再绑定材质(适用于创建新几何体的情况)。示例代码如下: 215 216 ```ts 217 let mesh = await resourceFactory.createMesh({ name: "mesh" }, geometry); 218 let geometryInstance = await resourceFactory.createGeometry({ name: "geometry" }, mesh); 219 geometryInstance.mesh.subMeshes[0].material = pbrMat; // 绑定材质 220 ``` 221 222 - 通过scene.root.getNodeByPath直接获取已有几何节点,修改绑定材质(适用于加载场景后修改材质)。示例代码如下: 223 224 ```ts 225 let pbrNode = scene.root.getNodeByPath("path/to/node"); 226 if (pbrNode) { 227 let geometry = pbrNode as Geometry; 228 if (geometry.mesh?.subMeshes?.[0]) { 229 geometry.mesh.subMeshes[0].material = pbrMat; // 绑定材质 230 } 231 } 232 ``` 233 234 5. 渲染并观察效果。运行渲染流程,观察材质的最终视觉表现。 235 236 237## 完整代码 238 239 ### 创建Shader材质并设置属性 240 241 ```ts 242 import { Image, Shader, MaterialType, Material, ShaderMaterial, Animation, Environment, Container, SceneNodeParameters, 243 LightType, Light, Camera, SceneResourceParameters, SceneResourceFactory, Scene, Node, Geometry, 244 CullMode } from '@ohos.graphics.scene'; 245 246 function createAndApplyShaderMaterial(): Promise<void> { 247 // 加载场景资源,支持.gltf和.glb格式,路径和文件名可根据项目实际资源自定义 248 let scenePromise: Promise<Scene> = Scene.load($rawfile("gltf/CubeWithFloor/glTF/AnimatedCube.glb")); 249 return scenePromise.then(async (scene: Scene) => { 250 if (!scene.root) { 251 return; 252 } 253 // 获取资源工厂 254 let rf: SceneResourceFactory = scene.getResourceFactory(); 255 // 创建Shader材质 256 let materialParams: SceneResourceParameters = { name: "material" }; 257 let material = await rf.createMaterial(materialParams, MaterialType.SHADER); 258 let shaderMat = material as ShaderMaterial; 259 // 加载并绑定自定义Shader代码(创建shader资源,路径和文件名可根据项目实际资源自定义) 260 let shader = await rf.createShader({ 261 name: "shaderResource", 262 uri: $rawfile("shaders/custom_shader/custom_material_sample.shader") 263 }); 264 shaderMat.colorShader = shader; 265 // 设置通用属性 266 shaderMat.shadowReceiver = true; 267 shaderMat.cullMode = CullMode.BACK; 268 shaderMat.blend = { enabled: true }; // 例如启用透明混合 269 shaderMat.alphaCutoff = 0.5; 270 // 绑定材质到已有网格节点 271 let shaderNode = scene.root.getNodeByPath("rootNode_/Unnamed Node 1/AnimatedCube"); 272 if (shaderNode) { 273 let geometry = shaderNode as Geometry; 274 if (geometry.mesh?.subMeshes?.[0]) { 275 geometry.mesh.subMeshes[0].material = shaderMat; 276 } 277 } 278 // 后续执行渲染观察效果 279 }); 280 } 281 ``` 282 283 ### 创建PBR材质并设置属性 284 285 不同模型支持的PBR属性可能有所不同,设置材质前建议根据模型内容进行适配。本例使用CompareClearcoat模型作为示例,设置其支持的PBR属性,开发者可根据需要使用对应模型并设置相关属性。 286 287 ```ts 288 import { Image, Shader, MaterialType, Material, ShaderMaterial, Animation, Environment, Container, SceneNodeParameters, 289 LightType, Light, Camera, SceneResourceParameters, SceneResourceFactory, Scene, Node, MetallicRoughnessMaterial, 290 Geometry, CullMode } from '@ohos.graphics.scene'; 291 292 function createAndApplyPBRMaterial(): Promise<void> { 293 // 加载场景资源,支持.gltf和.glb格式,路径和文件名可根据项目实际资源自定义 294 // 不同模型支持的PBR属性可能有所不同,设置材质前建议根据模型内容进行适配,本例使用CompareClearcoat模型作为示例。 295 let scenePromise: Promise<Scene> = Scene.load($rawfile("CompareClearcoat.glb")); 296 return scenePromise.then(async (scene: Scene) => { 297 if (!scene.root) { 298 return; 299 } 300 // 获取资源工厂 301 let rf: SceneResourceFactory = scene.getResourceFactory(); 302 // 创建PBR材质 303 let materialParams: SceneResourceParameters = { name: "MyPBRMaterial" }; 304 let material = await rf.createMaterial(materialParams, MaterialType.METALLIC_ROUGHNESS); 305 let pbrMat = material as MetallicRoughnessMaterial; 306 307 // 加载共享的metallic-roughness贴图(可复用,节省资源),图片路径和文件名可根据项目实际资源自定义,但贴图类型必须与材质属性匹配 308 let metallicImage = await rf.createImage({ 309 name: "materialTex", 310 uri: $rawfile("gltf/DamagedHelmet/glTF/Default_metalRoughness.jpg") 311 }); 312 pbrMat.material.image = metallicImage; 313 // 配置y,z,w分量控制粗糙度、金属度、反射率 314 pbrMat.material.factor = { x: 0, y: 0.8, z: 0.4, w: 0.6 }; 315 // 配置环境光遮蔽贴图 316 pbrMat.ambientOcclusion.factor.x = 1.0; 317 // 配置clearCoat强度与粗糙度 318 pbrMat.clearCoat.factor.x = 1.0; 319 pbrMat.clearCoatRoughness.factor.y = 0.5; 320 // 配置sheen光泽属性 321 pbrMat.sheen.factor = { x: 1.0, y: 0.2, z: 0.2, w: 0.8 }; 322 323 // 设置通用属性 324 pbrMat.shadowReceiver = true; 325 pbrMat.cullMode = CullMode.BACK; 326 pbrMat.blend = { enabled: false }; 327 pbrMat.alphaCutoff = 0.5; 328 // 绑定材质到已有网格节点 329 let pbrNode = scene.root.getNodeByPath("Unnamed Node 1/GeoSphere002"); 330 if (pbrNode) { 331 let geometry = pbrNode as Geometry; 332 geometry.mesh.subMeshes[0].material = pbrMat; 333 } 334 // 运行渲染流程,观察材质的最终视觉表现 335 }); 336 } 337 ``` 338 339 340<!--RP1--> 341## 相关实例 342 343对于3D资源更加综合的使用可以参考以下实例: 344- [3D引擎接口示例(ArkTS)(API12)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Graphics/Graphics3d) 345<!--RP1End-->