2

I'm trying to learn about normal mapping using Three.js, but I just can't seem to get it right.

Here is my code:

Javascript:

var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.fromGeometry(  new THREE.BoxGeometry(2,2,2) );


var positionAttributes = bufferGeometry.getAttribute('position');
var uvAttributes = bufferGeometry.getAttribute('uv');


var realVertices = [];
var realUvs = [];

for (var i = 0; i < positionAttributes.array.length ; i += 3){
  realVertices.push(new THREE.Vector3(positionAttributes.array[i+0], positionAttributes.array[i+1], positionAttributes.array[i+2] ));
}

for (var i = 0; i < uvAttributes.array.length; i+= 2){
  realUvs.push (new  THREE.Vector2( uvAttributes.array[i], uvAttributes.array[i+1] )   );
}

var tangents = new Float32Array(positionAttributes.array.length);
var bitangents = new Float32Array(positionAttributes.array.length);


var tangArray = [];
var bitangentArray = [];

   for (var i = 0; i < realVertices.length ; i += 3){
        var v0 = realVertices[i+0];
        var v1 = realVertices[i+1];
        var v2 = realVertices[i+2];

        var uv0 = realUvs[i+0];
        var uv1 = realUvs[i+1];
        var uv2 = realUvs[i+2]; 


        var deltaPos1 =  v1.sub(v0);
        var deltaPos2 = v2.sub(v0);

        var deltaUV1 = uv1.sub(uv0);
        var deltaUV2 = uv2.sub(uv0);

        var r = 1.0 / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
        var tangent =   deltaPos1.multiplyScalar(deltaUV2.y).sub(  deltaPos2.multiplyScalar(deltaUV1.y) ).multiplyScalar(r); //p1 * uv2.y - p2 * uv1.y
        var bitangent =  deltaPos2.multiplyScalar(deltaUV2.x).sub(  deltaPos1.multiplyScalar(deltaUV2.x) ).multiplyScalar(r);

        tangArray.push(tangent.x);
        tangArray.push(tangent.y);
        tangArray.push(tangent.z);

        tangArray.push(tangent.x);
        tangArray.push(tangent.y);
        tangArray.push(tangent.z);

        tangArray.push(tangent.x);
        tangArray.push(tangent.y);
        tangArray.push(tangent.z);

        bitangentArray.push (bitangent.x);
        bitangentArray.push (bitangent.y);
        bitangentArray.push (bitangent.z);

        bitangentArray.push (bitangent.x);
        bitangentArray.push (bitangent.y);
        bitangentArray.push (bitangent.z);

        bitangentArray.push (bitangent.x);
        bitangentArray.push (bitangent.y);
        bitangentArray.push (bitangent.z);
    } 

    for (var i = 0; i < bitangentArray.length; i++ ){
      tangents[i] =tangArray[i];
      bitangents[i] = bitangentArray[i];
    }


    bufferGeometry.addAttribute( 'tangent',  new THREE.BufferAttribute( tangents, 3 ) );
      bufferGeometry.addAttribute( 'bitangent',  new THREE.BufferAttribute( bitangents, 3 ) );

Vertex shader:

  varying vec3 L;
  varying vec3 V;

  uniform vec3 lightPosition;
  uniform vec3 eyePosition;

  varying vec2 vUv;

  attribute vec3 tangent;
  attribute vec3 bitangent;

  void main() {
     vUv = uv; //uv is built-in

     mTangent = bitangent;

     vec3 surfaceNormal = (modelViewMatrix * vec4(normal,0.0)).xyz;
      vec3 norm = normalize(surfaceNormal);


      vec3 tang = normalize((modelViewMatrix * vec4(tangent, 0.0)).xyz);
      vec3 bitang = normalize(cross(norm, tang));

      mat3 toTangentSpace = mat3(
        tang.x, bitang.x, norm.x,
        tang.y, bitang.y, norm.y,
        tang.z, bitang.z, norm.z
      );

     vec3 mvPosition = (modelViewMatrix * vec4(position,1.0)).xyz;

     L =  toTangentSpace *  (lightPosition - mvPosition) ;

     V =  toTangentSpace *  (- mvPosition);

     gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );


  }

Fragment shader:

  varying vec3 L;
      varying vec3 V;

      uniform vec3 lightColor;
      uniform sampler2D baseTexture;
      uniform sampler2D normalMap;
      varying vec2 vUv;


      void main() {

        vec4 normalMapValue = 2.0 * texture2D(normalMap, vUv, -1.0) - 1.0;

        vec3 unitNormal = normalize(normalMapValue.rgb);
        vec3 unitVectorToCamera = normalize(V);


        vec3 totalDiffuse = vec3(0.0);
        vec3 totalSpecular = vec3(0.0);

        float distance = length(L);
        vec3 unitLightVector = normalize(L);
        float nDotl = dot(unitNormal,unitLightVector);
        float brightness = max(nDotl,0.0);

        totalDiffuse = vec3( 0.5,0.5,0.5) + totalDiffuse + (brightness * lightColor);
        vec4 textureColour = texture2D(baseTexture,vUv);


        gl_FragColor=  vec4(totalDiffuse,1.0) * textureColour ;
      }

Here is the result:
screenshot

Which doesn't look very right. I've been trying to follow http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-13-normal-mapping/ but instead implementing it in three.js, but had no luck. Does anyone have any ideas?

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Anthony Tu
  • 21
  • 2
  • what doesnt look right can you be more specific? Perhaps it would be a good idea to render just the normals, or do a constant color NdotL to see how the surface changes. It's really confusing with the albedo. – pailhead Oct 07 '17 at 03:10

1 Answers1

3

You do not specify the tangent space matrix correctly. Change it to:

mat3 toTangentSpace = mat3( tang, bitang, norm );


A 4*4 matrix looks like this:

  c0  c1  c2  c3            c0  c1  c2  c3
[ Xx  Yx  Zx  Tx ]        [  0   4   8  12 ]     
[ Xy  Yy  Zy  Ty ]        [  1   5   9  13 ]     
[ Xz  Yz  Zz  Tz ]        [  2   6  10  14 ]     
[  0   0   0   1 ]        [  3   7  11  15 ]     

In GLSL the columns are addressed like this:

vec4 c0 = m44[0].xyzw;
vec4 c1 = m44[1].xyzw;
vec4 c2 = m44[2].xyzw;
vec4 c3 = m44[3].xyzw;

and a 4*4 matrix can be initialized like this:

mat4 m44 = mat4(c0, c1, c2, c3);

A 3*3 matrix looks like this:

                         x   y   z 
[ Xx  Yx  Zx ]        [  0   4   8 ]     
[ Xy  Yy  Zy ]        [  1   5   9 ]     
[ Xz  Yz  Zz ]        [  2   6  10 ]     

In GLSL the columns are addressed like this:

vec3 x = m33[0].xyz;
vec3 y = m33[1].xyz;
vec3 z = m33[2].xyz;

and a 3*3 matrix can be initialized like this:

mat3 m33 = mat3(x, y, z);


Note, the memory image of a 4*4 matrix looks like this:

[ Xx, Xy, Xz, 0, Yx, Yy, Yz, 0, Zx, Zy, Zz, 0, Tx, Ty, Tz, 1 ]

and of a 3*3 matrix like this:

[ Xx, Xy, Xz, Yx, Yy, Yz, Zx, Zy, Zz, Tx, Ty, Tz ]


See:

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • This is a great answer, but the question doesn't suit it :) `What is column-major order in a webgl matrix` would be better. Still voted up – pailhead Oct 08 '17 at 06:25