Babylon/lite 'createShaderMaterial/CreateShaderModule' fails to bind the custom uniforms struct in the fragment shader scope (WGSL parsing error)

Description:
When attempting to build a custom WGSL shader with createShaderMaterial containing an array of custom structural uniforms,
the resulting module throws a compilation crash during the fragment stage.
The framework successfully appends the uniform block types into the top of the shader files,
but it does not declare a global variable wrapper instance for that block in the fragment scope,
resulting in an unresolved value compiler error.

Steps to Reproduce:
Initialize a standard shader material using custom uniforms configurations:

const mat = createShaderMaterial({
    name: 'smat_PARAGRAPH',
    vertexSource: `@vertex fn mainVertex(input: VertexInput) -> VertexOutput { ... }`,
    fragmentSource: `
        @fragment fn mainFragment(input: VertexOutput) -> @location(0) vec4<f32> {
            // Fails regardless of whether we call 'material', 'materialUniforms', or 'smat_PARAGRAPH'
            return materialUniforms.uFrontFaceColor; 
        }
    `,
    attributes: ['position', 'normal', 'uv', 'color'],
    uniforms: [
		'world', 'worldViewProjection', 'cameraPosition',
        { name: 'uFrontFaceColor', type: 'vec4<f32>', defaultValue: [0,0,0,1] },
		..more values
    ],
	samplers: ['uDiffuseTexture', 'uDiffuseBackground'],
	
});

Error Log: Chrome:

Error while parsing WGSL: :57:25 error: unresolved value 'materialUniforms'
            baseColor = materialUniforms.uFrontFaceColor;
                        ^^^^^^^^^^^^^^^^
 - While calling [Device].CreateShaderModule([ShaderModuleDescriptor "smat_PARAGRAPH-fragment"]).

Expected Behavior:
The factory should automatically expose custom uniform objects under a uniform token binding identifier (like material or materialUniforms)
within the fragment shader program workspace.

Complete log from ‘mat’ above

{
    "name": "smat_PARAGRAPH",
    "vertexSource": "\n    struct VertexOutput {\n        @builtin(position) gl_Position: vec4<f32>,\n        @location(0) vNormalW: vec3<f32>,\n        @location(1) vUV: vec2<f32>,\n        @location(2) vFaceId: f32,\n        @location(3) vPositionW: vec3<f32>,\n    };\n\n    @vertex fn mainVertex(input: VertexInput) -> VertexOutput {\n        var output: VertexOutput;\n        let aFaceId = input.color.x; \n        output.vFaceId = aFaceId;\n        output.vUV = input.uv;\n        \n        // Always use shaderSystem for framework transformation matrices\n        let worldPosVec4 = shaderSystem.world * vec4<f32>(input.position, 1.0);\n        output.vPositionW = worldPosVec4.xyz;\n        \n        var localNormal = vec3<f32>(0.0);\n        if (aFaceId < 0.5) {\n            localNormal = normalize(vec3<f32>(input.normal.x * 0.2, input.normal.y * 0.2, -1.0));\n        } else if (aFaceId < 1.5) {\n            localNormal = normalize(vec3<f32>(input.normal.x, input.normal.y, -0.4));\n        } else if (aFaceId < 2.5) {\n            localNormal = vec3<f32>(0.0, 0.0, 1.0);\n        } else {\n            localNormal = vec3<f32>(0.0, 0.0, -1.0);\n        }\n        \n        output.vNormalW = normalize((shaderSystem.world * vec4<f32>(localNormal, 0.0)).xyz);\n        output.gl_Position = shaderSystem.worldViewProjection * worldPosVec4;\n        return output;\n    }",
    "fragmentSource": "\n    struct VertexOutput {\n        @builtin(position) gl_Position: vec4<f32>,\n        @location(0) vNormalW: vec3<f32>,\n        @location(1) vUV: vec2<f32>,\n        @location(2) vFaceId: f32,\n        @location(3) vPositionW: vec3<f32>,\n    };\n\n    @fragment fn mainFragment(input: VertexOutput) -> @location(0) vec4<f32> {\n        var baseColor = vec4<f32>(1.0);\n        var isBg = 0.0;\n        \n        // FIX: Access properties via the framework-injected materialUniforms block variable\n        if (input.vFaceId < 0.5) {\n            baseColor = materialUniforms.uFrontFaceColor;\n        } else if (input.vFaceId < 1.5) {\n            baseColor = materialUniforms.uSideWallColor;\n        } else if (input.vFaceId < 2.5) {\n            baseColor = materialUniforms.uBackFaceColor;\n        } else if (input.vFaceId < 3.5) {\n            baseColor = materialUniforms.uBackgroundColor;\n            isBg = 1.0;\n        } else {\n            baseColor = materialUniforms.uCornerColor;\n        }\n        \n        if (isBg > 0.5) {\n            if (materialUniforms.uHasBackground > 0.5) {\n                baseColor = baseColor * textureSample(uDiffuseBackground, uDiffuseBackgroundSampler, input.vUV);\n            }\n        } else {\n            if (materialUniforms.uHasTexture > 0.5) {\n                baseColor = baseColor * textureSample(uDiffuseTexture, uDiffuseTextureSampler, input.vUV);\n            }\n        }\n        \n        var finalRGB = baseColor.rgb;\n        \n        if (materialUniforms.uDisableLighting < 0.5) {\n            let lightDir = normalize(materialUniforms.uLightPos - input.vPositionW);\n            let diff = max(dot(input.vNormalW, lightDir), 0.0);\n            \n            let ambientComponent = baseColor.rgb * materialUniforms.uAmbientColor;\n            let diffuseComponent = baseColor.rgb * materialUniforms.uDiffuseColor * diff * materialUniforms.uLightColor;\n            \n            finalRGB = ambientComponent + diffuseComponent;\n            \n            if (isBg < 0.5) {\n                let viewDir = normalize(materialUniforms.uCameraPos - input.vPositionW);\n                let reflectDir = reflect(-lightDir, input.vNormalW);\n                let spec = pow(max(dot(viewDir, reflectDir), 0.0), materialUniforms.uSpecularPower);\n                finalRGB = finalRGB + (materialUniforms.uSpecularColor * spec * materialUniforms.uLightColor);\n            }\n        }\n        \n        if (input.vFaceId >= 0.5 && input.vFaceId < 1.5) {\n            finalRGB = finalRGB + materialUniforms.uEmissiveColor;\n        }\n        \n        if ((baseColor.a * materialUniforms.uAlpha) < 0.1) {\n            discard;\n        }\n        \n        if (input.vFaceId < 0.5 && materialUniforms.uEffectIntensity > 0.01) {\n            let t = materialUniforms.uTime * materialUniforms.uEffectSpeed;\n            let wave = sin(input.vPositionW.x * 2.0 + t) * cos(input.vPositionW.y * 2.0 + t);\n            let neon = vec3<f32>(0.0, 1.0, 0.8);\n            finalRGB = mix(finalRGB, neon, wave * materialUniforms.uEffectIntensity);\n        }\n        \n        return vec4<f32>(finalRGB, baseColor.a * materialUniforms.uAlpha);\n    }",
    "attributes": [
        "position",
        "normal",
        "uv",
        "color"
    ],
    "uniformDecls": [
        {
            "name": "world",
            "type": "mat4x4<f32>"
        },
        {
            "name": "worldViewProjection",
            "type": "mat4x4<f32>"
        },
        {
            "name": "cameraPosition",
            "type": "vec3<f32>"
        },
        {
            "name": "uLightPos",
            "type": "vec3<f32>",
            "defaultValue": [
                5,
                10,
                5
            ]
        },
        {
            "name": "uCameraPos",
            "type": "vec3<f32>",
            "defaultValue": [
                0,
                0,
                0
            ]
        },
        {
            "name": "uLightColor",
            "type": "vec3<f32>",
            "defaultValue": [
                1,
                1,
                1
            ]
        },
        {
            "name": "uFrontFaceColor",
            "type": "vec4<f32>",
            "defaultValue": [
                0.9607843137254902,
                0.23921568627450981,
                0.4470588235294118,
                1
            ]
        },
        {
            "name": "uSideWallColor",
            "type": "vec4<f32>",
            "defaultValue": [
                0.9607843137254902,
                0.23921568627450981,
                0.4470588235294118,
                1
            ]
        },
        {
            "name": "uBackFaceColor",
            "type": "vec4<f32>",
            "defaultValue": [
                0.9607843137254902,
                0.23921568627450981,
                0.4470588235294118,
                1
            ]
        },
        {
            "name": "uBackgroundColor",
            "type": "vec4<f32>",
            "defaultValue": [
                1,
                1,
                1,
                1
            ]
        },
        {
            "name": "uCornerColor",
            "type": "vec4<f32>",
            "defaultValue": [
                1,
                1,
                1,
                1
            ]
        },
        {
            "name": "uEmissiveColor",
            "type": "vec3<f32>",
            "defaultValue": [
                0,
                0,
                0
            ]
        },
        {
            "name": "uAmbientColor",
            "type": "vec3<f32>",
            "defaultValue": [
                1,
                1,
                0.8784313725490196
            ]
        },
        {
            "name": "uDiffuseColor",
            "type": "vec3<f32>",
            "defaultValue": [
                1,
                1,
                1
            ]
        },
        {
            "name": "uSpecularColor",
            "type": "vec3<f32>",
            "defaultValue": [
                1,
                1,
                1
            ]
        },
        {
            "name": "uSpecularPower",
            "type": "f32",
            "defaultValue": 16
        },
        {
            "name": "uAlpha",
            "type": "f32",
            "defaultValue": 1
        },
        {
            "name": "uDisableLighting",
            "type": "f32",
            "defaultValue": 0
        },
        {
            "name": "uHasTexture",
            "type": "f32",
            "defaultValue": 1
        },
        {
            "name": "uHasBackground",
            "type": "f32",
            "defaultValue": 0
        },
        {
            "name": "uTime",
            "type": "f32",
            "defaultValue": 0
        },
        {
            "name": "uEffectIntensity",
            "type": "f32",
            "defaultValue": 0
        },
        {
            "name": "uEffectSpeed",
            "type": "f32",
            "defaultValue": 1
        }
    ],
    "samplerDecls": [
        {
            "name": "uDiffuseTexture",
            "sampleType": "float"
        },
        {
            "name": "uDiffuseBackground",
            "sampleType": "float"
        }
    ],
    "storageBufferDecls": [],
    "defines": [],
    "needAlphaBlending": true,
    "blendMode": "additive",
    "transmissive": false,
    "needAlphaTesting": false,
    "backFaceCulling": false,
    "depthWrite": true,
    "depthCompare": "greater-equal",
    "depthOnlyFragment": false,
    "depthBias": 0,
    "depthBiasSlopeScale": 0,
    "_uboVersion": 0,
    "_uniformValues": {},
    "_textureSlots": {},
    "_storageBufferSlots": {},
    "_uniformVersion": 0,
    "_resourceVersion": 2,
    "_shaderDevice": {},
    "_shaderBindings": {
        "group1BGL": {},
        "systemSpec": {
            "_totalBytes": 144,
            "_offsets": {},
            "_structBody": "world: mat4x4<f32>,\nworldViewProjection: mat4x4<f32>,\ncameraPosition: vec3<f32>,"
        },
        "customSpec": {
            "_totalBytes": 224,
            "_offsets": {},
            "_structBody": "uLightPos: vec3<f32>,\nuCameraPos: vec3<f32>,\nuLightColor: vec3<f32>,\nuFrontFaceColor: vec4<f32>,\nuSideWallColor: vec4<f32>,\nuBackFaceColor: vec4<f32>,\nuBackgroundColor: vec4<f32>,\nuCornerColor: vec4<f32>,\nuEmissiveColor: vec3<f32>,\nuAmbientColor: vec3<f32>,\nuDiffuseColor: vec3<f32>,\nuSpecularColor: vec3<f32>,\nuSpecularPower: f32,\nuAlpha: f32,\nuDisableLighting: f32,\nuHasTexture: f32,\nuHasBackground: f32,\nuTime: f32,\nuEffectIntensity: f32,\nuEffectSpeed: f32,"
        },
        "vertexBuffers": [
            {
                "arrayStride": 12,
                "attributes": [
                    {
                        "shaderLocation": 0,
                        "offset": 0,
                        "format": "float32x3"
                    }
                ]
            },
            {
                "arrayStride": 12,
                "attributes": [
                    {
                        "shaderLocation": 1,
                        "offset": 0,
                        "format": "float32x3"
                    }
                ]
            },
            {
                "arrayStride": 8,
                "attributes": [
                    {
                        "shaderLocation": 2,
                        "offset": 0,
                        "format": "float32x2"
                    }
                ]
            },
            {
                "arrayStride": 16,
                "attributes": [
                    {
                        "shaderLocation": 3,
                        "offset": 0,
                        "format": "float32x4"
                    }
                ]
            }
        ],
        "pipelines": {},
        "_pipelineLayout": {}
    },
    "_shaderCustomSpec": {
        "_totalBytes": 224,
        "_offsets": {},
        "_structBody": "uLightPos: vec3<f32>,\nuCameraPos: vec3<f32>,\nuLightColor: vec3<f32>,\nuFrontFaceColor: vec4<f32>,\nuSideWallColor: vec4<f32>,\nuBackFaceColor: vec4<f32>,\nuBackgroundColor: vec4<f32>,\nuCornerColor: vec4<f32>,\nuEmissiveColor: vec3<f32>,\nuAmbientColor: vec3<f32>,\nuDiffuseColor: vec3<f32>,\nuSpecularColor: vec3<f32>,\nuSpecularPower: f32,\nuAlpha: f32,\nuDisableLighting: f32,\nuHasTexture: f32,\nuHasBackground: f32,\nuTime: f32,\nuEffectIntensity: f32,\nuEffectSpeed: f32,"
    },
    "_shaderCustomUbo": {},
    "_shaderCustomData": {},
    "_shaderCustomBytes": {
        "0": 0,
        "1": 0,
        "2": 160,
        "3": 64,
        "4": 0,
        "5": 0,
        "6": 32,
        "7": 65,
        "8": 0,
        "9": 0,
        "10": 160,
        "11": 64,
        "12": 0,
        "13": 0,
        "14": 0,
        "15": 0,
        "16": 0,
        "17": 0,
        "18": 0,
        "19": 0,
        "20": 0,
        "21": 0,
        "22": 0,
        "23": 0,
        "24": 0,
        "25": 0,
        "26": 0,
        "27": 0,
        "28": 0,
        "29": 0,
        "30": 0,
        "31": 0,
        "32": 0,
        "33": 0,
        "34": 128,
        "35": 63,
        "36": 0,
        "37": 0,
        "38": 128,
        "39": 63,
        "40": 0,
        "41": 0,
        "42": 128,
        "43": 63,
        "44": 0,
        "45": 0,
        "46": 0,
        "47": 0,
        "48": 246,
        "49": 245,
        "50": 117,
        "51": 63,
        "52": 245,
        "53": 244,
        "54": 116,
        "55": 62,
        "56": 229,
        "57": 228,
        "58": 228,
        "59": 62,
        "60": 0,
        "61": 0,
        "62": 128,
        "63": 63,
        "64": 246,
        "65": 245,
        "66": 117,
        "67": 63,
        "68": 245,
        "69": 244,
        "70": 116,
        "71": 62,
        "72": 229,
        "73": 228,
        "74": 228,
        "75": 62,
        "76": 0,
        "77": 0,
        "78": 128,
        "79": 63,
        "80": 246,
        "81": 245,
        "82": 117,
        "83": 63,
        "84": 245,
        "85": 244,
        "86": 116,
        "87": 62,
        "88": 229,
        "89": 228,
        "90": 228,
        "91": 62,
        "92": 0,
        "93": 0,
        "94": 128,
        "95": 63,
        "96": 0,
        "97": 0,
        "98": 128,
        "99": 63,
        "100": 0,
        "101": 0,
        "102": 128,
        "103": 63,
        "104": 0,
        "105": 0,
        "106": 128,
        "107": 63,
        "108": 0,
        "109": 0,
        "110": 128,
        "111": 63,
        "112": 0,
        "113": 0,
        "114": 128,
        "115": 63,
        "116": 0,
        "117": 0,
        "118": 128,
        "119": 63,
        "120": 0,
        "121": 0,
        "122": 128,
        "123": 63,
        "124": 0,
        "125": 0,
        "126": 128,
        "127": 63,
        "128": 0,
        "129": 0,
        "130": 0,
        "131": 0,
        "132": 0,
        "133": 0,
        "134": 0,
        "135": 0,
        "136": 0,
        "137": 0,
        "138": 0,
        "139": 0,
        "140": 0,
        "141": 0,
        "142": 0,
        "143": 0,
        "144": 0,
        "145": 0,
        "146": 128,
        "147": 63,
        "148": 0,
        "149": 0,
        "150": 128,
        "151": 63,
        "152": 225,
        "153": 224,
        "154": 96,
        "155": 63,
        "156": 0,
        "157": 0,
        "158": 0,
        "159": 0,
        "160": 0,
        "161": 0,
        "162": 128,
        "163": 63,
        "164": 0,
        "165": 0,
        "166": 128,
        "167": 63,
        "168": 0,
        "169": 0,
        "170": 128,
        "171": 63,
        "172": 0,
        "173": 0,
        "174": 0,
        "175": 0,
        "176": 0,
        "177": 0,
        "178": 128,
        "179": 63,
        "180": 0,
        "181": 0,
        "182": 128,
        "183": 63,
        "184": 0,
        "185": 0,
        "186": 128,
        "187": 63,
        "188": 0,
        "189": 0,
        "190": 128,
        "191": 65,
        "192": 0,
        "193": 0,
        "194": 128,
        "195": 63,
        "196": 0,
        "197": 0,
        "198": 0,
        "199": 0,
        "200": 0,
        "201": 0,
        "202": 128,
        "203": 63,
        "204": 0,
        "205": 0,
        "206": 0,
        "207": 0,
        "208": 0,
        "209": 0,
        "210": 0,
        "211": 0,
        "212": 0,
        "213": 0,
        "214": 0,
        "215": 0,
        "216": 0,
        "217": 0,
        "218": 128,
        "219": 63,
        "220": 0,
        "221": 0,
        "222": 0,
        "223": 0
    },
    "_shaderCustomVersion": 0
}
```;

Hi @Petros_Pap!

The custom UBO is bound in both shader stages, but its generated WGSL variable name is shaderUniforms , not materialUniforms :

@group(1) @binding(1)
var<uniform> shaderUniforms: ShaderUniforms;

Replace all custom uniform accesses accordingly:

baseColor = shaderUniforms.uFrontFaceColor;

System uniforms remain under shaderSystem , so you can also use:

let viewDir = normalize(shaderSystem.cameraPosition - input.vPositionW);

One unrelated correction: worldViewProjection already includes the world matrix, so the current vertex shader applies world twice. It should be:

let localPosition = vec4<f32>(input.position, 1.0);
let worldPosition = shaderSystem.world * localPosition;
output.vPositionW = worldPosition.xyz;
output.gl_Position = shaderSystem.worldViewProjection * localPosition;

The generated-name contract is documented here: Babylon-Lite/docs/lite/architecture/24-shader-material.md at master · BabylonJS/Babylon-Lite

Thank you @Deltakosh

“shaderUniforms” did the trick, i am sure i have read the 24-shader-material doc, but somehow i miss this critical information..

Thank you again!!

I have a relative question about “samplers” does anyone know how NOT apply setShaderTexture when i don’t needed?
sampling a placeholder texture when I do not need it causes unexpected color multiplication!