Normal Map is not working at all

Dear babylonJs developers.

I am very happy to share my first post.

I am a babylonJs beginner and need your help.

I tried to create a createCustomMaterial function and apply normal map/roughness map but normal map is not working at all.

I don’t know what is causing this. Please take a look at my current code and give me some advice.
Thanks.
// Modified createCustomMaterial to preserve original material name
function createCustomMaterial(originalMaterial, scene) {
const originalName = originalMaterial.name;

        BABYLON.Effect.ShadersStore[originalName + "VertexShader"] = `
            precision highp float;
            attribute vec3 position;
            attribute vec2 uv;
            attribute vec2 uv2;
            attribute vec3 normal;
            attribute vec3 tangent;
            
            uniform mat4 world;
            uniform mat4 viewProjection;
            uniform mat4 view;
            
            varying vec2 vUV;
            varying vec2 vUV2;
            varying vec3 vNormal;
            varying vec3 vPosition;
            varying vec3 vTangent;
            varying vec3 vBitangent;
            
            void main() {
                vec4 worldPos = world * vec4(position, 1.0);
                gl_Position = viewProjection * worldPos;
                vUV = uv;
                vUV2 = uv2;
                
                vNormal = normalize(mat3(world) * normal);
                vTangent = normalize(mat3(world) * tangent);
                vBitangent = cross(vNormal, vTangent);
                vPosition = worldPos.xyz;
            }
        `;

        BABYLON.Effect.ShadersStore[originalName + "FragmentShader"] = `
            precision highp float;
            varying vec2 vUV;
            varying vec2 vUV2;
            varying vec3 vNormal;
            varying vec3 vPosition;
            varying vec3 vTangent;
            varying vec3 vBitangent;
            
            uniform vec3 baseColor;
            uniform sampler2D patternTexture;
            uniform vec3 patternTint;
            uniform sampler2D lightmapTexture;
            uniform float lightmapIntensity;
            uniform bool hasPattern;
            uniform sampler2D normalMap;
            uniform sampler2D roughnessMap;
            uniform bool hasRoughnessMap;
            uniform sampler2D specularMap;
            uniform bool hasSpecularMap;
            uniform vec3 cameraPosition;
            
            void main() {
                // Normal mapping
                vec3 normalMapColor = texture2D(normalMap, vUV).rgb;
                vec3 normalFromMap = normalize(normalMapColor * 2.0 - 1.0);
                mat3 TBN = mat3(normalize(vTangent), normalize(vBitangent), normalize(vNormal));
                vec3 normal = normalize(TBN * normalFromMap);
                
                // Base color calculation
                vec3 finalColor = baseColor;
                if (hasPattern) {
                    vec4 pattern = texture2D(patternTexture, vUV);
                    finalColor = mix(baseColor, patternTint * pattern.rgb, pattern.a);
                }
                
                // View direction for specular
                vec3 viewDir = normalize(cameraPosition - vPosition);
                vec3 reflectDir = reflect(-viewDir, normal);
                
                // Roughness and specular calculation
                float roughness = hasRoughnessMap ? texture2D(roughnessMap, vUV).r : 0.5;
                float shininess = (1.0 - roughness) * 256.0;
                
                float specularStrength = hasSpecularMap ? texture2D(specularMap, vUV).r : 1.0;
                float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);
                vec3 specularColor = vec3(specularStrength * spec);
                
                // Combine all lighting components
                finalColor = finalColor + specularColor;
                
                // Apply lightmap
                vec3 lightmap = texture2D(lightmapTexture, vUV2).rgb * lightmapIntensity;
                finalColor *= lightmap;
                
                gl_FragColor = vec4(finalColor, 1.0);
            }
        `;

        const material = new BABYLON.ShaderMaterial(originalName, scene, {
            vertex: originalName,
            fragment: originalName,
        }, {
            attributes: ["position", "normal", "uv", "uv2", "tangent"],
            uniforms: [
                "world",
                "viewProjection",
                "view",
                "baseColor",
                "patternTint",
                "lightmapIntensity",
                "hasPattern",
                "cameraPosition",
                "hasRoughnessMap",
                "hasSpecularMap"
            ],
            samplers: [
                "patternTexture",
                "lightmapTexture",
                "normalMap",
                "roughnessMap",
                "specularMap"
            ],
            defines: []
        });

        // Copy properties from original material if possible
        if (originalMaterial instanceof BABYLON.StandardMaterial) {
            const color = originalMaterial.diffuseColor || RESET_COLOR;
            material.setVector3("baseColor", new BABYLON.Vector3(color.r, color.g, color.b));

            if (originalMaterial.diffuseTexture) {
                material.setTexture("patternTexture", originalMaterial.diffuseTexture);
                material.setFloat("hasPattern", 1);
            } else {
                material.setFloat("hasPattern", 0);
            }
        } else {
            material.setVector3("baseColor", new BABYLON.Vector3(1.0, 1.0, 1.0));
            material.setFloat("hasPattern", 0);
        }

        material.setVector3("patternTint", new BABYLON.Vector3(1, 1, 1));
        material.setFloat("lightmapIntensity", 1.0);
        material.setFloat("hasRoughnessMap", 0);
        material.setFloat("hasSpecularMap", 0);

        return material;
    }



            const normalTexture = new BABYLON.Texture(
                data.texture.normal.content,
                currentScene,
                true,
                false,
                BABYLON.Texture.TRILINEAR_SAMPLINGMODE
            );
            normalTexture.wrapU = normalTexture.wrapV = BABYLON.Texture.WRAP_ADDRESSMODE;
            material.setTexture("normalMap", normalTexture);

hey there and welcome

Please provide a repro in the Playground directly: playground.babylonjs.com

2 Likes