PBR Changes again… This just KILLS my my whole custom PBR shaders.
I had a whole shader system built in unity… For the Babylon Toolkit. It has been working great for wuite some time. But recently … SOMEONE is making breaking changes to PBRMaterial and all the Custom shader define blocks…
I can tell what the issues, but now NONE of my Unity based shaders work as of 4.15 ALPHA.
I think these breaking changes were introduced with all this new Node Material Editor stuff… My question is then… Why would you not make a separate new material class for all this new stuff and leave what was working in PBRMaterial be.
Anyways… I need to fix whatever changed that killed the way my PBRMaterial (Which i call UniversalMaterials…)
Here is my source for one of UniversalAlbedoMaterial.ts class and it supportted custom shaders from Unity… That now no longer work… I think the text replace code for some of the define chunks no longer work
/**
* Babylon universal albedo material pro class
* @class UniversalAlbedoMaterial - All rights reserved (c) 2020 Mackey Kinard
*/
export class UniversalAlbedoMaterial extends BABYLON.PBRMaterial {
protected universalMaterial:boolean = true;
protected locals:BABYLON.UniversalShaderDefines = null;
protected terrainInfo:any = null;
private _defines: BABYLON.PBRMaterialDefines = null;
private _uniforms:string[] = [];
private _samplers:string[] = [];
private _textures: { [name: string]: BABYLON.Texture } = {};
private _vectors4: { [name: string]: BABYLON.Vector4 } = {};
private _floats: { [name: string]: number } = {};
private _isCreatedShader: boolean;
private _createdShaderName: string;
protected enableShaderChunks:boolean;
protected materialShaderChunks: BABYLON.UniversalAlbedoChunks;
protected updateShaderChunks():void {}
public constructor(name: string, scene: Scene) {
super(name, scene);
this.locals = new BABYLON.UniversalShaderDefines();
this._defines = null;
this._setupAttachAfterBind();
this.enableShaderChunks = false;
this.materialShaderChunks = new BABYLON.UniversalAlbedoChunks();
this.customShaderNameResolve = this._buildCustomShader;
this.customShaderChunkResolve();
}
public getClassName(): string {
return "UniversalAlbedoMaterial";
}
public getShaderName(): string {
return "pbr";
}
public getShaderChunk(): string {
return null;
}
public getShaderDefines(): BABYLON.PBRMaterialDefines {
return this._defines;
}
/* Shader Material Property Accessor Functions */
public getTexture(name:string): BABYLON.Texture {
return this._textures[name];
}
public getVector4(name:string): BABYLON.Vector4 {
return this._vectors4[name];
}
public getFloat(name:string): number {
return this._floats[name];
}
public setTexture(name: string, texture: BABYLON.Texture, initialize:boolean = false): BABYLON.UniversalAlbedoMaterial {
if (initialize === true) this.checkSampler(name);
this._textures[name] = texture;
return this;
}
public setVector4(name: string, value: BABYLON.Vector4, initialize:boolean = false): BABYLON.UniversalAlbedoMaterial {
if (initialize === true) this.checkUniform(name);
this._vectors4[name] = value;
return this;
}
public setFloat(name: string, value: number, initialize:boolean = false): BABYLON.UniversalAlbedoMaterial {
if (initialize === true) this.checkUniform(name);
this._floats[name] = value;
return this;
}
public checkUniform(uniformName:string): void {
if (this._uniforms.indexOf(uniformName) === -1) {
this._uniforms.push(uniformName);
this.locals.defineBoolean(uniformName.toUpperCase());
}
}
public checkSampler(samplerName:string): void {
if (this._samplers.indexOf(samplerName) === -1) {
this._samplers.push(samplerName);
this.locals.defineBoolean(samplerName.toUpperCase());
this.checkUniform(samplerName + "Infos");
this.checkUniform(samplerName + "Matrix");
}
}
/* Shader Material Base Worker Functions */
public getAnimatables(): IAnimatable[] {
const results = super.getAnimatables();
for (const name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture && texture.animations && texture.animations.length > 0) results.push(texture);
}
return results;
}
public getActiveTextures(): BaseTexture[] {
const results = super.getActiveTextures();
for (const name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture) results.push(texture);
}
return results;
}
public hasTexture(texture: BaseTexture): boolean {
if (super.hasTexture(texture)) {
return true;
}
let found:boolean = false;
for (const name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture === texture) {
found = true;
break;
}
}
return found;
}
/* Shader Material Factory Class Functions */
public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
if (forceDisposeTextures) {
for (const name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture) texture.dispose();
this._textures[name] = null;
}
}
this._textures = {};
super.dispose(forceDisposeEffect, forceDisposeTextures);
}
public clone(cloneName: string): BABYLON.UniversalAlbedoMaterial {
let name: string;
const destination = BABYLON.SerializationHelper.Clone<BABYLON.UniversalAlbedoMaterial>(() => new BABYLON.UniversalAlbedoMaterial(cloneName, this.getScene()), this);
destination._textures = {};
for (name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture) destination.setTexture(name, texture.clone(), true);
}
destination._vectors4 = {};
for (name in this._vectors4) {
destination.setVector4(name, this._vectors4[name].clone(), true);
}
destination._floats = {};
for (name in this._floats) {
destination.setFloat(name, this._floats[name], true);
}
return destination;
}
public serialize(): any {
let name: string;
const serializationObject = BABYLON.SerializationHelper.Serialize(this);
serializationObject.customType = "BABYLON.UniversalAlbedoMaterial";
serializationObject.textures = {};
for (name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture) serializationObject.textures[name] = texture.serialize();
}
serializationObject.vectors4 = {};
for (name in this._vectors4) {
serializationObject.vectors4[name] = this._vectors4[name].asArray();
}
serializationObject.floats = {};
for (name in this._floats) {
serializationObject.floats[name] = this._floats[name];
}
return serializationObject;
}
public static Parse(source: any, scene: BABYLON.Scene, rootUrl: string): BABYLON.UniversalAlbedoMaterial {
let name: string;
const material = BABYLON.SerializationHelper.Parse(() => new BABYLON.UniversalAlbedoMaterial(source.name, scene), source, scene, rootUrl);
for (name in source.textures) {
const texture:BABYLON.Texture = source.textures[name];
if (texture) material.setTexture(name, <BABYLON.Texture>Texture.Parse(texture, scene, rootUrl), true);
}
for (name in source.vectors4) {
material.setVector4(name, BABYLON.Vector4.FromArray(source.vectors4[name]), true);
}
for (name in source.floats) {
material.setFloat(name, source.floats[name], true);
}
return material;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////// Protected Worker Funtions //////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
protected customShaderChunkResolve():void {
const chunkName:string = this.getShaderChunk();
if (chunkName != null && chunkName !== "") {
const shaderChunkBase = chunkName + "ShaderChunks";
if (BABYLON.Effect.ShadersStore[shaderChunkBase] != null) {
const shaderChunks:any = BABYLON.Effect.ShadersStore[shaderChunkBase];
const vertexBegin:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "VertexBegin")) ? shaderChunks["VertexBegin"] : null;
if (vertexBegin != null && vertexBegin !== "") {
this.materialShaderChunks.Vertex_Begin = vertexBegin;
}
const vertexDefinitions:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "VertexDefinitions")) ? shaderChunks["VertexDefinitions"] : null;
if (vertexDefinitions != null && vertexDefinitions !== "") {
this.materialShaderChunks.Vertex_Definitions = vertexDefinitions;
}
const vertexMainBegin:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "VertexMainBegin")) ? shaderChunks["VertexMainBegin"] : null;
if (vertexMainBegin != null && vertexMainBegin !== "") {
this.materialShaderChunks.Vertex_MainBegin = vertexMainBegin;
}
const vertexUpdatePosition:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "VertexUpdatePosition")) ? shaderChunks["VertexUpdatePosition"] : null;
if (vertexUpdatePosition != null && vertexUpdatePosition !== "") {
this.materialShaderChunks.Vertex_Before_PositionUpdated = vertexUpdatePosition.replace("result", "positionUpdated");
}
const vertexUpdateNormal:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "VertexUpdateNormal")) ? shaderChunks["VertexUpdateNormal"] : null;
if (vertexUpdateNormal != null && vertexUpdateNormal !== "") {
this.materialShaderChunks.Vertex_Before_NormalUpdated = vertexUpdateNormal.replace("result", "normalUpdated");
}
const vertexMainEnd:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "VertexMainEnd")) ? shaderChunks["VertexMainEnd"] : null;
if (vertexMainEnd != null && vertexMainEnd !== "") {
this.materialShaderChunks.Vertex_MainEnd = vertexMainEnd;
}
const fragmentBegin:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentBegin")) ? shaderChunks["FragmentBegin"] : null;
if (fragmentBegin != null && fragmentBegin !== "") {
this.materialShaderChunks.Fragment_Begin = fragmentBegin;
}
const fragmentDefinitions:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentDefinitions")) ? shaderChunks["FragmentDefinitions"] : null;
if (fragmentDefinitions != null && fragmentDefinitions !== "") {
this.materialShaderChunks.Fragment_Definitions = fragmentDefinitions;
}
const fragmentMainBegin:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentMainBegin")) ? shaderChunks["FragmentMainBegin"] : null;
if (fragmentMainBegin != null && fragmentMainBegin !== "") {
this.materialShaderChunks.Fragment_MainBegin = fragmentMainBegin;
}
const fragmentUpdateAlbedo:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentUpdateAlbedo")) ? shaderChunks["FragmentUpdateAlbedo"] : null;
if (fragmentUpdateAlbedo != null && fragmentUpdateAlbedo !== "") {
this.materialShaderChunks.Fragment_Custom_Albedo = fragmentUpdateAlbedo.replace("result", "surfaceAlbedo");
}
const fragmentUpdateAlpha:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentUpdateAlpha")) ? shaderChunks["FragmentUpdateAlpha"] : null;
if (fragmentUpdateAlpha != null && fragmentUpdateAlpha !== "") {
this.materialShaderChunks.Fragment_Custom_Alpha = fragmentUpdateAlpha.replace("result", "alpha");
}
const fragmentMetallicRoughness:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentMetallicRoughness")) ? shaderChunks["FragmentMetallicRoughness"] : null;
if (fragmentMetallicRoughness != null && fragmentMetallicRoughness !== "") {
this.materialShaderChunks.Fragment_MetallicRoughness = fragmentMetallicRoughness;
}
const fragmentMicroSurface:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentMicroSurface")) ? shaderChunks["FragmentMicroSurface"] : null;
if (fragmentMicroSurface != null && fragmentMicroSurface !== "") {
this.materialShaderChunks.Fragment_MicroSurface = fragmentMicroSurface;
}
const fragmentBeforeLights:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentBeforeLights")) ? shaderChunks["FragmentBeforeLights"] : null;
if (fragmentBeforeLights != null && fragmentBeforeLights !== "") {
this.materialShaderChunks.Fragment_Before_Lights = fragmentBeforeLights;
}
const fragmentBeforeFog:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentBeforeFog")) ? shaderChunks["FragmentBeforeFog"] : null;
if (fragmentBeforeFog != null && fragmentBeforeFog !== "") {
this.materialShaderChunks.Fragment_Before_Fog = fragmentBeforeFog;
}
const fragmentBeforeFragColor:string = (BABYLON.Utilities.HasOwnProperty(shaderChunks, "FragmentBeforeFragColor")) ? shaderChunks["FragmentBeforeFragColor"] : null;
if (fragmentBeforeFragColor != null && fragmentBeforeFragColor !== "") {
this.materialShaderChunks.Fragment_Before_FragColor = fragmentBeforeFragColor.replace("result", "color");
}
} else {
BABYLON.Tools.Warn("Failed To Locate Shader Chunk Base: " + shaderChunkBase);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// Private Worker Funtions ///////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
private _buildCustomShader(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: BABYLON.PBRMaterialDefines) : string {
this._defines = defines;
let shaderProgram:string = this.getShaderName();
if (shaderProgram == null || shaderProgram === "") {
shaderProgram = shaderName;
}
if (shaderProgram == null || shaderProgram === "") {
shaderProgram = "pbr";
}
// Validate Property Defines
const locals:any = this.locals.getDefines();
if (locals != null && this._defines != null) {
const keys:string[] = Object.keys(locals);
if (keys != null && keys.length > 0) {
const source:any = this._defines;
for (const key of keys) {
source[key] = locals[key];
}
this._defines.rebuild();
}
}
// Validate Property Uniforms
let index:number = 0;
if (this._uniforms != null && this._uniforms.length > 0) {
for (index = 0; index < this._uniforms.length; index++) {
const uniformName = this._uniforms[index];
if (uniforms.indexOf(uniformName) === -1) {
uniforms.push(uniformName);
}
}
}
// Validate Property Samplers
index = 0;
if (this._samplers != null && this._samplers.length > 0) {
for (index = 0; index < this._samplers.length; index++) {
const samplerName:string = this._samplers[index];
if (samplers.indexOf(samplerName) === -1) {
samplers.push(samplerName);
}
}
}
// Validate Shader Chunks
return (this.enableShaderChunks === true) ? this._createShaderChunks(shaderProgram) : shaderProgram;
}
private _createShaderChunks(shaderName: string): string {
if (this._isCreatedShader) {
return this._createdShaderName;
}
this._isCreatedShader = false;
let chunkName:string = this.getShaderChunk();
if (chunkName == null || chunkName === "") {
chunkName = shaderName;
}
BABYLON.UniversalShaderDefines.ShaderIndexer++;
const name: string = (chunkName + "Custom" + BABYLON.UniversalShaderDefines.ShaderIndexer).trim();
const vertex = Effect.ShadersStore[shaderName + "VertexShader"];
const fragment = Effect.ShadersStore[shaderName + "PixelShader"];
this.updateShaderChunks();
const vertexname = name + "VertexShader"
Effect.ShadersStore[vertexname] = vertex
.replace('#define CUSTOM_VERTEX_BEGIN', (this.materialShaderChunks.Vertex_Begin ? this.materialShaderChunks.Vertex_Begin : ""))
.replace('#define CUSTOM_VERTEX_DEFINITIONS', (this.materialShaderChunks.Vertex_Definitions ? this.materialShaderChunks.Vertex_Definitions : ""))
.replace('#define CUSTOM_VERTEX_MAIN_BEGIN', (this.materialShaderChunks.Vertex_MainBegin ? this.materialShaderChunks.Vertex_MainBegin : ""))
.replace('#define CUSTOM_VERTEX_UPDATE_POSITION', (this.materialShaderChunks.Vertex_Before_PositionUpdated ? this.materialShaderChunks.Vertex_Before_PositionUpdated : ""))
.replace('#define CUSTOM_VERTEX_UPDATE_NORMAL', (this.materialShaderChunks.Vertex_Before_NormalUpdated ? this.materialShaderChunks.Vertex_Before_NormalUpdated : ""))
.replace('#define CUSTOM_VERTEX_MAIN_END', (this.materialShaderChunks.Vertex_MainEnd ? this.materialShaderChunks.Vertex_MainEnd : ""));
const fragmentname = name + "PixelShader"
Effect.ShadersStore[fragmentname] = fragment
.replace('#define CUSTOM_FRAGMENT_BEGIN', (this.materialShaderChunks.Fragment_Begin ? this.materialShaderChunks.Fragment_Begin : ""))
.replace('#define CUSTOM_FRAGMENT_DEFINITIONS', (this.materialShaderChunks.Fragment_Definitions ? this.materialShaderChunks.Fragment_Definitions : ""))
.replace('#define CUSTOM_FRAGMENT_MAIN_BEGIN', (this.materialShaderChunks.Fragment_MainBegin ? this.materialShaderChunks.Fragment_MainBegin : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_ALBEDO', (this.materialShaderChunks.Fragment_Custom_Albedo ? this.materialShaderChunks.Fragment_Custom_Albedo : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_ALPHA', (this.materialShaderChunks.Fragment_Custom_Alpha ? this.materialShaderChunks.Fragment_Custom_Alpha : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS', (this.materialShaderChunks.Fragment_MetallicRoughness ? this.materialShaderChunks.Fragment_MetallicRoughness : ""))
.replace('#define CUSTOM_FRAGMENT_UPDATE_MICROSURFACE', (this.materialShaderChunks.Fragment_MicroSurface ? this.materialShaderChunks.Fragment_MicroSurface : ""))
.replace('#define CUSTOM_FRAGMENT_BEFORE_LIGHTS', (this.materialShaderChunks.Fragment_Before_Lights ? this.materialShaderChunks.Fragment_Before_Lights : ""))
.replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', (this.materialShaderChunks.Fragment_Before_Fog ? this.materialShaderChunks.Fragment_Before_Fog : ""))
.replace('#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR', (this.materialShaderChunks.Fragment_Before_FragColor ? this.materialShaderChunks.Fragment_Before_FragColor : ""));
this._isCreatedShader = true;
this._createdShaderName = name;
return name;
}
private _attachAfterBind(mesh:BABYLON.Mesh, effect:BABYLON.Effect):void {
let name: string;
const scene:BABYLON.Scene = this.getScene();
if (scene.texturesEnabled) {
for (name in this._textures) {
const texture:BABYLON.Texture = this._textures[name];
if (texture != null) {
effect.setTexture(name, texture);
effect.setFloat2(name + "Infos", texture.coordinatesIndex, texture.level);
effect.setMatrix(name + "Matrix", texture.getTextureMatrix());
}
}
}
for (name in this._vectors4) {
effect.setVector4(name, this._vectors4[name]);
}
for (name in this._floats) {
effect.setFloat(name, this._floats[name]);
}
}
private _setupAttachAfterBind():void {
const fn_afterBind = this._afterBind;
this._afterBind = (mesh:BABYLON.Mesh, effect:BABYLON.Effect) => {
this._attachAfterBind(mesh, effect);
if (fn_afterBind) try { fn_afterBind(mesh, effect); }catch(e){};
};
}
}
BABYLON._TypeStore.RegisteredTypes["UniversalAlbedoMaterial"] = UniversalAlbedoMaterial;
If anybody sees anything that the new PBRMaterial is breaking… please let me know