Creating and Using Image Resources

Image is essentially a two-dimensional buffer for storing information required for 3D rendering calculation, such as basic colors and normals.

ArkGraphics 3D provides the capability of creating image resources in PNG, JPG, and KTX formats and customizing image resources.

How to Develop

  1. Import the required modules.

    Import the core types provided by ArkGraphics 3D in the page script to create objects like scenes, cameras, materials, and images.

    import { Camera, Environment, Geometry, Image, Material, MaterialType, Scene, SceneResourceFactory,
      SceneResourceParameters, Shader, ShaderMaterial, EnvironmentBackgroundType } from '@kit.ArkGraphics3D';
    
  2. Load the scene and configure rendering parameters.

    Call Scene.load() to load model files in .glb or .gltf format, and obtain a scene object upon completion. Then, construct a SceneOptions object to specify the scene and rendering mode for rendering the scene content via Component3D.

    if (this.scene === null) {
      // Switched from .gltf to .glb; same content, different format
      Scene.load($rawfile('gltf/CubeWithFloor/glTF/AnimatedCube.glb'))
        .then(async (result: Scene) => {
          // Assign loaded scene to globalScene for unified resource creation
          globalScene = 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 + '.');
        });
    }
    
  3. Initialize the camera.

    Create a camera object, and set its enabled state and viewing position for subsequent model display.

    this.cam = await this.rf.createCamera({ 'name': 'Camera1' });
    this.cam.enabled = true;
    this.cam.position.z = 5;
    
  4. Obtain the geometry node.

    Call Scene.getNodeByPath() to obtain the geometry node of the target model, and record its original material. This enables rollback or recovery in case the material is later modified.

    this.geom = this.scene.getNodeByPath('rootNode_/Unnamed Node 1/AnimatedCube') as Geometry;
    
    // record original material
    this.originalMat = this.geom.mesh.subMeshes[0].material;
    
  5. Create an image resource and bind it to the material.

    Call SceneResourceFactory.createImage() to create an image resource, and call createMaterial() to create a shader material. Bind the image resource to the shader input property BASE_COLOR_Image to apply the texture to the model surface.

    function createImagePromise(): Promise<Image> {
      return new Promise((resolve, reject) => {
        // Ensure the scene is loaded before accessing sceneFactory
        if (globalScene) {
          let sceneFactory: SceneResourceFactory = globalScene.getResourceFactory();
    
          let sceneImageParameter: SceneResourceParameters = {
            name: 'image',
            uri: $rawfile('image/Cube_BaseColor.png')
          };
    
          let image: Promise<Image> = sceneFactory.createImage(sceneImageParameter);
          image.then((imageEntity: Image) => {
            resolve(imageEntity);
          }).catch((err: string) => {
            console.error('Image load failed: ' + err + '.');
            reject(err);
          });
        } else {
          reject('Scene is not loaded yet.');
        }
      });
    }
    
  6. Apply the image material to the model node.

    In the button click callback, call createShader() to create a shader, bind the shader to the material object, call createImagePromise() to obtain the image resource, and apply the image resource to the model geometry to replace the texture.

    Button('Replace with a Image material')
      // ...
      .onClick(async (): Promise<void> => {
        console.info('Start to replace with a material of image');
    
        if (!this.scene || !this.cam || !this.rf) {
          return;
        }
    
        // create shader
        this.shader = await this.rf.createShader({
          name: 'shaderResource',
          uri: $rawfile('shaders/custom_shader/custom_material_sample.shader')
        });
    
        // create imageMat
        this.imageMat = await this.rf.createMaterial({ name: 'imageMat' }, MaterialType.SHADER) as ShaderMaterial;
    
        // bind between shader and imageMat
        this.imageMat.colorShader = this.shader;
        let createdImage =  await createImagePromise();
        if (createdImage) {
          this.imageMat.colorShader.inputs['BASE_COLOR_Image'] = createdImage;
        }
    
        this.geom = this.scene.getNodeByPath('rootNode_/Unnamed Node 1/AnimatedCube') as Geometry;
    
        this.geom.mesh.materialOverride = undefined;
        this.geom.mesh.subMeshes[0].material = this.imageMat;
      })
    

Samples

The following sample is provided to help you better understand how to efficiently use 3D resources: