New PBR Shader Breaking Changes

Hey guys (@Deltakosh or @sebavan or @nasimiasl) i got some custom shader code issues.

I am using a Advanced version of PBRCustomMateral i made for Unity Terrain Splatmaps
was working great for over a year. Then recently i tried running again and now i am getting undefined errors when trying to access uvOffset and normalW and TBN when trying to use my code to blend together the perturb normals from the #define CUSTOM_FRAGMENT_UPDATE_ALBEDO block. Now someone must have gone in and changed things again.

Can someone please tell me how to access the uvOffset and normalW and TBN from the custom code blocks ?

Do i have to add another #include replace pbrBlockXXXXXXXXX function like i had to do with:

fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);
fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);

Any help would be appreciated :slight_smile:

1 Like

Adding @Evgeni_Popov who might have an idea of the new names

Actually, those string replace is not the final fix we provided. You should instead do what I have described in this post:

With that in place, everything will (should) work as expected because the whole shader code (at least all the specific PBR code) will be inlined inside the main function. If that still does not work, please provide the final fragment shader code that you generate which fails and we will have a more deeper look.

1 Like

Yo @Evgeni_Popov … Im sorry, i vaguely remember this. Am i supposed to string replace the code in the processFinalCode function… I dont get it… Do you have an example ? How about the PBRCustomMaterial… Dis you change this class to work with the processFinalCode stuff ?

If i am reading you right all i need is to stick that processFinalCode snippet at the top of my _buildCustomShader… Right ???

FYI… Here is the meat and potatoes of my custom shader builder:

        private _buildCustomShader(shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: BABYLON.PBRMaterialDefines, attributes?: string[], options?: BABYLON.ICustomShaderNameResolveOptions) : string {
            options!.processFinalCode = (type: string, code: string) => {
                if (type === "vertex") {
                    return code;
                }
                const sci = new BABYLON.ShaderCodeInliner(code);
                sci.inlineToken = "#define pbr_inline";
                sci.processCode();
                return sci.code;
            };
            // Validate Property Defines
            this._defines = 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 Property Attributes
            index = 0;
            if (this._attributes != null && this._attributes.length > 0) {
                for (index = 0; index < this._attributes.length; index++) {
                    const attributeName:string = this._attributes[index];
                    if (attributes.indexOf(attributeName) === -1) {
                        attributes.push(attributeName);
                    }
                }
            }
            // Validate Custom Attributes
            index = 0;
            const customAttributes:string[] = this.getCustomAttributes();
            if (customAttributes != null && customAttributes.length > 0) {
                for (index = 0; index < customAttributes.length; index++) {
                    const customAttributeName:string = customAttributes[index];
                    if (attributes.indexOf(customAttributeName) === -1) {
                        attributes.push(customAttributeName);
                    }
                }
            }
            // Return Existing Shader Name
            if (this._createdShaderName != null && this._createdShaderName !== "") {
                return this._createdShaderName;
            }
            // Format Shader Program Name
            let shaderProgram:string = this.getShaderName();
            if (shaderProgram == null || shaderProgram === "") {
                shaderProgram = shaderName;
            }
            if (shaderProgram == null || shaderProgram === "") {
                shaderProgram = "pbr";
            }
            // Setup Shader After Bind Functions
            const fn_afterBind = this._afterBind.bind(this);
            this._afterBind = (m, e) => {
                if (!e) { return; }
                this._attachAfterBind(m, e);
                try { fn_afterBind(m, e); } catch (e) { }
            };
            // Return Created Shader Name (With Created Setup)
            this._createdShaderName = (this.enableShaderChunks === true) ? this._createShaderChunks(shaderProgram) : shaderProgram;
            return this._createdShaderName;
        }
        private _createShaderChunks(shaderName: string): string {
            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 = BABYLON.Effect.ShadersStore[shaderName + "VertexShader"];
            let fragment = BABYLON.Effect.ShadersStore[shaderName + "PixelShader"];
            fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);
            fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);
            fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);

            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 : ""));
                if (this.materialShaderChunks.Vertex_After_WorldPosComputed) {
                    BABYLON.Effect.ShadersStore[vertexname] = BABYLON.Effect.ShadersStore[vertexname].replace('#define CUSTOM_VERTEX_UPDATE_WORLDPOS', this.materialShaderChunks.Vertex_After_WorldPosComputed);
                }
            // ..
            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 : ""));
                if (this.materialShaderChunks.Fragment_Before_Fog) {
                    BABYLON.Effect.ShadersStore[fragmentname] = BABYLON.Effect.ShadersStore[fragmentname].replace('#define CUSTOM_FRAGMENT_BEFORE_FOG', this.materialShaderChunks.Fragment_Before_Fog);
                }
            // ..    
            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 && texture.isReady && texture.isReady()) {
                        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]);
            }
        }

Yo @Evgeni_Popov … SO if i am now using the processFinalCode method… Do i still need to do this ???

fragment = fragment.replace(/#include<pbrBlockAlbedoOpacity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockAlbedoOpacity"]);

fragment = fragment.replace(/#include<pbrBlockReflectivity>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockReflectivity"]);

fragment = fragment.replace(/#include<pbrBlockFinalColorComposition>/g, BABYLON.Effect.IncludesShadersStore["pbrBlockFinalColorComposition"]);

@Evgeni_Popov is off until Monday so please be patient on this one @MackeyK24 :slight_smile:

Here’s an example using the feature:

In your code, you don’t need to do any replace like in the example but instead you need to inline the code by doing:

        const sci = new BABYLON.ShaderCodeInliner(code);
        sci.inlineToken = "#define pbr_inline";
        sci.processCode();
        return sci.code;

No, you don’t need to do this anymore once you use processFinalCode.

1 Like