sgorsten / linalg

linalg.h is a single header, public domain, short vector math library for C++
The Unlicense
854 stars 68 forks source link

Question about column order. #18

Closed mirmik closed 5 years ago

mirmik commented 5 years ago

I want to ask a question that does not give me peace for a long time, and to which I can’t find a definite answer. Why in general and in this library in particular the column order is used? What is the advantage of column order over row order and otherwise? Why did you choose the column order for this library?

sgorsten commented 5 years ago

Ah, whoops, I somehow missed this back when it was posted. I may as well answer it now.

The short answer is that all publicly-specified shading languages, that is GLSL, HLSL, Cg, and Metal, use column-major storage by default. As the initial community of users I built linalg.h for was writing graphics-heavy code, binary layout compatibility between linalg types and shader uniforms was desirable.

The somewhat longer answer is that math textbooks tend to write vectors as single-column matrices, and a matrix-vector product is usually written with the vector on the right. If the "natural" interpretation of a vector is as a single column, then it makes sense for a matrix to be defined as a collection of columns. This also yields a more efficient matrix multiplication. Each column can be multiplied by a scalar, and then the columns are summed together, giving you the product between a 4x4 matrix and a 4-element vector in 4 SIMD multiplies and 3 SIMD adds. With a row-oriented matrix, you need four dot products instead, followed by packing the result back into a register, which would be something like 4 SIMD multiplies, 4 (expensive) horizontal adds, and maybe 3 shuffle operations. Furthermore, in the former case, the product between two 4x4 matrices can just be done as 4 independent matrix * column vector multiplies, where in the row-oriented case, you would need to do that followed by a transpose operation.

Note that all of the above arguments for column-major matrices actually apply just fine to row-major matrices IF you adopt the convention of multiplying your vectors on the LEFT side of your matrices, treating them as single-row matrices. There are good arguments for using row vectors, chiefly, that the composition order of your matrix transforms is left-to-right and reads a little more naturally than the right-to-left order you have with column vectors. However, the world standardized on column vectors, and I mostly try to follow convention when I don't have a strong reason to break from it.