In this article I am going to blend javascript with the HTML 5 canvas and do some old school 3-D'in! I am not going to bore you with a lot of details I just want to provide for you a simple demonstration of 3D rotation in a text book purists sense. That mean's easy to follow code for you to cut and paste into your own coding experiments. Easy to follow means non-optimized or anything clever. Just straight up 3D!
While we're kickin it old school I am going to create a sin and cos lookup table. After all we're using a scripting language and not any flavor of a compiled language like, C, C++, C#, or Objective-C. This is so 1990's but has it's applications here, the trig lookup tables.
var sin_table = new Array();
var cos_table = new Array();
// function: initialize_trig_tables
//
// description: initialize the trig
// sin and cos lookup tables
//
// arguments: none
// returns: none
function initialize_trig_tables ()
{
for(angle=0; angle<361; angle++)
{
sin_table[angle] = Math.sin(angle);
cos_table[angle] = Math.cos(angle);
}
}
We're not done with our initializations yet. We will need functions to create a null matrix and an identity matrix.
// function: makeIdentityMatrix
//
// description: create a 4x4 identity matrix
//
// 1 0 0 0
// I = 0 1 0 0
// 0 0 1 0
// 0 0 0 1
//
// arguments: none
// returns: 4x4 identity matrix
function makeIdentityMatrix ()
{
return [
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1],
];
}
// function: makeNullMatrix
//
// description: create a 4x4 identity matrix
//
// 0 0 0 0
// N = 0 0 0 0
// 0 0 0 0
// 0 0 0 0
//
// arguments: none
// returns: 4x4 NULL matrix
function makeNullMatrix ()
{
return [
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
[0,0,0,0],
];
}
Now we need some code to perform the rotations Rx, Ry, and Rz. These are the respective rotations about the axis of x,y, and z. I store the rotation and projection code in a javascript library. Just a heads up for the driver code where it's implied you have this in a library somewhere.
For rotation Rx,
// function: createRotationXMatrix
//
// Description: creates a rotation matrix
// about the x-axis
//
// 1 0 0 0
// Rx(a) = 0 cos a -sin a 0
// 0 sin a cos a 0
// 0 0 0 1
//
// arguments: angle - angle of rotation
// returns: 4x4 Rotation matrix Rx
function createRotationXMatrix (angle)
{
// create and initalize rx to identity matrix
var rx = makeIdentityMatrix ();
rx[1][1] = cos_table[angle];
rx[1][2] = sin_table[angle];
rx[2][1] = -1 * sin_table[angle];
rx[2][2] = cos_table[angle];
return rx;
}
For rotation Ry,
// function: createRotationYMatrix
//
// Description: creates a rotation matrix
// about the y-axis
//
// cos t 0 sin t 0
// Ry(t) = 0 1 0 0
// -sin t 0 cos t 0
// 0 0 0 1
//
// arguments: angle - angle of rotation
// returns: 4x4 Rotation matrix Ry
function createRotationYMatrix (angle)
{
// create and initalize rx to identity matrix
var ry = makeIdentityMatrix ();
ry[0][0] = cos_table[angle];
ry[0][2] = -1 * sin_table[angle];
ry[2][0] = sin_table[angle];
ry[2][2] = cos_table[angle];
return ry;
}
For rotation about the Z axis, Rz,
// function: createRotationZMatrix
//
// Description: creates a rotation matrix
// about the z-axis
//
// cos p -sin p 0 0
// Rz(p) = sin p cos p 0 0
// 0 0 1 0
// 0 0 0 1
//
// arguments: angle - angle of rotation
// returns: 4x4 Rotation matrix Rz
function createRotationZMatrix (angle)
{
// create and initalize rz to identity matrix
var rz = makeIdentityMatrix ();
rz[0][0] = cos_table[angle];
rz[0][1] = -1 * sin_table[angle];
rz[1][0] = sin_table[angle];
rz[1][1] = cos_table[angle];
return rz;
}
Now we need a function to multiply matrices. Simple and straight forward -no optimization. This looks like,
// function: matrixMultiply
//
// Description: multiplies 2
// 4x4 matrices and returns the result
// arguments: A - matrix, B - matrix
// returns: 4x4 Matrix C as result of A * B
function matrixMultiply ( A, B ) {
// initialize an empty 4x4 matrix
var C = makeNullMatrix();
// Cij = E Aik Bkj
for (i=0; i<4;i++)
{
for (j=0; j<4; j++)
{
for (k=0; k<4; k++)
C[i][j] = C[i][j] + A[i][k]*B[k][j]
}
}
return C;
}
Using function matrixMultiply, we crunch some numbers brute force and create the Rotation desired,
// function: createRotationRMatrix
//
function createRotationRMatrix (anglex, angley, anglez)
{
var Rx = createRotationXMatrix (anglex);
var Ry = createRotationYMatrix (angley);
var Rz = createRotationXMatrix (anglez);
return ( matrixMultiply ( matrixMultiply ( Rx, Ry ), Rz ) );
}
The function returns a matrix with our desired results.
What is required now is means to project the rotation onto the display. We now need a perspective projection.
// Perspective Projection
//
// | 1 0 0 0 |
// | 0 1 0 0 |
// [x y z 1] | 0 0 0 1/d|
// | 0 0 0 1 |
//
function gc_projection ( x,y,z,d,P)
{
var xy_Array = new Array();
var gc_width = 800; // game console context width
var gc_height = 600; // game console context height
var xp = P[0][0]*x+P[1][0]*y+P[2][0]*z;
var yp = P[0][1]*x+P[1][1]*y+P[2][1]*z;
var zp = P[0][2]*x+P[1][2]*y+P[2][2]*z + 10;
var aspect_ratio = gc_width/gc_height;
xp = (gc_width/2) + (xp * d / zp);
yp = (gc_height/2) + (aspect_ratio * yp * d /zp);
xy_Array[0] = xp;
xy_Array[1] = yp;
return xy_Array;
}
Here's what a simple driver code looks like in javascript.
That's that!
No comments:
Post a Comment