D3DXInverseMatrix() isn't 100% accurate...

Started by
6 comments, last by SGreth 19 years, 11 months ago
I'm not sure if anyone else has seen this but here is my scenario.

D3DXMATRIX mInvView;
D3DXMATRIX mView = m_pCurrentCamera->GetViewMatrix();	
D3DXMatrixInverse( &mInvView, NULL, &mView );
In this example my view matrix is setup as so (camera is moved 20 on the z-axis). 1.0000000 0.00000000 0.00000000 0.00000000 0.00000000 1.0000000 0.00000000 0.00000000 0.00000000 0.00000000 1.0000000 0.00000000 0.00000000 0.00000000 20.000000 1.0000000 After passing this through the D3DXMatrixInverse() function I end up with a matrix like the following. 0.99999994 0.00000000 0.00000000 0.00000000 0.00000000 0.99999994 0.00000000 0.00000000 0.00000000 0.00000000 0.99999994 0.00000000 0.00000000 0.00000000 -19.999998 0.99999994 What's with the really bad rounding issues here? I noticed something similar with the D3DXVec3TransformCoord() function. D3DXVec3TransformNormal() on the otherhand doesn't seem to suffer from weird floating point oddities. Any insights would be greatly appreciated. Thanks, ~Rob
"The difference between insanity and genius is measured only by success."~Bruce Feirstein
Advertisement
I'd say that's just floating point resolution issues.

I can't remember the inverse-matrix maths/code off the top of my head, but it does involve a lot of floating point operations.

Try writing the algorithm yourself, but templating the value-type... then check the results between a single/double precision float, I'll be that these errors will either change/disappear accordingly.

Oh, and if I am correct - there's very little you can do about it, but given the minimal error you shouldn't notice it graphically (unless you're doing scientific simulation).

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

I figured it was FPU related as well. I'm actually using the function to cast a ray from screen-space to world-space for ray-picking, so accuracy is needed to avoid false hits & misses on the ray-test. NOTE: I did turn on the flag in the CreateDevice() call that makes Direct3D *not* turn off double-floating point precision.
"The difference between insanity and genius is measured only by success."~Bruce Feirstein
you must have pretty small objects / very high resolution for those differences to matter...

I've done a little bit of trivial ray-object picking, and I'd never of imagined that a +/- 1-pixel would make much odds.

Even if it technically did, is it really going to be noticeable to the user - they'll probably realise its the wrong object and click again.

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

My main reason for hunting down a fix is because when I switched from using D3DXVec3TransformCoord() to D3DXVec3TransformNormal() my floating-point rounding issues were resolved. I'm curious if DirectX has known rounding issues elsewhere (i.e. D3DXInverseMatrix) and how to avoid them. Inevitably if I know a bug exists, a user will find it and it'll end up in my bugzilla list sooner or later :)

"The difference between insanity and genius is measured only by success."~Bruce Feirstein
1) How are you displaying the values contained in the matrices?

If you're using watch in the debugger or displaying using something like the _RPT macros or your own sprintf/printf based system with %f as the float specifier, the values shown will be rounded so your 1.000000 may actually be something like (say) 1.00000001, and a value of 0.000000001f will display as 9.9999997e-010 in the debugger)


2) If the members of your view matrix were computed from other values such as a look at vector and an approximate up vector with some form of orthonormalization (e.g. Gram Schmidt or cross products) then it's extremely likely you'll have ever so slight fractional numerical error in there (remember floats aren't always "exact").


3) Alternatively if your view matrix has come from a package such as 3DS Max, particularly if it's been through any of the UI in Max, you'll also very often get similar numerical noise.


4) Are you storing any values as double anywhere in your code? including the implied doubles you'll encounter if you don't put an f or an F at the end of any constants and if you don't use the float form of C runtime library calls such as sqrt() [use sqrtf(), cosf() etc instead].
You should get a compiler warning any time most (but not all) conversions actually take place in your code. Where those conversions do take place, you'll get rounding which could once again cause noise in those lower bits of your mantissa.


5) There are various problems with extremely small fractional values creeping in to your numbers due to various earlier computations - one being less likihood there'll be an "exact" representation for the value in IEEE floating point; another being when you need to do things such as multiplication (which matrix inversion typically does a lot of when computing cofactors) where the error gets multiplied too!


6a) As a quick test I modified the "CreateDevice" tutorial in the SDK as follows (NB: without specifying D3DCREATE_FPU_PRESERVE, and this code follows the device creation):

    D3DXMATRIX a;    a._11=1.0f;     a._12=0.0f;     a._13=0.0f;     a._14=0.0f;    a._21=0.0f;     a._22=1.0f;     a._23=0.0f;     a._24=0.0f;    a._31=0.0f;     a._32=0.0f;     a._33=1.0f;     a._34=0.0f;    a._41=0.0f;     a._42=0.0f;     a._43=20.0f;    a._44=1.0f;    _RPT4(_CRT_WARN, "%f %f %f %f\n", a._11, a._12, a._13, a._14);    _RPT4(_CRT_WARN, "%f %f %f %f\n", a._21, a._22, a._23, a._24);    _RPT4(_CRT_WARN, "%f %f %f %f\n", a._31, a._32, a._33, a._34);    _RPT4(_CRT_WARN, "%f %f %f %f\n", a._41, a._42, a._43, a._44);    D3DXMATRIX b;    D3DXMatrixInverse(&b, NULL, &a);    _RPT4(_CRT_WARN, "%f %f %f %f\n", b._11, b._12, b._13, b._14);    _RPT4(_CRT_WARN, "%f %f %f %f\n", b._21, b._22, b._23, b._24);    _RPT4(_CRT_WARN, "%f %f %f %f\n", b._31, b._32, b._33, b._34);    _RPT4(_CRT_WARN, "%f %f %f %f\n", b._41, b._42, b._43, b._44);


6b) The output from the above code was as expected:
Direct3D9: (INFO) :Failed to create driver indexbuffer1.000000 0.000000 0.000000 0.0000000.000000 1.000000 0.000000 0.0000000.000000 0.000000 1.000000 0.0000000.000000 0.000000 20.000000 1.000000D3DX: (INFO) Using 3DNow Instructions1.000000 0.000000 0.000000 0.0000000.000000 1.000000 0.000000 0.0000000.000000 0.000000 1.000000 0.0000000.000000 0.000000 -20.000000 1.000000


6c) Since my original matrix is filled in with literal constants rather than being the result of some computation, it's much less likely there will be any numerical error, which is why I suspect your particular problem is being introduced elsewhere and I get the expected results!


7) How are you computing your view matrix?, and are you performing your own normalization of the input vectors?


8) View matrices are usually always "orthonormal" (in English, "they're usually formed purely from rotations and translations and never need to scale or skew"), because of this you can use a handy mathematical optimisation to find the inverse of the matrix which will be slightly more efficient AND slightly less susceptible to multiplication of fractional error than a general purpose inverse:

a) Transpose (i.e. swap rows and columns around) the top left 3x3 elements of the matrix

b) Invert the translation part of the matrix (i.e. tx=-tx, ty=-ty, tz=-tz)

Simon O'Connor | Technical Director (Newcastle) Lockwood Publishing | LinkedIn | Personal site

Simon,
Thanks for the reply. I tried using the _RPT4 macro and it looks as though the debugger was lying to me :)

Thanks for the interesting tip on speeding up my matrix inversion. You're correct on the assumption that I'm not planning on doing any scaling operations with the view matrix. I'll implement that now and see if I can get rid of the rest of the rounding errors that I'm seeing (very very negligable though...).

Thanks again for the help!
~Rob
Doh, sorry for the anony-post.
"The difference between insanity and genius is measured only by success."~Bruce Feirstein

This topic is closed to new replies.

Advertisement