Friday, February 13, 2015

Matrix And Quaternion - The Coder's Nightmare

A change of pace, I will explain matrix math to coders (software developers) who struggle with it. Well, I'll try, specifically in the context of three dimensional maths.

The matrix is essentially a rigid set of instructions, a 4x4 one is used for 3d coordinates. The first thing to consider is the order of operation, it always matters when multiplying matrixes, the right hand of the formula is always computed first.

That's the simplest part of the matrix, the second simplest part is the coordinate instructions, the first three rows of the fourth column in OpenGL. This is just the coordinate of the center of the object, that last row basically says "influence anything by this much," in most cases just leave that as 1.0.

The fourth row of the other three columns has odd effects on coordinates that I will not cover here. These are best left at 0 unless you know a lot about matrix maths, eventually I may explain these but don't hold your breath.

The most important instructions are in the first three rows of the first three columns. These instructions are simple but appear very complex when you are jumping into them.

Often the 3x3 matrix is called a rotation matrix, but really it's a scale matrix. The columns each represent one of the three axis in order of x, y, and z.

The amount of influence each initial axis has over the final value of the multiplication is from each row of that column. So column 1, row 1 has determines how much of the original x value is added to the new x value; column 1, row 2 is how much of the original y value is added to the new x value; etc.

Now the part that seems even more complicated than it is. Rotating is essentially shifting the effect one axis has on another's final value, the hard part is how this is calculated, but otherwise it's just another scale value multiplied by the length of that column.

You read that right, the actual scale value is multiplied by the rotation scale values of that column, and the rotation values are always normalized. This means that the length sqrt(x² + y² + z²) is the scale value for that column.

As long as skewing or twisting is not applied as well, don't try to comprehend those until you get the basics or you'll have a bad time. The scaling values for the rotations are enough for the beginner to worry about, so here goes the most dreaded of those rotations, quaternions.

First, a quaternion is like a coordinate with an imaginary dimension added, so it's 4 dimensional. This makes them both scary and annoying for most people, I am finding them easier to work with the more I incorporate them into my code.

A quaternion is an angle, where something points to using the fourth dimension as the imaginary reference. Trying to work them out in your head will just make you wish you'd never tried so I recommend that you look at this c code instead.

void matrix_3d_setRotationFromQuaternion(GLfloat md[], int offd, float qx, float qy, float qz, float qw)
{
GLfloat lenx = matrix_3d_getScaleX(md, offd);
GLfloat leny = matrix_3d_getScaleY(md, offd);
GLfloat lenz = matrix_3d_getScaleZ(md, offd);

md[offd] = (1.0 - (2.0 * (qy * qy)) - (2.0 * (qz * qz))) * lenx;
md[offd + 4] = ((2.0 * qx * qy) - (2.0 * qz * qw)) * leny;
md[offd + 8] = ((2.0 * qx * qz) + (2.0 * qy * qw)) * lenz;

md[offd + 1] = ((2.0 * qx * qy) + (2.0 * qz * qw)) * lenx;
md[offd + 5] = (1.0 - (2.0 * (qx * qx)) - (2.0 * (qz * qz))) * leny;
md[offd + 9] = ((2.0 * qz * qy) - (2.0 * qx * qw)) * lenz;

md[offd + 2] = ((2.0 * qx * qz) - (2.0 * qy * qw)) * lenx;
md[offd + 6] = ((2.0 * qy * qz) + (2.0 * qx * qw)) * leny;
md[offd + 10] = (1.0 - (2.0 * (qx * qx)) - (2.0 * (qy * qy))) * lenz;
};

Because of the nature of the quaternion, it can represent a rotation matrix on it's own, making it very easy to use with matrixes. So now you can see why I prefer them, also they tween and stack well for skeletal animations.

Converting them from axis/angle coordinates is likely your most common method when you incorporate them into any project, because you can picture those in your head easier but then you will want them as quaternions for the actual maths. The main reason to consider using matrixes and quaternions is because they use less to accomplish more.

This Java code should explain the conversion well.

public void axisAngleToQuaternion()
{
float angle = vec[3] * 0.5f;
float sa = (float)Math.sin(angle);
normalize();
vec[0] *= sa;
vec[1] *= sa;
vec[2] *= sa;
vec[3] = (float)Math.cos(angle);
normalizeAsQuaternion();
}

As you can see, the quaternion only uses 180 degrees, or 2*pi radians, half a circle. "vec" is the float array in the Java object, the fourth index has the angle and the w coordinate is stored there when finished, normalizing is done only for the x, y, and z coordinates.

The x, y, and z coordinates indicate a point along one axis, the second angle is in the w, rotating the circle by this. There you go, that's a quaternion simplified.

Another reason to use them is that there is a very simple method for multiplying then with a vertex coordinate that requires only basic math and works well in shaders, in GLSL it looks like this.

vec3 qrot(vec4 q, vec3 v)
{
    return v + 2.0*cross(q.xyz, cross(q.xyz,v) + q.w*v);
}

If you're doing 3d you should know a cross product by now. The above GLSL function will multiply a vector v by a quaternion q, and it produces no lag in shaders even for thousands of vertexes.

I came upon this while I was struggling to understand the quats, it explained them to me the best, just seeing this formula made it all connect in my head. If you don't know GLSL, I suggest you learn about swizzling to understand the above function.

Now the reasons to use these for everything, not just graphics. Matrixes can contain a large number of instructions for any operation, and multiplying them uses far fewer CPU/GPU cycles than if done procedural. I only touched on the very basics of these great mathematical concepts feared by most coders, and focused on one use.

Quaternions and matrixes can help you write applications for scientific studies, video games, even data crawling. I am working on an OpenGL game library for Android, and after that a neural network simulator with 3d interface and visualization.

Matrixes will become invaluable in the field of artificial intelligence, they are actually the computer's language. The biological brain has it's own complex and sloppy language, so we can correct that mistake by making the computer efficient and fast, we can use matrixes.

No comments:

Post a Comment