Row major vs. column major, row vectors vs. column vectors
Row-major vs. column-major is just a storage order thing and doesn’t have anything to do with what kind of vectors you use. But graphics programmers tend to be exposed to either GL (which uses column-major storage and column vectors) or D3D (which used row-major storage and row vectors in its fixed function pipeline, and still uses that convention in its examples), so they think the two things are tied together somehow. They’re not. So let’s disentangle them! But before we start, a small apology: In this article I’ll be using both “formula” and “code” notation for variable names, which is a typographic faux pas but necessary because WordPress isn’t very good at lining up formulas with body text and I don’t want to make this unnecessarily hard to read.
There’s no disagreement about how plain arrays are stored in memory: Any programming language that supports a plain (not associative) array type just stores the elements sequentially. Once a language supports multidimensional arrays however, it needs to decide how to squeeze the 2D arrangement of data into a 1D arrangement in memory, typically as an 1D array. One classical use case for multidimensional arrays are matrices:
Given a Matrix , there’s two “canonical” ways to store it in memory (discounting fancier storage schemes such as tiled or Morton-order storage):
- Row-major storage traverses the matrix by rows, then within each row enumerates the columns. So our
Awould be stored in memory as
a11, a12, a13, a21, a22, a23. This matches reading order in English and most other Western languages. Row-major storage order for 2D arrays is used by C / C++, the Borland dialects of Pascal, and pretty much any language that was designed since the mid-1970s and supports multidimensional arrays directly. (Several newer languages such as Java don’t support 2D arrays directly at all, opting instead to represent them as an 1D array of references to 1D arrays). It’s also the dominant storage order for images. The position of the element at row
jin the underlying 1D array is computed as
i*stride + j, where
strideis the number of elements stored per row, usually the width of the 2D array, but it can also be larger.
- Column-major storage traverses the matrix by columns, then enumerates the rows within each column.
Awould be stored in memory as
a11, a21, a12, a22, a13, a23. Column-major storage is used by FORTRAN and hence by BLAS and LAPACK, the bread and butter of high-performance numerical computation. It is also used by GL for matrices (due to something of a historical accident), even though GL is a C-based library and all other types of 2D or higher-dimensional data in GL (2D/3D textures etc.) use row-major.
Let me reiterate: Both row and column major are about the way you layout 2D (3D, …) arrays in memory, which uses 1D addresses. In the example (and also in the following), I always write the row index first, followed by the column index, i.e. is the element of
A in row
i and column
j, no matter which way it ends up being stored. By the same convention, I write “3×4 matrix” to denote a matrix with 3 rows and 4 columns, independent of memory layout.
The next ingredient we need is matrix multiplication. If
B are a and a matrix, respectively, their product
C=AB is a matrix – note the middle dimension has to match between the two. The element in row
i and column
j of matrix
C is computed as the dot product of the
i-th row of
A and the
j-th column of
B, or in formulas . All this is standard stuff.
The important point to note here is that for a matrix product
AB you always compute the dot product between rows of A and columns of B; there’s no disagreement here. This is how everyone does it, row-major layout or not.
Row and column vectors
So here’s where the confusion starts: a
n-element vector can be interpreted as a matrix (a “typecast” if you will). Again, there’s two canonical ways to do this: either we stack the
n numbers vertically into a matrix, which gives a column vector, or we arrange them horizontally, producing a matrix or row vector. Note that this has nothing to do with the storage layout; in both row and column major orders, row and column vectors are simply stored as a
n-element 1D array.
Here’s where the confusion starts: To transform a vector by a matrix, you first need to convert the vector to a matrix (i.e. choose whether it’s supposed to be a column or row vector this time), then multiply the two. In the usual graphics setting, we have a 4×4 matrix
T and a 4-vector
v. Since the middle dimension in a matrix product must match, we can’t do this arbitrarily. If we write
v as a 4×1 matrix (column vector), we can compute
Tv but not
vT. If v is instead a 1×4 matrix (row vector), only
vT works and
Tv leads to a “dimension mismatch”. For both column and row vectors, the result of that is again a column or row vector, respectively, which has repercussions: if we want to transform the result again by another matrix, again there’s only one place where we can legally put it. For column vectors, transforming a vector by
S leads to the expression , so the matrix that represents “first
S” is given by the matrix
ST. For row vectors, we get , so the matrix for the concatenation of the two is given by
TS. Small difference, big repercussions: whether you choose row or column vectors influences how you concatenate transforms.
GL fixed function is column-major and uses column vectors, whereas D3D fixed function was row-major and used row vectors. This has led a lot of people to believe that your storage order must always match the way you interpret vectors. It doesn’t. Another myth is that matrix multiplication order depends on whether you’re using row-major or column-major. It doesn’t: matrices are always multiplied the same way, and what that product matrix means depends on what type of vector you use, not what kind of storage scheme.
One more point: say you have two
v. There’s two ways to multiply these two together as a matrix product (yes, you can multiply two vectors): if
u is a row vector and
v is a column vector, the product is a 1×1 matrix, or just a single scalar, the dot product of the two vectors. This operation is the (matrix) inner product. If instead
u is a column vector and
v is a row vector, the result is a matrix, or outer product (actually the outer product can also be computed when the dimensions of
v don’t match, but I ignore that case here). The outer product isn’t as common as its more famous cousin, but it does crop up in several places in Linear Algebra and is worth knowing about. I bring these two examples up to illustrate that you can’t just hand-wave away the issue of vector orientation entirely; you need both, and which one is which matters when the two interact.
With that cleared up, two comments: First, I recommend that you stick with whatever storage order is the default in your language/environment (principle of least surprise and all that), but it really doesn’t matter whether you pick row or column vectors – not in any deep sense, anyway. The difference between the two is purely one of notation (and convention). Second, there happens to be a very much dominant convention in maths, physics and really all the rest of the world except for Computer Graphics, and that convention is to use column vectors by default. For whatever reason, some of the very earliest CG texts chose to break with tradition and used row vectors, and ever since CG has been cursed with a completely unnecessary confusion about things such as matrix multiplication order. If you get to choose, I suggest you prefer column vectors. But whatever you do, make sure to document whatever you use in your code and stick with it, at least within individual components. In terms of understanding and debuggability, a single source file that uses two different vector math libraries with opposite conventions (or three – I’ve seen it happen) is a catastrophe. It’s a bug honeypot. Just say no, and fix it when you see it.