How to compute a Normals Matrix?

I have the classic simple lighting example:

precision mediump float;
varying vec3 lighting;
attribute vec3 vertex;
attribute vec3 normal;
uniform mat4 mvMatrix, pMatrix, nMatrix;
void main() {
    gl_Position = pMatrix * mvMatrix * vec4(vertex,1.0);
    vec3 ambientLight = vec3(0.6,0.6,0.6);
    vec3 lightColour = vec3(0.5,0.5,0.75);
    vec3 lightDir = vec3(0.85,0.8,0.75);
    vec3 transformed = (nMatrix * vec4(normal,1.0)).xyz;
    float directional = max(dot(transformed,lightDir),0.0);
    lighting = ambientLight + (lightColour*directional);
}

precision mediump float;
varying vec3 lighting;
void main() {
    gl_FragColor = vec4(lighting,1.0);
}

This code is from the Mozilla webGL example.

My normals seem correct (if I draw the raw normal instead of the computed colour I get a nice rainbow). However, my computed light is the same color for all points.

I think that it's my normals matrix that is wrong. I am computing it so:

pMatrix = createPerspective(60.0,canvas.offsetWidth/canvas.offsetHeight,0.1,4),
mvMatrix = createLookAt([2,2,2],[0,0,0],[0,1,0]),
nMatrix = mat4_inverse(mvMatrix);

Being webGL, I've had to make my own little matrix helpers (OpenGL uses transposed matrices):

function createPerspective(fovy,aspect,near,far) {
        var top = near*Math.tan(fovy*Math.PI/360.0);
        var right = top*aspect, left = -right, bottom = -top;
        var rl = (right-left);
        var tb = (top-bottom);
        var fn = (far-near);
        return [(near*2)/rl,    0,  0,  0,
            0,  (near*2)/tb,    0,  0,
            (right+left)/rl,    (top+bottom)/tb, -(far+near)/fn,    -1,
            0,  0,  -(far*near*2)/fn,   0];
}

function createLookAt(eye,centre,up) {
        if (eye[0] == centre[0] && eye[1] == centre[1] && eye[2] == centre[2])
                return [1, 0, 0, 0,
                    0, 1, 0, 0,
                    0, 0, 1, 0,
                    0, 0, 0, 1];
        var z0,z1,z2,x0,x1,x2,y0,y1,y2,len; 
        //vec3.direction(eye, center, z);
        z0 = eye[0] - centre[0];
        z1 = eye[1] - centre[1];
        z2 = eye[2] - centre[2];
        // normalize (no check needed for 0 because of early return)
        len = 1/Math.sqrt(z0*z0 + z1*z1 + z2*z2);
        z0 *= len;
        z1 *= len;
        z2 *= len;
        //vec3.normalize(vec3.cross(up, z, x));
        x0 = up[1]*z2 - up[2]*z1;
        x1 = up[2]*z0 - up[0]*z2;
        x2 = up[0]*z1 - up[1]*z0;
        len = Math.sqrt(x0*x0 + x1*x1 + x2*x2);
        if(len) len = 1/len; else len = 0;
    x0 *= len;
    x1 *= len;
    x2 *= len;
        //vec3.normalize(vec3.cross(z, x, y));
        y0 = z1*x2 - z2*x1;
        y1 = z2*x0 - z0*x2;
        y2 = z0*x1 - z1*x0;
        len = Math.sqrt(y0*y0 + y1*y1 + y2*y2);
        if(len) len = 1/len; else len = 0;
    y0 *= len;
    y1 *= len;
    y2 *= len;
        return [x0, y0, z0, 0,
            x1, y1, z1, 0,
            x2, y2, z2, 0,
            -(x0*eye[0] + x1*eye[1] + x2*eye[2]), -(y0*eye[0] + y1*eye[1] + y2*eye[2]), -(z0*eye[0] + z1*eye[1] + z2*eye[2]), 1];
}

function mat4_inverse(mat) {
        var a00 = mat[0], a01 = mat[1], a02 = mat[2], a03 = mat[3];
        var a10 = mat[4], a11 = mat[5], a12 = mat[6], a13 = mat[7];
        var a20 = mat[8], a21 = mat[9], a22 = mat[10], a23 = mat[11];
        var a30 = mat[12], a31 = mat[13], a32 = mat[14], a33 = mat[15];
        var b00 = a00*a11 - a01*a10;
        var b01 = a00*a12 - a02*a10;
        var b02 = a00*a13 - a03*a10;
        var b03 = a01*a12 - a02*a11;
        var b04 = a01*a13 - a03*a11;
        var b05 = a02*a13 - a03*a12;
        var b06 = a20*a31 - a21*a30;
        var b07 = a20*a32 - a22*a30;
        var b08 = a20*a33 - a23*a30;
        var b09 = a21*a32 - a22*a31;
        var b10 = a21*a33 - a23*a31;
        var b11 = a22*a33 - a23*a32;
        var invDet = 1/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
        return [
        (a11*b11 - a12*b10 + a13*b09)*invDet,
        (-a01*b11 + a02*b10 - a03*b09)*invDet,
        (a31*b05 - a32*b04 + a33*b03)*invDet,
        (-a21*b05 + a22*b04 - a23*b03)*invDet,
        (-a10*b11 + a12*b08 - a13*b07)*invDet,
        (a00*b11 - a02*b08 + a03*b07)*invDet,
        (-a30*b05 + a32*b02 - a33*b01)*invDet,
        (a20*b05 - a22*b02 + a23*b01)*invDet,
        (a10*b10 - a11*b08 + a13*b06)*invDet,
        (-a00*b10 + a01*b08 - a03*b06)*invDet,
        (a30*b04 - a31*b02 + a33*b00)*invDet,
        (-a20*b04 + a21*b02 - a23*b00)*invDet,
        (-a10*b09 + a11*b07 - a12*b06)*invDet,
        (a00*b09 - a01*b07 + a02*b06)*invDet,
        (-a30*b03 + a31*b01 - a32*b00)*invDet,
        (a20*b03 - a21*b01 + a22*b00)*invDet];
}

I believe these matrix functions to be correct - in that I haven't had problems with them before.

So I have a nagging memory about gluLookAt matrices not being orthogonal and that messing up normals matrices but I can't find anything on Google about that particularly, or spot what fundamental error or bug I've made.

How do you compute and apply a proper normals matrix?

Answers


The normals matrix should be the inverse of the transpose of the modelview matrix, i.e.

nMatrix = mat4_inverse(mat4_transpose(mvMatrix));

Need Your Help

Open a new page in a new window, print it and closed it

javascript jquery html

I still can't get this code to work, I am trying to use the "onClick" to swap the image once clicked, open this new page in a new window, print this new opened window and closed it. It opens the ne...

How to retrieve the last autoincremented ID from a SQLite table?

database sqlite primary-key auto-increment junction-table

I have a table Messages with columns ID (primary key, autoincrement) and Content (text).

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.