Unprojecting from screen space to local space

Started by
19 comments, last by deffer 18 years, 8 months ago
Hi all, I am trying to write functions to unproject from screen space to local space like the D3DXVec3Unproject function. I have written a matrix class which includes an inverse function, and my own camera class whose projection and view matrices match exactly the ones returned from DirectX. However, the results of unprojecting screen space using the DirectX function is not the same as my unproject routine. My routine does the following... Put screen x and y into a 3d vector Transform vector using inverse of projection matrix Transform vector using inverse of view matrix Transform vector using inverse of world matrix Am I missing something here, or simply mis-understanding how unproject works? Thanks!
Advertisement
Remember, that after projecting, the x,y,z coordinates are divided by w, to make it == 1. Now, it is just a simple rescaling of the vector, so if it is done sooner, or later, it makes no big difference.
So, after:
Quote:Original post by Raeldor
Put screen x and y into a 3d vector
Transform vector using inverse of projection matrix


normalize your vector (by normalize, I mean (xyzw)/w).
(*Not sure that would be all, though*)

Myself, I don't use inversion at all I would rather transpose rotation component and negate translation. As for projection matrix, I use a specific class, to track it's base parameters.
Just a snippet:
class CProjectionController{   /////////////////////////////////   // Initializing.public:   void SetData(float fovAngleY, int sizeX, int sizeY);   void SetBorders(float fNear, float fFar);   /////////////////////////////////   // Base data.protected:   float  m_fFovAngleY;   int  m_nSizeX;   int  m_nSizeY;protected:   float  m_fNear;   float  m_fFar;   /////////////////////////////////   // Derived data.protected:   float  m_fAspectXY;   float  m_fTanX;   float  m_fTanY;   /////////////////////////////////   // Commands.public:   void ApplyPerspective(void) const;   // From mouse screen coordinates.   xvec GetRayDir(int x, int y) const;};void CProjectionController::SetData(float fovAngleY, int sizeX, int sizeY){   // Consume.   m_fFovAngleY = fovAngleY;   m_nSizeX = sizeX;   m_nSizeY = sizeY;   // Recompute.   m_fAspectXY = ((float)sizeX) / ((float)sizeY);   m_fTanY = tanf(fovAngleY/2);   m_fTanX = m_fTanY * m_fAspectXY;}xvec CProjectionController::GetRayDir(int x, int y) const{   // Screen coordinates to [-1, 1).   float xf = 2.0f * ((float)(x - (m_nSizeX/2))) / ((float)m_nSizeX);   float yf = 2.0f * -((float)(y - (m_nSizeY/2))) / ((float)m_nSizeY);   // To camera coordinates (on the Near plane).   float dx = xf * m_fNear * m_fTanX;   float dy = yf * m_fNear * m_fTanY;   return norm( xvec(dx, dy, m_fNear) );}void CProjectionController::ApplyPerspective(void) const{   glMatrixMode(GL_PROJECTION);   glLoadIdentity();   const float degsY = radsToDegs(m_fFovAngleY);   gluPerspective( degsY, m_fAspectXY, m_fNear, m_fFar);}


Now, it's for openGL, but it barely makes any difference. Only one function call.
Oops, I forgot I have...

	// first work out as a value between -1.0f and 1.0f	retVector.x=((in_vector.x/m_outputWidth)*2.0f)-1.0f;	retVector.y=((in_vector.y/m_outputHeight)*2.0f)-1.0f;


After putting my screen co-ordinates into the 3d vector, but before the transformations. Is this wrong?

I have tried...

	retVector.x=in_vector.x/m_outputWidth;	retVector.y=in_vector.y/m_outputHeight;


too.

Quote:Original post by deffer
Remember, that after projecting, the x,y,z coordinates are divided by w, to make it == 1. Now, it is just a simple rescaling of the vector, so if it is done sooner, or later, it makes no big difference.
So, after:
Quote:Original post by Raeldor
Put screen x and y into a 3d vector
Transform vector using inverse of projection matrix


normalize your vector (by normalize, I mean (xyzw)/w).
(*Not sure that would be all, though*)


Haven't I lost W? Also, I think because 'd' was 1, that it just divided x and y by z before projecting? And I definitely don't have 'z'.
Ok, let's assume you used:
D3DXMATRIX* WINAPI D3DXMatrixPerspectiveFovLH
( D3DXMATRIX *pOut, FLOAT fovy, FLOAT Aspect, FLOAT zn, FLOAT zf );
as the projecton matrix. There's no screen width/height mentioned, so for unprojecting you need XY in (-1,1)x(-1,1) with z=0 and w=1. So this you're doing right.

You should also use D3DXVec3TransformCoord, not D3DXVec3Transform, otherwise you would have to normalize (w->1) manually.

Test if this is right, without using other matrices for now (use identities). All step by step.
Quote:Original post by deffer
Ok, let's assume you used:
D3DXMATRIX* WINAPI D3DXMatrixPerspectiveFovLH
( D3DXMATRIX *pOut, FLOAT fovy, FLOAT Aspect, FLOAT zn, FLOAT zf );
as the projecton matrix. There's no screen width/height mentioned, so for unprojecting you need XY in (-1,1)x(-1,1) with z=0 and w=1. So this you're doing right.

You should also use D3DXVec3TransformCoord, not D3DXVec3Transform, otherwise you would have to normalize (w->1) manually.

Test if this is right, without using other matrices for now (use identities). All step by step.


The only way I can see to get my screen x and y in a range of -1 to 1 is to divide by the output width and height. How else would I do it?

Also, I am not sure what you mean by having to normalize w manually. Can you expand on this? I thought W was fixed at 1?

Thanks!
Oh, and also for DirectX should X and Y be in the range 0 to 1, not -1 to 1?
No no, you are transforming to (-1,1) the right way (look into my code, if you're not sure).

As for w, if it was fixed for 1, what purpose would it serve? For all non-affine transformations it is =1. For projection, it is not. Basically, for projection to perform, we need division. As matrix/vector operations consist only of addition and mulitplication, we are relying on w coordinate, which through multiplication by specific projection matrix gets some specific value (depending on xyz and the matrix, of course). Then, we have to explicitly make the division part by hand: x/=w; y/=w; y/=w; w/=w; (that's what I refer to normalization of a point (Note: it has nothing to do with vector normalization))

Now, normally you would have perform matrix/vecto transformation with D3DXVec3Transform (assuming you are using DX math classes). But there's a special version of that, that is doing also the dividing part: D3DXVec3TransformCoord. If you won't use this function, you will loose the w value after matrix multiplication (unless you're doing all the transformations by hand, or using 4D vectors (as I am)).
Quote:Original post by deffer
No no, you are transforming to (-1,1) the right way (look into my code, if you're not sure).

As for w, if it was fixed for 1, what purpose would it serve? For all non-affine transformations it is =1. For projection, it is not. Basically, for projection to perform, we need division. As matrix/vector operations consist only of addition and mulitplication, we are relying on w coordinate, which through multiplication by specific projection matrix gets some specific value (depending on xyz and the matrix, of course). Then, we have to explicitly make the division part by hand: x/=w; y/=w; y/=w; w/=w; (that's what I refer to normalization of a point (Note: it has nothing to do with vector normalization))

Now, normally you would have perform matrix/vecto transformation with D3DXVec3Transform (assuming you are using DX math classes). But there's a special version of that, that is doing also the dividing part: D3DXVec3TransformCoord. If you won't use this function, you will loose the w value after matrix multiplication (unless you're doing all the transformations by hand, or using 4D vectors (as I am)).


I am using 4d vectors, but not using the DirectX function, as I am trying to code my own. My problem is, after transforming the vector using the reverse projection matrix, I am getting w=0. The reason for that seems to be is because z=0, and only position _34 of column 4 of the inverse matrix has any value (a 1).
Ah crap. I think I may have just found the problem. My determinant and inverse functions of my matrix are only working on the first three columns and rows. I will do a little more research!

This topic is closed to new replies.

Advertisement