创建并使用材质资源
材质(Material):材质是用于定义物体表面视觉效果的重要资源。材质决定了物体如何与光线交互,从而影响其最终的渲染效果,如颜色、金属感、粗糙度等外观属性。
ArkGraphics 3D采用基于物理的渲染(PBR, Physically-Based Rendering)模型,其材质实现遵循通用的PBR原理。开发者既可以使用标准材质快速实现真实感效果,也可以通过自定义Shader材质灵活控制渲染逻辑。
基本概念
着色器(Shader):着色器是GPU上可以执行的一段程序,可以控制GPU执行哪些并行计算操作。AGP引擎提供的默认着色器实现了PBR渲染,开发者只需要指定对应的参数就可以完成不同的PBR渲染。
ArkGraphics 3D支持开发者创建自定义的着色器,开发者可以通过自定义着色器自定义渲染计算过程,完全控制渲染计算流程,比如控制某物体不受某光源的影响、自定义边缘描边、高亮效果等个性化视觉呈现。
着色器通常配合MaterialType.SHADER材质使用,是实现个性化渲染的重要手段。其创建依赖名称及沙箱路径,创建后可绑定至材质上,替代默认的渲染行为。
材质类型(MaterialType)
ArkGraphics 3D中的材质类型通过MaterialType枚举指定,目前支持以下两种类型:
- MaterialType.SHADER:基于Shader(着色器)的材质类型,支持绑定自定义着色器,开发者可通过自定义渲染程序实现个性化的视觉表现,适用于高级图形渲染需求。
- MaterialType.METALLIC_ROUGHNESS:基于金属-粗糙度模型的标准PBR材质类型,符合glTF材质规范,适合快速构建真实感渲染效果,支持设置基础色、金属度、粗糙度、法线贴图等常见属性。
材质的创建一般通过SceneResourceFactory.createMaterial()方法完成,需指定材质名称和类型。不同类型的材质支持不同的参数配置,开发者可按需选择以实现预期的渲染效果。
材质的属性
材质的属性定义了其在渲染时的视觉表现行为,例如颜色、金属感、粗糙度、光照响应以及透明度控制等。通过设置这些属性,开发者可以精准控制物体在场景中的外观效果。
在ArkGraphics 3D中,材质属性设计既提供了统一的基础能力,也根据材质类型提供了差异化扩展,满足从基础场景搭建到高级视觉定制的多样需求。
通用属性
所有材质均具备以下基础属性(如materialType、shadowReceiver、blend等)可通过设置Material类型对象实现,用于控制材质的类型及其渲染基础行为:
-
materialType:材质类型,标识该材质是标准PBR材质还是基于Shader的自定义材质。
适用场景:当需要快速使用预设真实感材质时选择PBR材质,需实现个性化或特殊渲染效果时选择Shader材质。
-
shadowReceiver:材质是否可以接收场景中的阴影投射,true表示可以接收,false表示不能接收,默认为false。
适用场景:需要表现阴影效果的物体,如地面、墙体、角色等开启;纯发光物体或不参与阴影计算的可关闭以优化性能。
-
cullMode:剔除模式,决定是否剔除背面几何体,默认值为BACK,即剔除背面。
适用场景:普通实体模型一般开启剔除背面提升渲染效率;透明或双面材质(如树叶、布料)需要禁用剔除以显示完整模型。
-
blend:是否启用材质的透明效果模式。true表示开启透明,false表示关闭透明,默认值为false。
适用场景:表现透明或半透明材质时开启,如玻璃、水面、烟雾、透明塑料等。
-
alphaCutoff:透明度阈值,取值范围[0,1],默认值为1。像素的alpha值低于该阈值时不进行渲染,用于实现透明裁剪效果。
适用场景:需要硬透明裁剪的材质,比如树叶、铁丝网、布料边缘等带有透明区域但无半透明渐变的模型。通过设置阈值,可以快速裁剪掉透明部分像素。
-
renderSort:渲染排序设置,用于控制材质在渲染队列中的渲染顺序,确保透明或特殊效果材质正确叠加显示。
适用场景:多重透明材质、叠加特效、UI元素等需要严格渲染顺序的场景。
PBR材质属性
符合glTF标准的基于物理渲染(PBR)的金属-粗糙度材质,通过设置MetallicRoughnessMaterial实现,其中各项属性采用MaterialProperty类型封装,支持绑定纹理和设置因子(factor)值。具体属性包括:
-
baseColor:基础颜色和透明度,包含纹理及对应因子,用于定义材质表面的主色调。
适用场景:所有材质的基本颜色设置,适用于任何物体表面,尤其是需要表现颜色和透明度的模型。
-
normal:法线贴图,用于模拟表面细节凹凸,增强光照效果的真实感。
适用场景:需要表现表面细节如石材纹理、皮肤毛孔、木纹凹凸等,提升真实感的模型。
-
material:金属度、粗糙度与反射率参数,描述材质表面的光学特性。
适用场景:区分金属和非金属表面,控制材质的光滑或粗糙程度,常用于金属件、塑料、橡胶等多种材质。
-
ambientOcclusion:环境光遮蔽贴图,提升材质细节处的阴影层次和真实感。
适用场景:增强模型细节阴影效果,适合复杂结构或细节丰富的物体,如建筑物裂缝、机械零件缝隙。
-
emissive:自发光颜色及纹理,表达材质自发光的效果。
适用场景:灯光、屏幕、发光标志、荧光材料等需要表现光源或自发光效果的材质。
-
clearCoat:清漆层强度,模拟车漆等具有透明反光层的材质。
适用场景:汽车车身、家具表面等有光泽涂层的材质,表现透明光泽和反射。
-
clearCoatRoughness:清漆层的粗糙度,控制清漆层的反光细节。
适用场景:配合clearCoat使用,调节涂层表面的光滑度或粗糙感。
-
clearCoatNormal:清漆层法线贴图,增强清漆层的光照变化。
适用场景:清漆层带有细节纹理时使用,增加涂层的真实光照反馈。
-
sheen:微纤维漫反射层,用于表现布料、织物的光泽感。
适用场景:衣物、窗帘、沙发等纺织品,表现柔和的光泽和细腻质感。
-
specular:镜面反射属性,控制非金属材质的高光反射强度。
适用场景:玻璃、水面、塑料等非金属材质的高光表现,增强材质的镜面反射效果。
创建Shader材质并设置属性
在需要自定义渲染逻辑或实现特殊视觉效果时,可以通过MaterialType.SHADER类型创建Shader材质。Shader材质支持绑定自定义的.shader文件,开发者可以在其中编写自定义的渲染计算逻辑,灵活控制模型的外观表现,实现如描边、高光、发光等个性化视觉效果。以下示例展示了Shader材质的创建与使用流程,包括场景加载、Shader资源创建与绑定,以及将Shader材质应用到目标几何体节点的过程。
-
导入相关模块。
在页面脚本中导入ArkGraphics 3D提供的核心类型,用于创建Shader材质及绑定Shader资源。
import { Camera, Environment, Geometry, Image, Material, MaterialType, Scene, SceneResourceFactory,SceneResourceParameters, Shader, ShaderMaterial, EnvironmentBackgroundType } from '@kit.ArkGraphics3D'; -
加载场景并设置渲染参数。
调用Scene.load()方法加载.glb或.gltf格式的模型文件,并在加载完成后获取Scene对象。随后构建SceneOptions对象,指定场景及渲染模式,用于后续通过Component3D将场景内容渲染到界面中。
if (this.scene === null) {// Switched from .gltf to .glb; same content, different formatScene.load($rawfile('gltf/CubeWithFloor/glTF/AnimatedCube.glb')).then(async (result: Scene) => {// Assign loaded scene to globalScene for unified resource creationglobalScene = result;this.scene = result;this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;this.rf = this.scene.getResourceFactory();// ...}).catch((error: string) => {console.error('init error: ' + error + '.');});} -
初始化相机。
创建相机对象并设置相机启用状态与观察位置,用于后续展示模型。
this.cam = await this.rf.createCamera({ 'name': 'Camera1' });this.cam.enabled = true;this.cam.position.z = 5; -
获取几何体节点。
通过Scene.getNodeByPath()方法获取目标模型的几何体(Geometry)节点,并记录其原始材质,以便在后续修改材质后可进行回退或恢复操作。
this.geom = this.scene.getNodeByPath('rootNode_/Unnamed Node 1/AnimatedCube') as Geometry;// record original materialthis.originalMat = this.geom.mesh.subMeshes[0].material; -
创建Shader材质(空白)。
调用SceneResourceFactory.createMaterial()创建Shader类型的空白材质,为后续绑定自定义Shader做准备。
function createMaterialPromise(): Promise<Material> {return new Promise((resolve, reject) => {// Ensure the scene is loaded before accessing sceneFactoryif (globalScene) {let sceneFactory: SceneResourceFactory = globalScene.getResourceFactory();let sceneMaterialParameter: SceneResourceParameters = { name: 'material' };// Create Materiallet material: Promise<Material> = sceneFactory.createMaterial(sceneMaterialParameter, MaterialType.SHADER);material.then(resolve).catch((err: string) => {console.error('Blank material create failed: ' + err);reject(err);});} else {reject('Scene is not loaded yet.');}});} -
创建并绑定Shader资源。
通过SceneResourceFactory.createShader()创建自定义着色器资源,并将其绑定到Shader材质上,实现自定义渲染逻辑。
function createShaderPromise(): Promise<Shader> {return new Promise((resolve, reject) => {// Ensure the scene is loaded before accessing sceneFactoryif (globalScene) {let sceneFactory: SceneResourceFactory = globalScene.getResourceFactory();// Create a SceneResourceParameters object and use it to create a shaderlet sceneResourceParameter: SceneResourceParameters = {name: 'shaderResource',uri: $rawfile('shaders/custom_shader/custom_material_sample.shader')};let shader: Promise<Shader> = sceneFactory.createShader(sceneResourceParameter);shader.then((shaderEntity: Shader) => {resolve(shaderEntity);}).catch((err: string) => {console.error('Shader load failed: ' + err + '.');reject(err);});} else {reject('Scene is not loaded yet.');}});} -
应用Shader材质到几何体节点。
通过按钮点击事件调用不同的函数,可在运行时动态切换模型的材质,实现从默认材质到Shader材质的过渡效果。
Button('Replace with a blank material')// ....onClick(async (): Promise<void> => {console.info('Start to replace with a blank material');if (!this.blankMat) {this.blankMat = await createMaterialPromise();}if (!this.scene || !this.rf) {return;}this.geom = this.scene.getNodeByPath('rootNode_/Unnamed Node 1/AnimatedCube') as Geometry;this.geom.mesh.materialOverride = undefined;if (this.blankMat) {this.geom.mesh.subMeshes[0].material = this.blankMat;}});Button('Replace with a Shader material')// ....onClick(async (): Promise<void> => {console.info('Start to replace with a shader material');if (!this.shader) {this.shader = await createShaderPromise();}if (!this.scene || !this.rf) {return;}if (!this.shaderMat) {let rf = this.scene.getResourceFactory();this.shaderMat = await rf.createMaterial({ name: 'shaderMat' }, MaterialType.SHADER);}if (this.shader) {this.shaderMat.colorShader = this.shader;}this.geom = this.scene.getNodeByPath('rootNode_/Unnamed Node 1/AnimatedCube') as Geometry;this.geom.mesh.materialOverride = undefined;if (this.shaderMat) {this.geom.mesh.subMeshes[0].material = this.shaderMat;}})
创建PBR材质并设置属性
在ArkGraphics 3D中,基于物理的渲染(PBR)材质允许开发者通过调整金属度、粗糙度、透明度等参数精确控制物体的外观效果,从而实现高度真实的渲染表现。由于不同模型在导出时所携带的PBR属性可能存在差异,因此在设置材质前建议根据模型内容进行适配。本示例选用CompareClearcoat模型,该模型自带清漆层(Clearcoat)相关的材质参数,适合用于演示清漆效果的调节。通过设置clearCoat与clearCoatRoughness等属性,可以直观观察清漆层的强度、光泽度与反射特性的变化。
-
导入相关模块。
在页面脚本中导入ArkGraphics 3D提供的核心类型,用于创建PBR材质及绑定贴图资源。
import { Scene, Camera, Material, Node, Image, SceneResourceFactory, Geometry, EnvironmentBackgroundType,PostProcessSettings, ToneMappingType, MetallicRoughnessMaterial, Vec4 } from '@kit.ArkGraphics3D';import {lookAt, OrbitCameraHelper } from '../common/utils'; -
加载场景资源。
调用Scene.load()方法加载.glb或.gltf格式的模型文件,并在加载完成后获取Scene对象。场景加载完成后,可以访问场景的资源工厂以创建材质和其他资源。
if (this.scene == null) {// Switched from .gltf to .glb; same content, different formatScene.load($rawfile('gltf/CompareClearcoat/CompareClearcoat.glb')).then(async (scene: Scene) => {this.scene = scene;if (!this.scene.root) {return;}let rf: SceneResourceFactory = scene.getResourceFactory();// ...});} -
获取几何体节点并预加载纹理。
通过场景的节点路径获取目标几何体节点,并提取其材质,随后预加载清漆层(Clearcoat)相关的纹理资源。
let pbrNode: Node | null | undefined = this.scene.root?.getNodeByPath('Unnamed Node 1/GeoSphere003');if (pbrNode) {this.material = (pbrNode as Geometry).mesh.subMeshes[0].material;let mrMaterial = (this.material as MetallicRoughnessMaterial);let original: Image | null = mrMaterial.clearCoat.image;const helmAlbedo: Resource = $rawfile('image/round_pattern.png');const irregularUri: Resource = $rawfile('image/irregular_pattern.png');let round: Image | null = await rf.createImage({name: 'round', uri: helmAlbedo });let irregular: Image | null = await rf.createImage({name: 'irregular', uri: irregularUri });if (original && round && irregular ) {this.textures.push(original);this.textures.push(round);this.textures.push(irregular);}} -
配置环境光照。
创建图像基础的光照(IBL)环境,配置环境贴图和辐射贴图,以实现真实的环境光照效果。
scene.environment = await rf.createEnvironment({ name: 'env' });scene.environment.backgroundType = EnvironmentBackgroundType.BACKGROUND_CUBEMAP;scene.environment.environmentImage = await rf.createImage({ name: 'cube', uri: $rawfile('Environment/quarry_02_2k_skybox.ktx') });scene.environment.radianceImage = await rf.createImage({ name: 'rad', uri: $rawfile('Environment/quarry_02_2k_radiance.ktx') });scene.environment.irradianceCoefficients =[{ x: 1.080343842506409, y: 0.936282396316528, z: 0.665518164634705 },{ x: 0.959947884082794, y: 0.828918874263763, z: 0.569704353809357 },{ x: 0.848236382007599, y: 0.715092182159424, z: 0.473145037889481 },{ x: -0.591795265674591, y: -0.501678705215454, z: -0.334018945693970 },{ x: -0.775423347949982, y: -0.655484378337860, z: -0.437325984239578 },{ x: 1.053589701652527, y: 0.887459456920624, z: 0.587381422519684 },{ x: -0.018954016268253, y: -0.014871496707201, z: -0.008891185745597 },{ x: -0.566255271434784, y: -0.476870059967041, z: -0.314557582139969 },{ x: -0.239390164613724, y: -0.200478553771973, z: -0.132790848612785 }]; -
创建相机并设置视角。
创建一个相机对象,并设置其位置和观察目标。然后启用轨道控制功能,让用户可以通过手势旋转和缩放视图。
this.cam = await rf.createCamera({ 'name': 'ClearcoatCam' });this.cam.enabled = true;lookAt(this.cam,{x:0,y:0,z:-3},{x:0,y:0,z:0},{x:0,y:1,z:0});this.sceneOpt = { scene: this.scene, modelType: ModelType.SURFACE } as SceneOptions;this.orbitCamera.SetOrbitFromEye(this.cam.position, this.scene.root.position, this.cam.rotation); -
切换清漆层纹理。
允许用户在不同的清漆纹理之间切换。通过按下按钮或触发事件来实现纹理的动态切换。
changeClearcoatTex() {if (this.textures.length > 0) {let i = ++this.textureInUse % this.textures.length;(this.material as MetallicRoughnessMaterial).clearCoat.image = this.textures[i];}} -
调整清漆层强度。
通过滑动条调整清漆层的强度。这个方法通过更新材质的clearCoat.factor属性来实现。
setClearcoat(v: number) {if (this.material) {const f: Vec4 = (this.material as MetallicRoughnessMaterial).clearCoat.factor;f.x = v / RESO;(this.material as MetallicRoughnessMaterial).clearCoat.factor = f;}} -
切换粗糙度纹理。
类似于清漆层纹理切换,用户也可以在不同的粗糙度纹理之间切换。
changeClearcoatRoughTex() {if (this.textures.length > 0) {let i = ++this.textureInUse % this.textures.length;(this.material as MetallicRoughnessMaterial).clearCoatRoughness.image = this.textures[i];}} -
调整清漆层粗糙度。
通过滑动条调整清漆层的粗糙度,同样地,这通过更新clearCoatRoughness.factor来实现。
setClearcoatRoughness(v: number) {if (this.material) {const f: Vec4 = (this.material as MetallicRoughnessMaterial).clearCoatRoughness.factor;f.y = v / RESO;(this.material as MetallicRoughnessMaterial).clearCoatRoughness.factor = f;}}