PBRCustomMaterial support in GLTF loader

At Babylon.js/pbrCustomMaterial.ts at master · BabylonJS/Babylon.js · GitHub it is shown how you can make a custom pbr-material. It derives from the PBRMaterial.

I want to be able to take a gltf-file I find on the internet and load it, but have the material objects be created from my custom-pbr-material instead of the standard PBRMaterial.

I access the GLTF-loader via BABYLON.SceneLoader.LoadAssetContainerAsync.

Is this possible? If so how?

I did my own little investigation and I think that it is not possible. I can register a new extension but that is not what I am after. The choice to create a PBRMaterial seems to be done in

loaders/src/glTF/2.0/glTFLoader.ts :: _createDefaultMaterial. (a private function)

My thinking is that a factory function that is used instead of the constructor for the PBRMaterial would solve my problem.

Any thoughts?

ping @bghgary

1 Like

I think my blog about this has exactly what you want?


https://www.babylonjs-playground.com/#20XT9A#4

You can override createMaterial to create whatever material you want.

4 Likes

That worked wonderfully! I had to change it into typescript but it worked (even with babylon in NPM). Beautiful.

The new material has to extend the base PBRMaterial since the loader checks the type (using instanceof) so I can’t supply my own material that has all the properties but has a separate implementation. But that is ok for me so I am super happy. Thanks!

Here is my code for reference. Note that I try to stay close to the default method whereas you wanted to do a special one so I have other attributes on the material compared to your code.

// See https://babylonjs.medium.com/extending-the-gltf-loader-in-babylon-js-588e48fb692b for more information
class CustomLoader implements GLTF2.IGLTFLoaderExtension, BABYLON.IDisposable {
  public name: string = 'custom'
  public enabled = true
  private loader: GLTF2.GLTFLoader

  constructor (loader: GLTF2.GLTFLoader) {
    this.loader = loader
  }

  // @ts-ignore
  // tslint:disable-next-line: naming-convention
  public createMaterial (context: string, material: IMaterial, babylonDrawMode: number): BABYLON.Material {
    this.loader.babylonScene._blockEntityCollection = true // Since we are loading into asset container
    const babylon_material = new MYPBRMaterial(material.name || 'material' + material.index, this.loader.babylonScene)
    this.loader.babylonScene._blockEntityCollection = false
    // Moved to mesh so user can change materials on gltf meshes: babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
    babylon_material.fillMode = babylonDrawMode
    babylon_material.enableSpecularAntiAliasing = true
    // babylonMaterial.useRadianceOverAlpha = !this._parent.transparencyAsCoverage
    // baylonMaterial.useSpecularOverAlpha = !this._parent.transparencyAsCoverage
    babylon_material.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE
    babylon_material.metallic = 1
    babylon_material.roughness = 1
    return babylon_material
  }

  public dispose () {
    // TODO: Are we supposed to dispose this.loader? Is it ours? I think not!
  }

  public static factory_function (loader: GLTF2.GLTFLoader): GLTF2.IGLTFLoaderExtension {
    return new CustomLoader(loader)
  }
}
1 Like

You are free to use your own material not derived from PBRBaseMaterial if you also override loadMaterialPropertiesAsync.

Glad it works though!