title: Tangent Space Normal Mapping - shader sanity check
body: <p>I'm getting some pretty freaky results from my tangent space normal mapping shader :).  In the scene I show here, the teapot and checkered walls are being shaded with my ordinary Phong-Blinn shader (obviously teapot backface cull gives it a lightly ephemeral look and feel :-) ).  I've tried to add in normal mapping to the sphere, with psychedelic results: </p>
  
  <p><img src="https://i.stack.imgur.com/LQPdx.png" alt="Incorrect Normal Mapping on Sphere"></p>
  
  <p>The light is coming from the right (just about visible as a black blob).  The normal map I'm using on the sphere looks like this:</p>
  
  <p><img src="https://i.stack.imgur.com/0zBJh.jpg" alt="Normal Map"></p>
  
  <p>I'm using AssImp to process input models, so it's calculating tangent and bi-normals for each vertex automatically for me.  </p>
  
  <p>The pixel and vertex shaders are below.  I'm not too sure what's going wrong, but it wouldn't surprise me if the tangent basis matrix is somehow wrong.  I assume I have to compute things into eye space and then transform the eye and light vectors into tangent space and that this is the correct way to go about it.  Note that the light position comes into the shader already in view space.</p>
  
  <pre><code>// Vertex Shader
  #version 420
  
  // Uniform Buffer Structures
  
  // Camera.
  layout (std140) uniform Camera
  {
      mat4 Camera_Projection; 
      mat4 Camera_View;
  };
  
  // Matrices per model.
  layout (std140) uniform Model
  {
      mat4 Model_ViewModelSpace;
      mat4 Model_ViewModelSpaceInverseTranspose;
  };
  
  // Spotlight.
  layout (std140) uniform OmniLight
  {
      float Light_Intensity;
  
      vec3 Light_Position;            // Already in view space.
      vec4 Light_Ambient_Colour;
      vec4 Light_Diffuse_Colour;
      vec4 Light_Specular_Colour;
  };
  
  // Streams (per vertex)
  layout(location = 0) in vec3 attrib_Position;
  layout(location = 1) in vec3 attrib_Normal;
  layout(location = 2) in vec3 attrib_Tangent;
  layout(location = 3) in vec3 attrib_BiNormal;
  layout(location = 4) in vec2 attrib_Texture;
  
  // Output streams (per vertex)
  out vec3 attrib_Fragment_Normal;
  out vec4 attrib_Fragment_Position;
  out vec3 attrib_Fragment_Light;
  out vec3 attrib_Fragment_Eye;
  
  // Shared.
  out vec2 varying_TextureCoord;
  
  // Main
  void main()
  {
      // Compute normal.
      attrib_Fragment_Normal = (Model_ViewModelSpaceInverseTranspose * vec4(attrib_Normal, 0.0)).xyz;
  
      // Compute position.
      vec4 position = Model_ViewModelSpace * vec4(attrib_Position, 1.0);
  
      // Generate matrix for tangent basis.
      mat3 tangentBasis = mat3(   attrib_Tangent, 
                                  attrib_BiNormal, 
                                  attrib_Normal);
  
      // Light vector.
      attrib_Fragment_Light = tangentBasis * normalize(Light_Position - position.xyz);
  
      // Eye vector.
      attrib_Fragment_Eye = tangentBasis * normalize(-position.xyz);
  
      // Return position.
      gl_Position = Camera_Projection * position;
  }
  </code></pre>
  
  <p>... and the pixel shader looks like this:</p>
  
  <pre><code>// Pixel Shader
  #version 420
  
  // Samplers
  uniform sampler2D Map_Normal;
  
  // Global Uniforms
  
  // Material.
  layout (std140) uniform Material
  {
      vec4  Material_Ambient_Colour;
      vec4  Material_Diffuse_Colour;
      vec4  Material_Specular_Colour;
      vec4  Material_Emissive_Colour;
  
      float Material_Shininess;
      float Material_Strength;
  };
  
  // Spotlight.
  layout (std140) uniform OmniLight
  {
      float Light_Intensity;
  
      vec3 Light_Position;    
      vec4 Light_Ambient_Colour;
      vec4 Light_Diffuse_Colour;
      vec4 Light_Specular_Colour;
  };
  
  // Input streams (per vertex)
  in vec3 attrib_Fragment_Normal;
  in vec3 attrib_Fragment_Position;
  in vec3 attrib_Fragment_Light;
  in vec3 attrib_Fragment_Eye;
  
  // Shared.
  in vec2 varying_TextureCoord;
  
  // Result
  out vec4 Out_Colour;
  
  // Main
  void main(void)
  {
      // Compute normals.
      vec3 N = normalize(texture(Map_Normal, varying_TextureCoord).xyz * 2.0 - 1.0);  
      vec3 L = normalize(attrib_Fragment_Light);
      vec3 V = normalize(attrib_Fragment_Eye);
      vec3 R = normalize(-reflect(L, N));
  
      // Compute products.
      float NdotL = max(0.0, dot(N, L));
      float RdotV = max(0.0, dot(R, V));
  
      // Compute final colours.
      vec4 ambient  = Light_Ambient_Colour * Material_Ambient_Colour;
      vec4 diffuse  = Light_Diffuse_Colour * Material_Diffuse_Colour * NdotL;
      vec4 specular = Light_Specular_Colour * Material_Specular_Colour * (pow(RdotV, Material_Shininess) * Material_Strength);
  
      // Final colour.
      Out_Colour = ambient + diffuse + specular;      
  }
  </code></pre>
  
  <p>Edit: 3D Studio Render of the scene (to show the UV's are OK on the sphere):</p>
  
  <p><img src="https://i.stack.imgur.com/NBSQh.png" alt="enter image description here"></p>