I used to find myself needing to do a quick refresher on the different matrix notations and usage patterns in computer graphics every time I sat down to do any 3D math. I'm admittedly not a graphics expert and it took me a long while to beat this information into my head. There are a lot of articles online on the topic, but I found most of them to be incomplete or poorly written. In the interest of saving others some headaches, I decided to write up my own explanation. If you get confused by the different notations for matrices, the "right-handed" vs "left-handed" coordinate systems, pre- vs post-multiplication, or the differences between row-major and column-major matrices, read on.
A lot of the confusion boils down to OpenGL's notation versus Direct3D's notation. Many, many programmers often confuse the different terms. Direct3D code is often using a left-handed coordinate system, row-major matrices, row vectors, and pre-multiplication. OpenGL code is often using a right-handed coordinate system, column-major matrices, column vectors, and post-multiplication. Very few libraries exist that use other combinations, which means that many programmers have come to confuse coordinate system handedness with matrix notation or multiplication order.
The end goal of this article is to explain how OpenGL and Direct3D are actually very compatible and how you don't need to have two different ways of doing math to handle both. A single math library can drive either graphics API without needing to tranpose matrices or do any extra work.
Matrix Notation and Terminology
Matrices in mathematics can be thought of simply as a rectangle grid of numbers, composed of rows and columns. For example,
| 0 -1 3 4 |
| 0 2 -2 -1 |
| 3 0 1 0 |
A vector is often described as a separate entity, but in reality a vector is just a matrix which has either one column (a column vector) or one row (a row vector). These are sometimes just called column-major and row-major vectors, but I prefer to avoid that nomenclature since the same naming is used for an entirely different concept in matrix notation. Examples of row and column vectors:
| 1 -2 1 0 |
| 1 |
| -2 |
| 1 |
| 0 |
We'll got into why and when you should use a row vector vs a column vector in a bit. For now, I'm going to focus on matrices in general.
A matrix supports a few different basic operations. The most important for the purposes of this article is the matrix product, also often called "matrix multiplication" or "matrix concatenation." This is an operation that takes two input matrices and produces a third output matrix. For computer graphics, where matrices can represent transformations in 2D or 3D space, this is often used to combine two transformation matrices into a single transformation matrix which performs the same operations as the original two matrices.
Matrices have dimensions. The first matrix above is a 4x4 matrix, because it has four columns and four rows. The row vector above might be referred to as a 1x4 matrix or a 4x1 matrix, depending on which notation you are using. This is the first place things start to get confusing.
Row Major vs Column Major Notation
Matrix notation can use either a row major or a column major notation. It's important to remember that there is both a difference in notation and a difference in storage in computer memory, but these are separate issues. However, they are generally used together; that is, if you are using row-major notation, you are also most likely using row-major storage. I'll get to the storage issue in a bit, but for now we're purely talking about notation.
It's also very worth noting that handedness is an entirely different issue. There is no such thing as a "right handed matrix" for instance. More on that later.
In row-major notation, the size and indices of a matrix are written with the row first and the column second. The row vector above would be called a 1x4 matrix (1 row, 4 columns). To refer to the third element, you would write M1,3 (1st row, 3rd column). When using column-major notation, these numbers are reversed. The row vector would be called a 4x1 matrix (4 columns, 1 row) and the third element would be be referred to as M3,1 (3rd column, 1st row).
With most mathematical texts, the row-major notation is generally used. Examples of row-major matrix notation:
| 1 0 |
| -4 3 |
| 1 4 |
The element at M2,1 is -4.
A 2x4 matrix:
| -2 0 0 5 |
| 1 -7 7 3 |
The element at M2,1 is 1.
In general, mathematics texts also use column vectors. That is, a 4-component vector is a 4x1 matrix (4 rows, 1 column).
Direct3D programmers will often use row-major notation as well (however, D3D programmers often use row vectors instead of column vectors). However, OpenGL uses a column-major notation. When reading OpenGL-related texts, the row and column will hence likely be reversed in order. This is also true of the matrix types in GLSL shaders.
In OpenGL, a mat4x2 is a matrix with 4 columns and 2 rows. In Direct3D, a float4x2 is a matrix with 4 rows and 2 columns.
| 1 -4 1 |
| 0 3 4 |
The element at M2,1 is -4.
A 2x4 matrix:
| -2 1 |
| 0 -7 |
| 0 7 |
| 5 3 |
The element at M2,1 is 1.
There is a reason why OpenGL went with column-major notation, and that's because OpenGL uses column-major storage as well. On the other hand, Direct3D's original math libraries favored row-major matrices and hence also used row-major storage. It's actually very advantage to keep these the same; mixing row-major notation with column-major storage will complicate things down the line.
Matrix storage is where most people get confused by column-major or row-major notation. By matrix storage, I mean how the matrix is laid out in computer memory. Memory is a sequential list of addresses, not a grid, so some kind of mapping between the row and column indices to memory addresses is required.
If a matrix is stored in row-major format in memory, that means that the values are stored out one row at a time. For example,
| 2 3 -1 |
| -1 0 7 |
is stored row-major in memory like this:
2 3 -1 -1 0 7
and is stored column-major in memory like this:
2 -1 3 0 -1 7
| 1 0 -1 |
| 2 -3 1 |
The same matrix as a column-major 3x2 matrix (transposed):
| 1 2 |
| 0 -3 |
| -1 1 |
Both matrices stored in memory, using row-major for the first and column-major for the second:
1 0 -1 2 -3 1
The notation for the matrices is transposed, but the storage is the same. It means that you can use row-major notation (and storage) for a matrix and pass that matrix unmodified into an API that expects column-major matrices without needing to transpose the matrix first.
Unfortunately, there is still the difference between the use of row vectors and column vectors, which actually changes the values you store in your matrices, causing them to be transposed. The effect these have comes down to the matrix product.
The Matrix Product
The matrix product is the multiplication of two matrices. Note that the matrix product is associative but it is not commutative. Thus,
A x B x C = (A x B) x C = A x (B x C)
Matrices are NOT commutative:
A x B != B x A
The matrix product can be computed using a pre-multiplication or post-multiplication. The terms derive from whether a vector should be on the left side (in front of, or "pre") the matrix or on the right side (after, or "post").
Since the matrix product is not commutative, swapping which side of the matrix a vector is on when calculating the matrix product will result in an entirely different result. When graphics APIs choose to use pre- or post-multiplication, they actually need to use transoposed matrices in order to get the same result vector.
In mathematics texts, post-multiplication is the norm. This in turn means that the matrix product is computed like so: each component of the result, at a given row R and column C, is computed by taking the dot product of the left matrix's row R by the right matrix's column C. For example:
| 2 -3 | x | 1 0 | = | a b |
| 1 4 | | -2 3 | | c d |
So, with post-multiplication, to compute the result component b of matrix C, at row 1 column 2, take the dot product of row 1 of matrix A and column 2 of matrix B (noting this is in row-major notation). All of the results are:
a = 2 * 1 + -3 * -2 = 8
b = 2 * 0 + -3 * 3 = -9
c = 1 * 1 + 4 * -2 = -7
d = 1 * 0 + 4 * 3 = 12
A x B = C
| 2 -3 | x | 1 0 | = | 8 -9 |
| 1 4 | | -2 3 | | -7 12 |
The following is all using a row-major notation until otherwise noted.
It gets a tiny bit trickier when you're not multiplying square matrices. A 3x3 matrix multiplied with a 3x1 matrix (a 3-component column vector) results in a 3x1 matrix. A 4x2 matrix multiplied by a 2x3 matrix results in a 4x3 matrix. The reasons should be fairly clear: since you take the dot product of the first matrix's row and the second matrix's column, the first matrix must have a number of columns equal to the second matrix's number of rows. Since you do this for each row in the first matrix and each column in the second matrix, your result matrix will have the same number of rows as the first matrix and the same number of columns as the second matrix.
In general, take the dimensions of the two matrices written next to each other. The inner two values must be the same, and the resulting matrix has the size of the outer values. 4x3 x 3x2 = 4x2. 2x4 x 4x1 = 2x1. A 4x3 and a 2x4 matrix cannot be multiplied, since 3 is not equal to 2.
Notable, with post-multiplication, you must use the column vector notation. A row vector would be a 1x4 matrix (row-major notation), and you can't multiply a 4x4 matrix against a 1x4 matrix.
Now let's talk about pre-multiplication.
With pre-multiplication, you are now forced to use row vectors. A column vector is a 4x1 matrix, but you can't multiply a 4x1 matrix with a 4x4 matrix. You instead must use a row vector, such that you have a 1x4 matrix times a 4x4 matrix (the inner/closest two values match) giving you a 1x4 matrix (row vector) as a result (the outer two values are 1 and 4).
However, note that with pre-multiplication, the operations are different. With post-multiplication, a 4x4 matrix multiplied with a 4x1 column vector took the dot product of each row of the matrix with the vector. Now with pre-multiplication, the dot product is with the vector and each column of the matrix (since the matrix is now on the right side of the multiplication operator).
Thankfully, this is easy to correct for. When computing matrices for pre-multiplication you simply need to transpose the matrix you'd have used for post-multiplication. This also makes sense since you are transposing the vectors, going from the 4x1 column vector to the 1x4 row vector.
| 1 2 | x | -4 | = | 2 |
| -2 1 | | 3 | | 11 |
Pre-multiplication of the transposed
1x2 vector and transposed 2x2 matrix:
| -4 3 | x | 1 -2 | = | 2 11 |
| 2 1 |
The results are the same vector, transposed:
| 2 | = transpose(| 2 11 |)
| 11 |
What happens if you use post-multiplication with column-major notation, like OpenGL typically uses? The math is all the same, but the order of the matrix sizes is reversed. So you can't multiply a column-major 4x3 matrix with a 3x2 matrix, but you can take the product of a 2x4 and 1x2, which results in a 4x1 matrix (a 4-component row vector). The results and how they work are all identical. Only the notation ends up being different.
The Tricky Part
What matters is whether pre-multiplication or post-multiplication are used. The D3DX math library uses pre-multiplication and OpenGL's standard matrix stack uses post-multiplication. That in general would mean that the matrices must be transposed.
However, remember that D3DX uses row-major matrices and OpenGL uses column-major matrices. They are thus already transposes of each other. Take a look at the following simple 3x3 transformation matrix representing a uniform scale of 2 and a translation of (10,-5) in both styles:
| 2 0 0 |
| 0 2 0 |
| 10 -5 1 |
Row-major memory layout:
2 0 0 0 2 0 10 -5 1
3x3 matrix intended for column-vectors
| 2 0 10 |
| 0 2 -5 |
| 0 0 1 |
Column-major memory layout:
2 0 0 0 2 0 10 -5 1
The matrices are transposed when written out conceptually, but when stored in memory, they're the exact same!
That's both the tricky part and the cool part. All of your code will look and feel different due to the different notation and the different matrix product order, but the actual matrix values are interchangeable.
The end result of all this is that you can easily write a single matrix class that handles both the Direct3D and OpenGL styles. So long as you use column-major with post-multiplication and row-major with pre-multiplication, the data is fully compatible.
To stress this again: handedness had nothing to do with any of this! Not once did I mention a left-handed matrix (there's no such thing) or a right-handed product (again, no such thing). Handedness comes down entirely to the coordinate system, which is a separate issue entirely.
Coordinate System Handedness
Coordinate systems are a generalization of the concept of vector spaces in linear algebra. When we talk about Euclidian space, such as a given (x,y,z) coordinate in 3D space, we are working with a particular basis. A basis essentially tells us how to interpret the coordinates we have. Which direction is up: positive Y, or positive Z, or maybe -X? The answer is that it's all up to you to decide. You can define your basis for 3D space any which way you want.
That said, there are two conventions generally used. These are generally just referred to as the right-handed and left-handed coordinate systems. In both systems, positive X means "to the right" and positive Y means "up." The difference is whether positive Z means "away from you" or "towards you."
The naming of the coordinate systems can help you remember which is which. Let's start with the right-handed coordinate system. On your right hand, extend your index and thumb fully, so that they make a right angle and are parallel to your palm. Your thumb represents the positive X axis and your index finger represents the positive Y axis. Now raise your middle out 90 degrees from your palm. Your middle finger represents the Z axis. Now look into your palm, such that your thumb (positive X) is pointing to the right and your index finger (positive Y) is pointing up. Your middle finger will be pointing towards yourself, meaning that positive Z points towards you, and negative Z is pointing away from you.
Now let's do the left-handed coordinate system. Try the same exercize with your left hand. You'll end up staring at the back of your left hand when you align your thumb and index finger to be pointing right and up, respective. Your middle finger is now pointing away from you, meaning that positive Z points away from you and negative Z points towards you.
What does all this have to do with vectors, matrices, row-major vs column-major, pre-multiplication vs post-multiplication, and row vs column vectors? Absolutely nothing at all.
The only difference you will ever see from using a right-handed vs a left-handed coordinate system is the math behind how you construct your projection and camera matrices.
With the right-handed system, your camera matrix will be aligning its "view" or "look at" vector with the negative Z axis, and the projection matrix will be converting this back around to positive Z when it converts to normalized device coordinates (NDC space). On the other hand (sorry), for the left-handed coordinate system, your camera matrix will be aligning its view vector with the positive Z axis, and the projection matrix will not be negating the Z values any longer.
You can use a left-handed or right-handed coordinate system in either Direct3D or OpenGL. You can use a left-handed or right-handed coordinate system with both row-major and column-major matrices. You can use a left-handed or right-handed coordinate system with both pre- and post-multiplication.
Other Coordinate Systems
You will very rarely see other coordinate systems, but there is one in particular that comes up now and again, especially for content tools and artists. The reason for this other coordinate system tends to arise from folks who work a lot more often with paper than computers, I believe. It's standard convention to have positive X be to the right and positive Y to be upwards for whichever medium you're working on, be it a computer screen or paper. The Z axis then extends into or out of that medium. However, if you're working on paper and orient your head to be looking forward (perpendicular to the desk your paper is laying on), the "up" axis is now sticking straight out of the paper, and the axis that is running vertically along the paper's surface is facing away from you. You hence see a few people who think that means that the positive Z axis should be up and that the positive Y axis should point away from you.
Unfortunately, that also includes the developers who wrote certain popular content tools, such as 3DS Max.
Your engine should use one of the two standard coordinate systems. Your content pipeline will need to convert data from any misbehaving content tools into the standard basis. Do not try to use an alternative coordinate system in your engine; it will just make writing your projection matrices harder since you have to re-derive the math, and it will make the code and game tools more confusing for any other programmers who work on it.
Your source code is the last place to assert individuality. Conform! It just makes everything easier for other programmers to understand and maintain.
Math Library API Design and Implementation
Standard mathematical notation these days is generally considered to be row-major matrices, column vectors and post-multiplication, and the right-handed coordinate system. I mentioned before that the OpenGL library originally used column-major matrices but otherwise it follows the standard notation. Direct3D's bundled math library instead uses the standard row-major matrix notation, but uses pre-multiplication and row vectors. This can seem a little frustrating and odd.
In reality, these are both historical artifacts. For one, it's important to note that today's more standard mathematical notation was anything but standard just a few years back. Some of the original mathematical papers on these topics used row-vectors and pre-multiplication, for instance. I theorize that the OpenGL model became closer to standard simply because OpenGL is the graphics API of choice in the academic world, so today's computer-savvy mathematician simply has an easier time by using conventions closer to the OpenGL model. Row-major matrices have been the standard mathematical notation since their inception, however, so those have stuck.
One might wonder why OpenGL picked column-major matrices if the standard notation had always been row-major. The reason basically boiled down to a technical decision combined with the desire to use post-multiplication. When performing the actual computations on the original hardware OpenGL was designed for, it turned out to be faster to do post-multiplication if the memory layout of items in the matrices were column-major. Since it's convenient to represent a matrix as a C rectangular array, that meant that the first component index listed would be the column rather than the row. Simply swapping all the notation made the OpenGL API internally consistent, even if it did put it at odds with convention.
When the designers of Direct3D chose to go with pre-multiplication, they lucked out in that the row-major matrix notation they preferred was also actually the faster approach (since pre-multiplication with row-major matrices results in the same memory layout as OpenGL, which was designed for fast matrix multiplication). I don't know to what degree this might have influenced their choice to go with pre-multiplication, but it's entirely possible that given that there was at the time no strong consensus on pre- or post-multiplication notation for matrices but a strong preference for row-major notation, the D3D designers found it an easy choice to make at the time.
There's a small wrinkle in the story, in that Direct3D's effects framework would use column-major ordering. This means that a Direct3D programmer who wants to use pre-multiplication in his shaders needs to transpose his matrices. The Direct3D 9 effects API did this automatically, but Direct3D 10 changes this to be optional. The shaders can be written using either pre- or post-multiplication. The D3DX math library can also be used for either order, although the functions that construct matrices assume pre-multiplication will be used.
The OpenGL API has a few different versions and some deprecated functionality. The original math library included with OpenGL and the GLU library (GL Utility) both assumed post-multiplication, as well as assuming the right-handed coordinate system. When you ignore the deprecated OpenGL math library, however (which you should!), you are free to use whichever conventions you want. Just be consistent internally in your own code.
Unfortunately, there are also a few other key differences between OpenGL and Direct3D that do require some extra explicit work to get around. More on those at the end of the article.
Rolling It Yourself
If you're writing your own math library, here's a few tips.
First, remember that you should always require that matrices either use row-major matrix memory layout with pre-multiplication or column-major matrix memory layout with post-multiplication. If you do that, you only need a single matrix class, as we showed that the two matrix transpose operations necessary to convert between the two cancel out. You need only define a single matrix product operator and two matrix-vector operators, one for pre-multiplication (using row vector notation) and one for post-multiplication (using column vector notation).
For your matrix creation procedures, your rotation, scale, and translation functions need only a single version. Again, you must transpose the matrices to convert between row-major and column-major as well as between pre- and post-multiplication, which cancels out.
For example, the following C function will multiply two 4x4 matrices using either approach:
void mat44_product(const mat44 *lhs, const mat44 *rhs, mat44 *output)
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
output->m[i][j] = 0;
for (int k = 0; k < 4; ++k)
output->m[i][j] += lhs->m[k][j] * rhs->m[i][k];
That one function handles both the cases of row-major matrices with pre-multiplication and column-major matrices with post-multiplication.
On C++ Templates
There's a tendency for C++ programmers to want to use templates in their matrix and vector libraries. It seems pretty handy to be able to write a single class that can handle different primitive data types (float vs double, for instance) as well as different sizes. Who wants to have to cut-n-paste and modify a version of the matrix class for everything from 2x2, 2x3, etc., 3x4, 4x4? Who even wants to write three different versions for 2-, 3-, and 4-component vectors?
There are a couple problems with this approach, however. The largest problem is that it removes the ability to customize methods easily. For example, the cross product is a well-defined concept for 3-component vectors, but it's meaningless for 2- and 4-component vectors. You're also likely going to want to have different constructors for each version that directly takes the correct number of component values.
You might think to use a base class that implements most functionality and then thin wrapper classes to add the desired constructors and any methods specific to certain variations. You might also think to make some of that functionality in free-standing functions, which is actually encouraged by many C++ luminaries. However, both of these "fixes" come at a cost: reduced IDE/tool support. The methods or operators in the base class will return values of the wrong type, unless you add a lot of ugly code with casts and the CRTP (Curiously Recursive Template Pattern, for those who are not C++ buffs), and the free-standing functions don't show up in code-completion lists for anyone trying to explore or inspect your API From an IDE.
A second problem with the template approach is that templates are simply much slower for the compiler to deal with. Using a template-heavy approach in your math library (or any other portion of your engine) can push your compile times through the roof. This in turn greatly impacts your iteration time, which means it takes longer and costs more money to make your game, or that you have to reduce scope of your game to stay in budget.
A third problem is that you will need to write custom versions anyway if you want to heavily hand-optimize your math code. Or you may just find that your 4-component vector and 4x4 matrix can be written with fast SIMD code while the others aren't, simply because of the size and alignment requirements of SIMD data types along with the desire to keep smaller data types actually small for memory/cache reasons.
My recommendation is to build good quality versions of a 4-component vector and a 4x4 matrix. Add all the standard mathematical functionality you think you'll need. Test it thoroughly with high-quality unit tests. Do not overly worry about optimization, since the compiler (especially VC++ 2012, GCC 4.7, Clang 3.1, or ICC) is probably smarter than you when it comes to optimizing loops and vectorizing its code anyway unless you're a hardened games veteran with serious compiler-fu skills (and if you do want to optimize, as always, do it when needed, not up front). Then just cut-n-paste and modify them for the other cases you actually need, and add any functionality (with tests) to the variations as makes sense (like cross product for the 3-component vector). Chances are you're not going to need a 3x2 matrix or a 4x3 matrix, so don't bother implementing them.
I'd also make a case for remembering to keep the number of function invocations lean for debugging purposes. It's really nice and clean to be able to have your vector .Magnitude() method call return the square root of the .SquareMagnitude() result, which in turn just calls .Dot(*this). However, when debugging, inlining is generally disabled, and the checked method call overhead can and will swamp most hand-optimized math code you write, especially since all that hand-optimized code will just be forced to pass SIMD values around on the stack instead of in registers anyway. The same advice holds for any other "hot" classes you might write, especially data structures and smart pointers. Prefer accessing matrix components directly rather than through wrapper methods or overloaded operator, and so on. You should very very rarely run into any actual problems with the weaker abstractions, and the extra boost in runtime speed in debug builds will be a great boon to your iteration times, especially for smaller projects.
OpenGL and Direct3D Differences
Unfortunately, there are also a few other key differences between OpenGL and Direct3D that do require some extra explicit work to get around. Some of them may directly affect your math library if you're trying to be portable.
OpenGL expects Z-buffer values to be in the range of [-1,+1], while Direct3D expects them to be in the range [0,+1]. There are pros and cons to each; the hardware is internally using the [0,+1] range for its actual depth checks, but using [-1,+1] makes the range consistent with the X and Y ranges in NDC space.
This can be easily fixed in your math library routines for creating projection matrices, or it can be fixed with a separate set of transforms applied to the projection matrix. You can take a Direct3D projection matrix and convert it to an OpenGL one by applying a Z scale of 2 and a translation of -1. This will make [0,+1] to [-1,+1].
glScalef(1, 1, 2);
glTranslatef(0, 0, -1);
OpenGL and Direct3D 10 and 11 use a half-pixel center. That is, the screen coordinate (40,50) corresponds to the center of the pixel at that location. In Direct3D 9, the coordinate (40,50) maps to the upper left corner of the pixel. This can result in different rendering for some effects and 2D graphics, making them look fuzzy and blurred.
This again can be fixed by simply applying a small offset to matrices. As this is only an issue for Direct3D 9, it's almost safe to just start ignoring this one.
Screen Coordinate Reference
Window space coordinates in OpenGL have their origin in the bottom left, while in Direct3D they have their origin in the upper left. This can be a problem particularly when writing full-screen post-processing effects.
This can be fixed several ways, again all relatively easily. There are extensions to flip the coordinates in OpenGL to match Direct3D, namely ARB_fragment_coord_conventions, which is by far the easiest. Otherwise, simply adjusting the Y coordinate in shaders is easy enough, although it requires passing in the window height to the shader. You could also pass in a screen coordinate matrix, which would be the identity matrix for one API and a translation+inversion for the other.
#extension GL_ARB_fragment_coord_conventions : require
layout(origin_upper_left) in vec4 gl_FragCoord;
Texture Coordinate Reference
This is the same issue as the previous item, except for texture coordinates instead of screen space coordinates.
Unfortunately, I do not know of any handy extensions to easily fix the issue. The easiest approach is to pass in a texture matrix and use a translation+inversion for one of the two APIs. Since the desired transformation is to simply subtract the V component of the UV vector from 1.0, this could just be hard-coded into the shaders for one of the two APIs. Higher-level shader systems can even do this automatically.
// which must be multiplied with texture coordinates
// in the shaders
glTranslatef(0, 1, 0);
glScalef(0, -1, 0);
Triangles have a front face and a back face. Which face is which is dependent on the vertex ordering of the triangle and the API. OpenGL says that triangles should have counterclockwise ordering while Direct3D says they should have clockwise ordering.
This item is easily fixed, as both APIs allow you to change the vertex ordering between clockwise and counter-clockwise as you see fit.
OpenGL and Direct3D matrices are more or less completely compatible. Pre-multiplication with row-major matrices and post-multiplications with column-major matrices produce the exact same results in memory. There's no need to worry about which one to use or how to make them work together; pick the convention you prefer and everything will just work, even if you're trying to support both graphics APIs with the same core code.
Re-read this article a time or two if you must. Let this information sink in. There's really not a whole lot to it, but there is a lot of confusing misinformation out there. Lots of forum posts or Stackexchange answers mix up the terminology or muddle the different concepts together, which in turn leads to more programmers not knowing the difference between coordinate system handedness and matrix notation, among other things.
If you have any additional questions or feel there's anything I can clarify (or, if I screwed up, anything I should fix!), let me know. I'd like to get future generations of graphics students and game professionals working without any of the confusion many of us suffer with today.
Thanks to Treb Connell for editing and corrections.