Jump to content
  • Advertisement
Sign in to follow this  
Raeldor

Unprojecting from screen space to local space

This topic is 4863 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, 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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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'.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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)).

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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!

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!