A word on Matrices

Working on a sample regarding Orientation in a Direct3D app on Windows Phone 8, I came upon the need to clarify why I’m writing some math code in a specific way.

Coming from XNA to C++/DirectX11 might occasionally be confusing. Once source of that confusion can be in ‘when’ and ‘how’ matrices are used, namely row major vs column major representations and row-vector vs column-vector mathematics. Shortly, in a row major representation, the matrix is represented as an array of rows in memory; while in the column major, the matrix in represented as an array of columns in memory. There’s also the difference between row-vector math and column-vector math, which affects how transformation matrices are represented, and the correct order in which various operations should be made. For more details on the mathematical aspects, see this and this. A nice analysis of all these four aspects, which I totally recommend anyone to read,  can be found here: Row major vs. column major, row vectors vs. column vectors.

The problem

For OpenGL and GLSL users, it’s been quite clear for some time now: column major representation and column vectors.

For DirectX, it’s not always that simple, and here are some reasons why:

  • The old D3D9 fixed function pipeline worked with row-major matrices and row-vector math
  • HLSL by default uses column-major matrices and row-vector math (to be fair, if you use mul(M,v) v is treated as a column vector, so I think you could end up using column-major and colum-vector math, if you wanted to, so it’s more fair to say that HLSL uses whatever vector math you want it to).
  • DirectXMath uses row-major matrices and row-vector math.
  • The old D3DX Effects framework (and the XNA Effects framework), took care to transform the row-major matrices used by D3DXMath and Xna.Framework.Math into column-major matrices before passing them to shaders. This was done transposing the matrices silently, behind the scenes, so you didn’t have to worry about that.
  • BasicMath.h (commonly found in the Windows 8 and Windows Phone 8 samples) tried to mimic HLSL in CPU code, and thus wants to use column-major matrices (so it’s easy to copy them directly to HLSL without transposition). But on the math side, I see it as a royal pain in the ass, as it seems to require me to think in column-vector math with regards to the order of multiplication of transforms. So either I’m too thick to properly understand how I should work with BasicMath, or something in it’s implementation is not quite right.

So, what is the way to deal with all of this? Thankfully, HLSL gives us a helping hand, since it knows how to handle both row-major and column-major matrix packing. The only place this matters is that constant buffers, where you pass the matrices from the CPU to the shader. By default, HLSL assumes you provide these matrices in column-major. To change this behavior, you have three options:

Making a choice

To make your life easier, and to avoid confusion, you should make a choice as early as possible, and stick with it. There’s no right or wrong choice, but I’ll make some comments about advantages and disadvantages of each options, to justify my own choice. Let’s see which are the options:

  • BasicMath.h – Found in almost any Win8/WP8 sample from MSDN. mimics HLSL in syntax, but it’s really basic. You don’t need to transpose the matrices before sending then to HLSL, but it requires you to work with different multiplication order than HLSL
  • OpenGL Mathematics – Works in column-major and column-row. You’ll need to either transpose matrices before sending them to HLSL, or use the column-row version of mul() in your HLSL code. But otherwise, it is a full-fledged math library, that has all functionality you’ll need.
  • DirectXMath + transpose matrices – part of the Platform SDK. It’s a great math library, with support for SIMD. It works with row-vector math and uses row-major representations, so all you need to do is transpose the matrices before sending them to the shader
  • DirectXMath + change HLSL matrix layout – just use DirectXMath normally, but instead of transposing all matrices that you send to the shaders, set the layout for matrix parameters as row_major, using one of the methods explained previously.

I’m sure there are many other options, but these are the ones that I focused on. At AmusedSloth, we decided to use glm (OpenGL Mathematics), since it’s portable, and works out of the box on all platforms we want to target. But for my samples/tutorials/spare-time projects, I wanted to use something as close as possible to how things worked in XNA (row-vector math), which is why I’ll be using one of the last two options in my samples from now on. For simplicity and clarity, I’ll likely change the matrix layout in HLSL instead of transposing all matrices most of the time, so my choice for future samples on this site is: DirectXMath + change HLSL matrix layout.

Edit: Effects11

Meanwhile I found out (thanks Travis) that the updated Effects Framework for Direct3D11 was released recently as shared-source. However, this is for use in Win32 desktop applications, and was provided to help porting apps. So this will (just like the previous Effect frameworks) transpose the matrices automatically for you when needed, but it won’t be useable on WinRT or Windows Phone

  • RabidLionGames

    You probably already know this, but the updated Effects framework is available as source from Chuck Walbourn’s blog (http://blogs.msdn.com/b/chuckw/archive/2012/10/24/effects-for-direct3d-11-update.aspx) which still transposes the matrices behind the scenes, for people who want to carry on using the Effects API they’re familiar with.

  • Marc

    Good post!

    One question, though: why do you say you must transpose matrices from OpenGL Mathematics to HLSL if both of them work on column-major order? (And as you said HLSL treat vectors as column-vectors with the mul() function).

    Thank you!

  • Catalin Zima

    Hi Marc, you’re right indeed.
    I just tested this in production code, and yes, if I use the clumn-row version of mul, I don’t have to transpose the GLM matrices anymore. I’ll update the article, I don’t know how I missed that.

    Thanks!

  • Catalin Zima

    @Travis, no, I wasn’t aware of Chucks updated Effects framework.
    Thanks, I’ll add that in, and might decide to use it in my own samples :)

  • Marc


    Catalin Zima:

    Hi Marc, you’re right indeed.
    I just tested this in production code, and yes, if I use the clumn-row version of mul, I don’t have to transpose the GLM matrices anymore. I’ll update the article, I don’t know how I missed that.
    Thanks!

    Good to know! I was planning to use GLM in a little OpenGL 4.x/Direct3D 11 engine.

  • Pingback: Catalin's Game Development Blog » Handling Orientation in a Windows Phone 8 game

  • http://hocdautu.edu.vn/loi-nhuan-lao-doc-ngan-hang-giam-luong-va-nhan-su/ hoc forex

    Great put up, very informative. I ponder why the other experts of this sector don’t understand this. You should continue your writing. I’m sure, you’ve a huge readers’ base already!|What’s Happening i’m new to this, I stumbled upon this I’ve discovered It absolutely useful and it has aided me out loads. I am hoping to contribute & help different customers like its aided me. Great job.

  • Nicolas

    The DirectXMath library uses the exact same layout as OpenGL. In order to use it with shaders without transposing, or specifying “row-major” parameters. All you need to do is specify mul(m, v).

    Proof:

    In order to determine whether a library is compatible with your conventions, all you need to look at is how a translation is stored in memory.

    A matrix can be stored as row major or column major and can be interpreted as row major or column major(giving 4 possible ways to look at them. Ignore all interpretations and consider a 4×4 matrix as 16 consecutive floats. Say float m[16]. If you store the translation transformation in m[12],m[13],m[14] then you are using the exact same memory layout as OpenGL.

    If you look at the source code for XMMatrixTranslation you will see that the x,y, and z offsets for translation are beign stored in these very slots. DirectXMath uses the exact same memory layout as OpenGL does for its matrices.

    Thus another alternative to transposing matricies, or specifying “row_major” , you can simply do matrix multiplication in your shaders as mul(m, v).

    I am working on my own math library, because I need one that handles both floats and doubles, but that will interoperate with other math libraries nicely. Picking the correct memory layout for my matrices is key to making it work nicely with DirectXMath and OpenGL Mathematics. I have decided to stick with column major ordering which means whenever I look at the code for my matrices its always m[column][row]. It definitely takes some getting used to reading the code this way but this maximizes compatibility of my library while having no performance overheard. Transposing all matrices sent to shaders is not an option as I don’t’ want to waste the CPU cycles.

  • http://oconnell.ednet.ns.ca/moodle/user/view.php?id=36656&course=1 how to make money trading options

    Right here is the perfect website for anyone who hopes to understand this topic.
    You know so much its almost tough to argue with you (not that I personally would want to…HaHa).
    You definitely put a fresh spin on a subject which has been discussed
    for ages. Excellent stuff, just wonderful!

  • Connie Cartjer

    Wonderful! So simple. I had a good experience filling forms online and happy to share it with you. You will be surprised how easy it can be to fill forms. Try fillingl UK Soho House Annual Member Application through the online sowtware https://goo.gl/Bl1psc