I'm implementing a normal mapping PBR shader using WebGPU, but I'm getting a strange dark spot and visual glitches. My scene consists of a cube with a brick texture which has a normal map, as well as a point light. dark spot on cube with brick texture and normal map

At certain angles, there is a cone-shaped specular highlight visible: faulty specualr highlight

Here's a video of me moving around the camera.

Here's the full shader code I'm using. Critical parts: Calculation of TBN matrix in vertex shader:

    out.t = normalize((model_transform * vec4(in.tangent.xyz, 0.0)).xyz);
    out.n = normalize((model_transform * vec4(in.normal, 0.0)).xyz);
    let bitangent = cross(in.normal, in.tangent.xyz) * in.tangent.w;
    out.b = normalize((model_transform * vec4(bitangent, 0.0)).xyz);

Transformation of normals in fragment shader:

    let tbn = mat3x3<f32>(in.t, in.b, in.n);
    var normal = textureSample(t_normal, s_normal, in.tex_coords).rgb * 2.0;
    normal = normal - vec3(1.0);
    normal = normalize(tbn * normal);

This is what the output looks like when I just return the normal variable at the end of the fragment shader instead of the color: frag shader output when just returning normal

I already tried several approaches (transforming everything to tangent space, just transforming the normals to tangent space, etc.) including approaches requiring you to invert the model matrix to no avail. What could be the reason for the glitches I'm experiencing? I suspect something's wrong with my TBN matrix.

  • 1
    \$\begingroup\$ Your normal map projection looks odd. If we say that greens are the areas looking "down", then on the blue side they are looking "left" somehow. Same with reds looking "right" yet on blue side they look "up". Try debugging on simpler textures, e.g. famous cone, pyramid, hemisphere. \$\endgroup\$
    – Kromster
    Jan 21 at 11:42

1 Answer 1


The first thing that jumps out at me is that you aren't normalizing your pixel shader inputs that represent quantities that you're expecting to be normalized. This is necessary because the interpolation that happens on those values will change their length.

However, in the case of a cube, you might get away without doing that, because all of the vertices on one face share the same normal. I think it's still worth doing though, even if it doesn't fix anything in this specific case.

Looking at the video, I saw a dark spot in the middle of the screen. It might be worth investigating what's going on there. I'd recommend using a GPU Debugger like Renderdoc.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .