2
\$\begingroup\$

I know these questions have been asked a million times in various formats, but I'm getting weirdly skeptical about some of the row-major vs. column-major claims about the DirectXMath library.

If I run the following code to get a translation matrix for example:

DirectX::XMMATRIX a = DirectX::XMMatrixTranslation(1.0f, 1.0f, -1.0f));

I would expect the memory layout to look like the following (with rN indicating some row, but all the numbers contiguously in memory):

r1: 1.0   0.0   0.0   1.0      r2: 0.0   1.0   0.0   1.0     r3: 0.0   0.0   1.0  -1.0     r4: 0.0   0.0   0.0   1.0

This would correspond to the following matrix (in standard notation) where each row is stored as a contiguous array:

1.0   0.0   0.0   1.0
0.0   1.0   0.0   1.0
0.0   0.0   1.0  -1.0
0.0   0.0   0.0   1.0

Except when I look at the memory, it's the transpose of the above (i.e. the last array in memory is the last column above) which I'm to understand is "column-major" order. So, in memory, what I'm actually getting is this:

r1: 1.0   0.0   0.0   0.0     r2: 0.0   1.0   0.0   0.0     r3: 0.0   0.0   1.0   0.0     r4: 1.0   1.0   -1.0   1.0

This leaves me thoroughly confused with all the commentary and documentation around DirectXMath matrices needing to be transposed before putting them into HLSL constant buffers (of which I'm using the default column-major order). Everything states that DirectXMath matrices are stored in row-major order, but when I actually look at the memory (either as an XMMATRIX or a more direct XMFLOAT4X4) I find myself staring at a translation matrix that looks like it's been stored in column-major order.

So my confusion then lies in the fact that my transformations actually work when I don't transpose the matrices prior to putting them into the constant buffer.

Am I missing something here?

\$\endgroup\$
2
\$\begingroup\$

You posted a column-major version of the translation matrix for (1,1,-1):

1.0   0.0   0.0   1.0
0.0   1.0   0.0   1.0
0.0   0.0   1.0  -1.0
0.0   0.0   0.0   1.0

The row-major version is:

1.0   0.0   0.0   0.0 [r0]
0.0   1.0   0.0   0.0 [r1]
0.0   0.0   1.0   0.0 [r2]
1.0   1.0  -1.0   1.0 [r3]

Which is precisely what you see in XMMATRIX.

As noted in the DirectXMath Programmer's Guide:

DirectXMath uses row-major matrices, row vectors, and pre-multiplication. Handedness is determined by which function version is used (RH vs. LH), otherwise the function works with either left-handed or right-handed view coordinates.

For reference, Direct3D has historically used left-handed coordinate system, row-major matrices, row vectors, and pre-multiplication. Modern Direct3D does not have a strong requirement for left vs. right-handed coordinates, and typically HLSL shaders default to consuming column-major matrices. See HLSL Matrix Ordering for details.

See also DirectXMath GitHub.

\$\endgroup\$
  • \$\begingroup\$ I guess that's where my confusion is then because my understanding is that row-major storage means each row of a standard notation matrix (written in mathematical standard notation) would be contiguous in the array. So for a translation matrix, where the x, y, and z translations are in the last column of a standard notation matrix, they would each be the 4th, 8th, and 12th elements of the whole array. Am I getting mixed up between something with notation vs. the storage order here? \$\endgroup\$ – Dulan Feb 2 '18 at 3:40
  • \$\begingroup\$ XMMATRIX.r[3] contains the translation, i.e. the 4th row's x, y, and z. See Wikipedia: "In a row-major order, the consecutive elements of a row reside next to each other, whereas the same holds true for consecutive elements of a column in a column-major order.". This blog post might also help. \$\endgroup\$ – Chuck Walbourn Feb 2 '18 at 19:23
  • \$\begingroup\$ So then how does that relate to a translation matrix written in standard math notation, where the translation is in the last column? \$\endgroup\$ – Dulan Feb 2 '18 at 20:54
  • \$\begingroup\$ That's why the DirectXMath documentation states row vectors and premultiplication. Your 'standard notation' above is how OpenGL books write the translation matrix, and OpenGL math libraries general use column-major, column vectors. For Direct3D, you transpose it. See this blog post for a long exploration of the topic. \$\endgroup\$ – Chuck Walbourn Feb 3 '18 at 9:00

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.