Programmatic use of materials

Materials creation

Material resources can be regarded as resource instances of shader resources (EffectAsset) in the scene.

Creator supports manually creating material resources in Assets, and also supports IMaterialInfo interface to create materials programmatically in a script module. Configurable parameters for IMaterialInfo include:

  • effectAsset/effectName:An effect asset reference that specifies which EffectAsset describes the process to use for rendering. (either effectAsset or effectName must be selected)
  • technique:Specifies which technique in the EffectAsset to use, defaults to 0.
  • defines:List of macro definitions, specifying which preprocessing macro definitions to enable, all are disabled by default.
  • states:List of pipeline state overloads, specifying which overloads are available for rendering pipeline states (depth, stencil, transparent blending, etc.), the default is consistent with the effect declaration.

Code example to create a material is as follows:

  1. const mat = new Material();
  2. mat.initialize({
  3. // Specifies the shader resource used by the material by the effect name
  4. effectName: 'pipeline/skybox',
  5. defines: {
  6. USE_RGBE_CUBEMAP: true
  7. }
  8. });

Use material

add material

Any renderer component can be accessed programmatically in the script module. The code example is as follows:

  1. // Materials for 3D objects are accessible through mesh renderer components (MeshRenderer, SkinnedMeshRenderer, SkinnedMeshBatchRenderer)
  2. let renderable = this.getComponent(MeshRenderer);
  3. // Get the material with index 0
  4. let material = renderable.getMaterial(0)
  5. // Set the material with index 0
  6. renderable.setMaterial(mat, 0);
  7. let sprite = this.node.getComponent(Sprite)
  8. // Get a custom material for a 2D renderer component
  9. let customMaterial = sprite.customMaterial;
  10. // Set custom materials for 2D renderer components
  11. sprite.customMaterial = mat;
  12. // Get and set the material of the particle renderer
  13. let particleSystem = this.getComponent(ParticleSystem);
  14. const material = particleSystem.material;
  15. particleSystem.material = material;
  16. // Set and get particle trailing material
  17. const trailMaterial = particleSystem.renderer.trailMaterial;
  18. particleSystem.renderer.trailMaterial = trailMaterial;

Notes:

  1. What is accessed here is the shared material.
  2. There are two situations in the material: shared material and material instance. Shared material cannot be batched with material instance.

Set material properties

After the material is initialized through the IMaterialInfo interface, the Uniform variable of the material can only be set through Material.setProperty. The code example is as follows:

  1. mat.setProperty("uniform name", uniformValue)

Uniform corresponds to Uniform-qualified variables declared within Shader. To learn more about Uniform please refer to:

If you need to set the value of Uniform frequently, use Pass.setUniform for better performance.

Shared material & material instance

In the renderer component, materials exist as shared materials and material instances.

  • Shared materials

    A shared material is used by multiple renderer components, and modifying the shared material affects all renderer components that use it. By default, the same material is shared among multiple renderer components.

    The code example to get the shared material is as follows:

    1. // Get the renderer component
    2. let renderableComponent = this.node.getComponent(MeshRenderer) as RenderableComponent
    3. // Get the element at index 0 in the shared material array
    4. let sharedMaterial = renderableComponent.sharedMaterial
    5. // Get an array of shared materials
    6. let sharedMaterials = renderableComponent.sharedMaterials
    7. // Get the element at index 0 in the shared material array
    8. let sharedMaterial = renderableComponent.getMaterial(0)
  • Material instance

    Material instances are used solely by a single renderer component, and modifying a material instance affects only the renderer components that use it. The material defaults to a shared material. When the shared material is modified, the engine will create a material instance according to the material, for example:

    • When the getter of RenderableComponent.getMaterialInstance or RenderableComponent.material is called, the engine will create a material instance based on the current material
    • When the setter of RenderableComponent.material is called, the engine will create a material instance based on the passed in material

      The code example is as follows:

      1. // Get the renderer component
      2. let renderableComponent = this.node.getComponent(MeshRenderer) as RenderableComponent
      3. // Get the array of material instances, if not, create it according to the current material array
      4. let materialInstances = renderableComponent.materials
      5. // Get the element with index 0 in the material instance array, if not, create it according to the current material
      6. let materialInstance = renderableComponent.material
      7. // Get the material instance, if not, create it according to the current material
      8. let materialInstance = renderableComponent.getMaterialInstance(materialIndex);

FAQ

Q:Why the DrawCall is increased after modifying the properties of the material?
A:It may be because the getMaterialInstance of the renderer component or the getter method of RenderableComponent.material is used, resulting in the generation of a new material instance, which affects the batching process.