I’m trying to convert my tangent space normal map to world space. Why, you might ask? Because my tangent space normal map produces seams when placed next to an adjacent mesh with a continuation of the texture. I’m not sure if converting from tangent to world would work, but I have to try.

I know that to compute the TBN matrix, you need three vectors: the tangent, bitangent, and normal, as stated in OpenGL Tutorial 13 : Normal Mapping

Three.js has a method that computes the tangent for you. It is already defined here and sets it as an attribute to be passed to the shader. This makes my life a lot easier. I can then compute the TBN matrix like this:

Vertex Shader

```
attribute vec4 tangent;
uniform sampler2D tx;
varying vec3 vn;
varying vec3 vp;
varying vec2 vUv;
varying vec4 vt;
void main() {
vUv = uv;
vp = position;
vn = normal;
vt = tangent;
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
```

Fragment Shader

```
varying vec3 vn;
varying vec3 vp;
varying vec4 vt;
varying vec2 vUv;
uniform sampler2D tx;
float light(vec3 normalMap, vec3 lightPosition, vec3 cP) {
vec3 lightDirection = normalize(lightPosition - normalMap.xyz);
vec3 viewDirection = normalize(cP - normalMap.xyz);
vec3 ambientColor = vec3(0.2, 0.2, 0.2); // Ambient light color
vec3 diffuseColor = vec3(0.2, 0.2, 0.2); // Diffuse light color
vec3 specularColor = vec3(0.2, 0.2, 0.2); // Specular light color
float shininess = 0.0; // Material shininess factor
// Ambient lighting calculation
vec3 ambient = ambientColor;
// Diffuse lighting calculation
float diffuseIntensity = max(dot(normalMap.xyz, lightDirection), 0.0);
vec3 diffuse = diffuseColor * diffuseIntensity;
// Specular lighting calculation
vec3 reflectionDirection = reflect(-lightDirection, normalMap.xyz);
float specularIntensity = pow(max(dot(reflectionDirection, viewDirection), 0.0), shininess);
vec3 specular = specularColor * specularIntensity;
// Final lighting calculation
vec3 finalColor = ambient + diffuse + specular;
return clamp(dot(normalMap.xyz, lightDirection), 0.0, 1.0) * max(max(finalColor.r, finalColor.g), finalColor.b);
}
void main() {
vec3 nMap = texture2D(tx,vUv).rgb;
vec4 tangent = vt;
vec3 bitangent = normalize(cross(tangent.rgb,vn));
mat3 TBN = mat3(tangent.rgb,bitangent,normalize(vn));
vec3 worldSpaceNormal = normalize(TBN * nMap);
//uncomment it add light
/*
vec3 lightDirection = vec3(0.,0.,100.);
vec3 cameraPosition = vec3(0.,0.,0.);
float finalColor = light(worldSpaceNormal,lightDirection,cameraPosition);
*/
gl_FragColor = vec4(vec3(worldSpaceNormal), 1.0);
}
```

The results are not as expected. If this were correct, it should look more like this image. This is the normal I’m trying to produce.