Reverse projection

Started by
2 comments, last by StanLee 12 years ago
Hello!

I am stuck on a math problem at the moment. I want to project 2D screen coordinates to a plane in 3D view space which is parallel to the view plane and has got a particular distance.
Let's suppose I have got a 2D point P[sub]2[/sub](x, y) on my view plane. My resolution is 800x600, so my x is ranging from 0 to 800 and my y from 0 to 600. I define a distance d[sub]z[/sub] which the plane in 3D view space and the view plane should have. Now I want to estimate the the 3D point P[sub]3[/sub] (x', y', z') in view space on that specific plane with distance d[sub]z[/sub] to my view plane.

A projection matrix is given:
XMMATRIX projMatrix = XMMatrixPerspectiveFovLH(XM_PIDIV4, (float)(screenWidth) / (float)(screenHeight), 0.1f, 1000.0f);

The camera position vector camPos, the lookTo-vector and the up-vector are also given.

How do I estimate P[sub]3[/sub] now? I tried to calculate the differen coordinates like that:
z' = camPos + lookTo*d[sub]z[/sub]
x' = z' + cross(lookTo, up)*x
y = z' + up*y

The problem is, that due to perspective projection the plane in view space is bigger than the view plane itself and it gets bigger the bigger the distance d[sub]z[/sub] is between them. Thus when I got a big distance between those planes the projected plane on the on the view plane is very small and when I move my cursor to the right top corner to draw something for example it gets drawn somewhere in the middle of the screen.

I have to scale the x and y values somehow but I don't know how. Is there maybe another approach to solve this?
Advertisement
Not exactly sure if this covers what you want but maybe it would work if you...

Travel down the 4 corners of your near plane in the view frustum a distance, d. This would provide you the width and height of your new plane. Then you could define a new frustum using those new dimensions and render to that.

The view frustum is a truncated pyramid (in perspective projection) so expect your width and height to increase.
Yeah, this would be a great approach, but how do I estimate those 4 vectors and the 4 corners? I am aware that I can extract the 6 different planes which define the view frustum out of the projection matrix and thus just calculate their intersection line. But it seems quite elaborate and I am still missing those 4 corner points in 3D view space.
Ok, now finally found a solution to solve this. I just post my approach so that everyone who has got the same problem can take this in consideration. ;)

I took a look at the initialization of my projection matrix. There i defined the field of view in angle, to be precisely it was PI/4 = 0.7853... defined by XM_PIDIV4:
XMMATRIX projMatrix = XMMatrixPerspectiveFovLH(XM_PIDIV4, (float)(screenWidth) / (float)(screenHeight), 0.01f, 100.0f);

As far as I have understood the perspective projection this value defines the field of view in radian angle in horizontal and vertical direction. So everytime I rotate my camera the field of view is streched around my lookTo vector by the angle XM_PIDIV4 in radian. Therefore my lookTo vector intersects with the center of every plane wich lies in the frustum and is parallel to the view plane.
With those information I did the following:
I took my up and right vector of my view space and generated rotation matrices which rotate around each of them with the angle XM_PIDV/2.0, just the half of my field of view:
XMMATRIX rotY = XMMatrixRotationAxis(up, XM_PIDIV4/1.3);
XMMATRIX rotX = XMMatrixRotationAxis(right, XM_PIDIV4/2.);
XMMATRIX rot_Y = XMMatrixRotationAxis(up, -XM_PIDIV4/1.3); //other direction
XMMATRIX rot_X = XMMatrixRotationAxis(right, -XM_PIDIV4/2.); //other direction


With those rotation matrices I took my lookTo vector and rotated him in such a way that I get all the four vectors which define the corners of my frustum:
XMVECTOR topleft, topright, bottomleft, bottomright;
topright = XMVector3Transform(lookTo, rotY);
topright = XMVector3Transform(topright, rot_X);
topleft = XMVector3Transform(lookTo, rot_Y);
topleft = XMVector3Transform(topleft, rot_X);
bottomright = XMVector3Transform(lookTo, rotY);
bottomright = XMVector3Transform(bottomright, rotX);
bottomleft = XMVector3Transform(lookTo, rot_Y);
bottomleft = XMVector3Transform(bottomleft, rotX);


Now it was possible to travel down on those vectors to get to the new corners of the plane which is parallel to the view plane. I used the mouse wheel to define the distance between both planes:
topright *= mouseZabsolute;
topleft *= mouseZabsolute;
bottomright *= mouseZabsolute;
bottomleft *= mouseZabsolute;


I haven't normalized the different vectors before, because my lookTo vector was already normalized.

In the next step I defined the right and up vector of the plane, so that their magnitudes are the width and height of the plane:
right = topleft - topright;
up = topleft - bottomleft;


Then I normalized the mouse coordinates which are in screen space
float normMouseX = (float)(-mouseXabsolute)/((float)(_screenWidth));
float normMouseY = (float)(-mouseYabsolute)/((float)(_screenHeight));


and used them to move my cursor on that plane:
cursor += camPos + topleft+normMouseX*right+normMouseY*up;


There is still one thing which bothers me. The rotation around the up vector must be adjusted everytime the aspect ratio changes. I found out that when the aspect ratio is 1.333 the somehow optimal rotation angle in radian is XM_PIDIV4/1.7 and when the aspect ratio is 1.777 the angle is XM_PIDIV4/1.3 . I can't figure out for now how to adjust this divisor properly.

This topic is closed to new replies.

Advertisement