Tangent Normals - ShaderMaterial

I’m currently trying to hook in the shader chunks related to tangent normal mapping into a custom shader material, which works for the most part but my normal vectors appear to still be in tangent space after being multiplied by the TBN matrix.

I’m unsure if I’m misunderstanding the purpose of this matrix multiplication, but my assumption is the vector should end up in world space. If I multiply my light vector direction by the normal matrix, then I get correct results, but I’d prefer to do it in either view or world space for convenience (and I’d like to understand what’s going on here! :slightly_smiling_face:)

Here’s the code I’ve got so far:

Vertex:

    #include <uv_pars_vertex>
    #include <normal_pars_vertex>

    uniform sampler2D heightMap;
    uniform vec3 sunDirection;
    varying vec3 vViewPosition;
    varying vec3 localPosition;

    void main() {
      #include <uv_vertex>
      #include <beginnormal_vertex>
      #include <defaultnormal_vertex>
      #include <normal_vertex>

      vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.0);
      vViewPosition = -mvPosition.xyz;

      gl_Position = projectionMatrix * mvPosition;
    }

Fragment:

   uniform vec3 sunDirection;

    #include <uv_pars_fragment>
    #include <normal_pars_fragment>
    #include <normalmap_pars_fragment>

    varying vec3 vViewPosition;
    varying vec3 localPosition;

    uniform mat3 normalMatrix;

    void main() {

      #include <normal_fragment_begin>
      #include <normal_fragment_maps>

      vec3 outputColor = vec3(1.0);

      float irradiance = max( dot( normal, sunDirection ), 0.0 );

      outputColor *= irradiance;

      gl_FragColor = vec4(outputColor, 1.0);

Shader constructor:

  constructor(params?: Ocean3Parameters) {
    super();

    // All optional params are definitely assigned for the moment
    this.normalMap = params?.normalMap;
    this.normalMapMatrix = params?.normalMap?.matrix;
    this.sunDirection = params?.sunDirection ?? new Vector3(1, 1, 1).normalize();
    this.heightMap = params?.heightMap;
    this.uniforms.sunDirection = { value: this.sunDirection };
    this.uniforms.normalMap = { value: this.normalMap };
    this.uniforms.normalMapMatrix = { value: this.normalMapMatrix };
    this.uniforms.heightMap = { value: this.heightMap };

    if (this.normalMap) {
      this.uniforms.normalScale = { value: new Vector2(1, 1) };
    }
  }

Any help is appreciated!

Okay I think the issue has been solved - the normal ends up in view space after these calculations, and my light vector needed to be converted into view space. The thing that was tripping me up before was I had tried multiplying the light direction vector by the viewMatrix, which was causing incorrect results.

I needed to drop the translation components of the viewMatrix (tested by creating a new mat3 from the viewMatrix) and use that to multiply my light direction with.