# Help with calculating tangent/binormal in Vulkan fragment shader using GLSL

I need help understanding what am I doing wrong with transformation/calculation of tangents and binormals using GLSL. I'm using Willems' PBR demo shaders as a reference: https://github.com/SaschaWillems/Vulkan-glTF-PBR/blob/master/data/shaders/pbr.vert https://github.com/SaschaWillems/Vulkan-glTF-PBR/blob/master/data/shaders/pbr_khr.frag

I start by programmatically generating a sphere with normals and texture coordinates as seen in this image:

The "light" is directly to the left (-10, 0, 0) and both texture coordinates and normals seem to be proper as per smooth gradient and appropriately wrapped texture

However the moment I try to add binormal and tangent into the equation using the "getNormals" code from fragment shader above - it all gets messed up, lit from improper direction, not evenly and blocky:

I tried to RenderDoc that PBR demo and the only big difference I see thus far is that he uses right handed system (Z is going backwards) whereas I'm using left-handed system (Z is forward) through GLM - but I'm not sure if that's what ruins calculations?

Do note that I'm using identity matrices so the only transformation that sphere encounters is projection*view as vertex main output and identity as "world position" (worldPos) output because there are no world-level transformations happening to vertices themselves.

I then feed this worldPos together with texCoord and normal into getNormals() using the following code:

vec3 Q1 = dFdx(worldPos);
vec3 Q2 = dFdy(worldPos);
vec2 st1 = dFdx(texCoord);
vec2 st2 = dFdy(texCoord);

vec3 N = normalize(normal);
vec3 T = normalize(Q1 * st2.t - Q2 * st1.t);
vec3 B = -normalize(cross(N, T));

mat3 TBN = mat3(T, B, N);

return normalize(TBN * normal); // transform normal


and get that issue. Setting T and B to vec3(0.0) both returns the smooth shading back and the result is equal to just passing normal as return.

does it have anything to do with the coordinate system I'm using? E.g. X is right, Y is up, Z is forward, U is right, V is down?

any ideas or tips?

• Why are you multiplying the same normal used to calculate the TBN matrix by that matrix? This is not a geometrically meaningful operation. A TBN matrix is useful for transforming a tangent space normal read from a normal map texture into world space, but as shown here, the variable normal is treated as though it's already in world space. Commented Mar 10, 2023 at 17:38
• Figures, but when I'm reading normal from normal map texture - I get another weird result where triangles are improperly shaded and are visible
– krz
Commented Mar 10, 2023 at 17:49
• That's coming from the fact that you're computing your tangent vectors per fragment - so they're using partial derivatives that are constant over each triangle but discontinuous at triangle edges - instead of computing them per vertex and sending them to the GPU as vertex attributes, so all adjacent triangles agree about the tangents along their shared edges. Commented Mar 10, 2023 at 18:17
• but what I don't understand is why glTF-PBR demo that does the same - has smooth triangle shading whereas my attempts at doing the same result in visible triangle edges
– krz
Commented Mar 10, 2023 at 18:24