Sign in to follow this  
Six222

Matrix Multiplication Order

Recommended Posts

I have a question about matrix multiplication order in HLSL. For example in C++ I do the following:

XMMATRIX matFinal = matWorld * matView * matProj;

This works correctly and I just upload the final matrix to the GPU and do 

position = mul(matWorld, position)

But when I transfer each matrix individually and try and to the multiplication in the shader I doesn't work... Example:

float4x4 matFinal = mul(mul(matWorld, matView), matProj);
position = mul(matFinal, position);

If anyone could explain or point me in the right direction that would be great smile.png

 

Thanks.

Edited by Six222

Share this post


Link to post
Share on other sites

To me, that looks like you need to change your multiplication nesting. That said, I could be horribly wrong, so if it doesn't work part 1 of the solution is probably to ignore me =)

 

float4x4 matFinal = mul(matWorld, mul(matView, matProj));

Edited by CoreLactose

Share this post


Link to post
Share on other sites

Try this:

float4x4 matFinal = mul(matProj, mul(matView, matWorld));

But it's probably better to transform vertex thrice:

position = mul(matWorld, position);
position = mul(matView, position);
position = mul(matProj, position);

Share this post


Link to post
Share on other sites

Your ordering looks wrong. By default HLSL uses column major matrices, while in C they are row major.

The code should be
mul(proj, mul(view, world)).

 

Ah that worked, thanks! Is there any reason why the DirectX math library uses row major and then decides to switch in HLSL to column?

Share this post


Link to post
Share on other sites

Ah that worked, thanks! Is there any reason why the DirectX math library uses row major and then decides to switch in HLSL to column?

I don't know, probably legacy reasons.

You can put '#pragma pack_matrix(row_major)' at the start of your shader, then you can use the same multiplication order as in C, but then you also need to change the multiplication order of the position and the matrix.

Share this post


Link to post
Share on other sites

Ah that worked, thanks! Is there any reason why the DirectX math library uses row major and then decides to switch in HLSL to column?

Because DX math is for C / C++, and these languages, like most others, use row-major as "natural" layout: The elements of a row are consecutive in memory, assuming that the 1st index of a float[i][j] is the row, and the 2nd is the column.

But the HLSL compiler by default assumes that one register (4 floats) contains one column (column-major). Assuming row vectors, the order of multiplication is this:

v * M

Here, the compiler can create very efficient code, because the multiplication is just 4 dp4 (dot product) instructions. However, the C code packed the matrix "wrong", and setting the shader constants puts a row into one register, not a column, and so the calculation yields nonsense.

You have 3 options:

 

1. Change the order of multiplication, like already mentioned. This makes the compiler assume a column vector:

M * v

So you actually "cheat" by making an implicit matrix transpose.

2. Use the already mentioned compiler option so the compiler assumes that a register contains a row, not a column.

 

However, the problem is that the compiler must create less efficient code here (4 vector * scalar and addition); this needs 3 instructions more.

 

Option 3:

Transpose the matrix before setting it as shader constant. Now it is actually column-major, the multiplication order is the same as it is in your C++ code, and the GPU code is optimal.

Edited by cdoubleplusgood

Share this post


Link to post
Share on other sites

To me, that looks like you need to change your multiplication nesting.

Matrix multiplication is associative, so the nesting cannot be wrong.

Edited by cdoubleplusgood

Share this post


Link to post
Share on other sites

 


To me, that looks like you need to change your multiplication nesting.

Matrix multiplication is transitive, so the nesting cannot be wrong.

 

After sleeping and resetting my brain, I don't really know how I came up with my answer.

That said, don't you mean matrix multiplication is associative? X(YZ) = (XY)Z

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this