Jump to content
  • Advertisement
Sign in to follow this  
cozzie

Transpose World Inverse Transpose?

This topic is 711 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

Just a short question.
I'm moving away from effects11, first adding manual CBuffer managing.

I've learned that you need to transpose all matrices before you send them to the GPU.
This all works, but I'm not sure how to handle the WorldInverseTranspose.

Since it's already transposed, I could transpose it again or simply send in the WorldInverse. Would this be the way to go?

Share this post


Link to post
Share on other sites
Advertisement

I've learned that you need to transpose all matrices before you send them to the GPU

 

Actually you may not transpose them on CPU: just change multiplication order in shader code:

 

Something like this:

    OUT.position     = mul(IN.wvp, float4(IN.position, 1));
    OUT.normalVS     = mul((float3x3)IN.wv, IN.normal);

Share this post


Link to post
Share on other sites
But would I still need the the WorldInverseTranspose then?

Not sure if I understand you correct, I believe it goes for all matrices.

Share this post


Link to post
Share on other sites
But would I still need the the WorldInverseTranspose then?

 

I do not do "WorldInverseTranspose" for any matrix when I place it to a buffer in my code (and Transpose also).

 

Transpose is often used for OpenGL matrix-vector order multiplication in hlsl code.

There 3 ways to bypass it:

1. transpose matrix on CPU

2. use different order for multiplication (Vec*Mat => Mat*Vec)

3. use row_major pragma

Edited by Serenity79

Share this post


Link to post
Share on other sites

I've learned that you need to transpose all matrices before you send them to the GPU. This all works, but I'm not sure how to handle the WorldInverseTranspose. Since it's already transposed, I could transpose it again or simply send in the WorldInverse. Would this be the way to go?

What do you mean it's already transposed?
 
You mean you have:
shader_world = transpose(world)
shader_worldInverseTranspose = transpose(transpose(inverse(world)))

If so, then yes, you can optimize that to:
shader_world = transpose(world)
shader_worldInverseTranspose = inverse(world)


However, the only reason that you "have" to transpose matrices before you send them to the GPU is because you're using a CPU-side math library that stores the elements in row-major array order, and by default, GLSL/HLSL interpret matrices using column-major array order... but you can override that default setting and tell GL/D3D that your matrices are stored in using row-major element indices:

//HLSL
float4x4 world;//column major array element indexing
row_major float4x4 world;//row major array element indexing
//GLSL
mat4 world;//column major array element indexing
layout(row_major) mat4 world;//row major array element indexing

 Honestly I have no idea why Microsoft decided to make their CPU math library use row-major indexing and their shader language use column-major indexing :(

 

Or an alternative fix is to use a better math library on the CPU side :lol: :wink:
 

Actually you may not transpose them on CPU: just change multiplication order in shader code:

IMHO using different mathematical conventions in different parts of your game leads to a lot of confusion in the long run.

This works because as above, your CPU code has been using the computer-science convention of row-major array indexing, while your GPU code is currently using the computer science convention of column-major array indexing.
This mismatch of computer science conventions coincidentally has the side-effect of causing your matrices to become mathematically transposed... which means you can "fix" it by swapping your mathematical conventions as mentioned by Serenity... but it feels much better to me to just avoid ever having a computer-science or mathematical convention mixup in the first place, and especially not flipping two different conventions to coincidentally cancel each other out.

Edited by Hodgman

Share this post


Link to post
Share on other sites
Or an alternative fix is to use a better math library on the CPU side :lol: :wink:

Hodgman, could you suggest replacement for DirectXMath?

Currently I have own small wrapper upon DirectXMath.

 

I am looking for:

1. At least 80% functional of DirectXMath (float2/3/4, matrix, Quaternion)

2. Support for SSE4/AVX

3. C++ - friendly.

4. <optional> Not a big, general use, just for computer graphics/physics.

Edited by Serenity79

Share this post


Link to post
Share on other sites

DXMath isn't actually that bad, I just personally don't like their conventions :lol: It has quite good SIMD code generation from what I've seen.
 
GLM isn't bad either: http://glm.g-truc.net/0.9.7/index.html but I think it prioritizes functionality over performance (it does have SIMD support though!). IMHO your C++ math code will be a lot prettier (more readable) in GLM than in DXM, but IIRC, it uses opposite mathematical conventions to DXM.

You could check out Sony's library: https://github.com/erwincoumans/sce_vectormath It was originally created for PS3 developers (IIRC), but has grown a lot since then. I've used this a bit in the workplace where people trusted it's SIMD abilities.

Edited by Hodgman

Share this post


Link to post
Share on other sites

Thanks guys, good discussion.

I've decided not to change orders of multiplication or add row_major in the shaders. If something might change in the future I just have to remember to send in all matrices transposed, to the shaders, as long as I use DirectXMath.

 

Option 1:

		XMMATRIX worldInvTranspose = CMathHelper::InverseTranspose(world);

DirectX::XMMATRIX CMathHelper::InverseTranspose(const DirectX::CXMMATRIX M)
{
	DirectX::XMMATRIX A = M;
	A.r[3] = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);

	DirectX::XMVECTOR det = DirectX::XMMatrixDeterminant(A);
	return DirectX::XMMatrixTranspose(XMMatrixInverse(&det, A));
}

Option 2:

	XMMATRIX worldInv = XMMatrixInverse(NULL, world);

I'm now using option 2, nice and clean.

Share this post


Link to post
Share on other sites

What was option 1 supposed to do exactly? The determinant is an output parameter so that stuff you were passing in got discarded anyway.

Edited by Mona2000

Share this post


Link to post
Share on other sites

You're absolutely right, the determinant is returned/ an output.

So basically this would do the same trick for option 1:

DirectX::XMMATRIX CMathHelper::InverseTranspose(const DirectX::CXMMATRIX M)
{
	return DirectX::XMMatrixTranspose(XMMatrixInverse(NULL, M)));
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!