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.
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)
}
}
I am trying to do the same thing with ES6 modules but I can’t make it work.
Here is my code, model is loaded but I do not have the console message and the model is not using PBRCustomMaterial class. I tried several things but I can’t find what I am doing wrong.
import { SceneLoader } from '@babylonjs/core/Loading/sceneLoader';
import { GLTFLoader } from '@babylonjs/loaders/glTF/2.0/glTFLoader';
import { PBRCustomMaterial } from '@babylonjs/materials/';
//* Replace material loader
function Custom(loader) {
this.name = 'custom';
this.enabled = true;
this.createMaterial = function (context, material, babylonDrawMode) {
console.log('use custom');
const custom = new PBRCustomMaterial(`custom${material.index}`, loader.babylonScene);
return custom;
};
}
GLTFLoader.RegisterExtension('custom', (loader) => {
return new Custom(loader);
});
SceneLoader.ImportMesh('', folder, file, scene, (meshes) => {
console.log(meshes[1].material);
}, null, (scene, message) => {
console.error(`Error loading model: ${url}`, message);
});
Hello @MarianG, thanks for your answer and this playground.
This is a good idea to put the Fragment_Before_FragColor function directly in createMaterial!
My issue is not in using these functions but to make it work with ES6 modules which is not replicable in the playground, unfortunately.
Hello I am re-opening this topic because I need your help in order to go further.
I am trying to play with the material alpha of an imported model using PBRCustomMaterial.
I manage to do it on a simple plane as you can see in this playground:
But when I do the same thing with a model imported I can’t use the varying vAlbedoUV or vUV even if my model does have UVMapping: https://playground.babylonjs.com/#6T8SBT#1
Can’t you enlighten me on how to correctly use UV in PBRCustomMaterial?
Yes, this was exactly what I was looking for @bghgary!
Now to go forward, I then need to clone the material in order to be able to change it on each mesh independently. But after cloning, the onBind function is not called anymore as you can see in this playground:
But with this custom code, I only get white color: https://playground.babylonjs.com/debug.html#6T8SBT#12
I tried to add needAlphaBlending or needDepthPrePass and also tried to use Fragment_Before_FragColor instead of Fragment_Custom_Alpha but none have worked.
Hello, I’m back with new questions about using PBRCustomMaterial.
There’s a problem because of the use of the shader variable vAlbedoUV. As you can see in this playground, if I remove the albedoTexture from the material, I get the following shader error 'vAlbedoUV': undeclared identifier. First question: What other variable could I use to make the effect work even without an albedoTexture? I’m used to seeing vUV used in most BabylonJS shader examples, but it seems we can’t use it with PBRCustomMaterial. Second question: Is there somewhere in the documentation a list of BJS Shader variables created ? So that we know how to add glsl cutom code more easily?