Convert 3D to 2D screen and visa-versa

Started by
9 comments, last by dgcoventry 15 years, 5 months ago
I have a viewing screen in my 3D world such that the unit normal of it's plane is (0.6,-0.34,0.72) I want to project a 2D point on the screen onto the world coordinate system, with the WC Z value of 0. Say the 2D screen coordinates are (12,10), I want to know where a line passing through 12,10 and parallel to the vector (0.6,-0.34,0.72) strikes the plane which is defined by the normal (0,0,1) and the origin (0,0,0). Similarly, I want to take a point in the WCS, say (5,2,2) and find the line parallel to (0.6,-0.34,0.72) which passes through it, as this would give me the 2D coordinates of the point on the viewing screen. I'm not very familiar with vector maths, and matrices always had me flumoxed at college, but I'm ok with trig. I hope this makes sense.

Advertisement
some missing bits of information.

1. what is your camera position in WCS?

2. what is the 'tilt' of your camera in WCS? Basically, the other two vectors in WCS that define the direction of the width and height) of your camera screen.

3. what is the field of view of your camera?

4. what is the distance of your camera origin from the camera near plane?

from these informations, you can reconstruct the position of opint (12, 10) in your screen into WCS, then its simply a ray-plane intersection test.

similarly, you can apply the same principle backward for finding the screen position of point (5, 2, 2), by computing the equation of your near plane in WCS and do another ray equation.

Everything is better with Metal.

Hi oliii,

Thanks for taking the time.

The Camera position is not important because I'm using orthographic views. ie. all points on the screen are projected onto the X-Y Plane of the World Coordinate System using a line parallel to the normal of the screen.

So, if I have a point (12,10) on the screen plane (unit normal of [0.6,-0.34,0.72]), I need the intersection of the line and the X-Y plane.

I have found a wiki which appears to do what I want:

http://en.wikipedia.org/wiki/Line-plane_intersection

However, I'm a bit thick and I can't seem to follow the terminology or the symbols that are employed (is it me, or does every mathematician on the web use symbols unique to himself?).

I guess I'm not helping myself here.

My 2D screen coordinate is measured against the screen plane (with a zero z-coordinate) so it needs to be translated to the WCS.

So my (12,10) coordinate needs to be given a Z coordinate of 0 which will put it on the X-Y plane. Then it needs to be rotated by the screen plane's normal so that it falls on the screen plane. It is *this* point (along with the normal vector) which defines the line.

However, I still need to find where this line intersects the X-Y Plane.

if you are using OpenGL, gluUnproject() and gluProject() will do this automatically for you, using the modelview and projection matrix, wether it is a orthographic projection, or a perspective projection.

In you case, you need to consider the resolution of your screen in pixels, to be able to map a point on screen into wcs.

for the actual ray-plane intersection routine, you can look at this.

All you need is to convert point (12, 10, 0) into a near plane point in WCS, and apply the ray tacing test.

if you draw the process on paper, it will make it easier to work out the mappings of your screen corners (pixels(0, 0) and pixels(screen_width, screen_height)) in WCS. once you have that, you can work out the position of pixel (12, 10) in WCS using simple linear interpolation.

Once you have pixel(12, 10) in WCS, and the direction or your camera (vector(0.6, -0.34, 0.72)), you can cast a ray.

If you do not want to use matrices, you'll have to use basic vector re-normalisation.

i.e. Your camera is made of three orthonormal vectors right, up, dir. dir is known, right is known (vector(1, 0, 0)). the up vector will not necessary be vector(0, 1, 0) or it will violate the orthonormal properties of your camera.

In short, your camera view will be squewed. So you need to make the up vector perpendicular to the other two vector.

=>
right = vector(1, 0, 0); // that is fixed.
dir.normalise();
up = dir x right;
up.normalise();
right = up x dir;
right.normalise();

now your camera has an orthonormal view. You know which way the vertical axis of your screen goes (the up direction), and which way the horizontal axis your you screen goes (the right direction) in WCS.

Quote:
The Camera position is not important because I'm using orthographic views. ie. all points on the screen are projected onto the X-Y Plane of the World Coordinate System using a line parallel to the normal of the screen.


Consider your screen view area as a rectangular 'beam' in WCS. The beam still needs to be positioned in WCS to capture what the camera sees. As you move around, looking at the world, the beam will also translates accordingly.

Now, you need to know how much of the world your camera sees. It's like a zoom function. To be able to adjust the area seen by your camera, you need to specify what extents your screen has in WCS (or how thick the 'beam' is).

thirdly, to be able to map a pixel to somewhere inside the beam, you need to know your screen resolution.

so your camera will need...

Vector right;Vector up;Vector dir;Vector position;float  screen_width;float  screen_height;float  wcs_width;float  wcs_height;Vector screenToWorld(float x, float y){    Vector p;    p = position;    p +=  right * ((x / screen_width) - 0.5f)  * wcs_width;    p +=  up    * ((y / screen_height) - 0.5f) * wcs_height;    return p;}Vector worldToScreen(Vector p){      Vector s;    s.x = ((p - position).dotProduct(right) / wcs_width) + 0.5f;    s.y = ((p - position).dotProduct(up) / wcs_height) + 0.5f;    s.z = ((p - position).dotProduct(dir)); // distance from camera    return s;}


once you have that point in WCS, cast a ray from it towards the plane.

Everything is better with Metal.

Hi Oliii,

Yes, I saw Zyrolasting's thread, but only subsequent to me starting this one. Sorry.

It seems as though he is also battling to understand the different terms, symbols and nomenclature of the professional mathematician in the same way I am.

Regarding matrices, I am happy to apply matrices to my problem, as long as I understand what they are doing.

Okay, so here goes:

My Viewport has its bottom left corner at the origin of the WCS, but it is 12 units wide by 10 units high and it opens a window onto the WCS with the bottom left corner at the origin and the top-right corner at 240,200 units. So the scale factor is 0.05 (which accounts for the 'zoom function' you mention).

Therefore point 'A' (screen coords 6,5) would translate to 120,100 in the World Coordinate System.

But that's where I hit my first snag.

The vector (0.64,0.21,-0.43) gives the angle alpha in the XY plane which is -29.5388 degrees.

So, if I rotate (120,100,0) around the Z axis I get:

x1=Ax*COS(alpha)-Ay*SIN(alpha)=153.7
y1=Ax*SIN(alpha)-Ay*COS(alpha)=-146.16
z1=0

Theta, which is the angle of the vector (0.64,0.21,-0.43) in the XZ plane is 49.992 degrees.

Rotating the point (153.7,-146.16,0) around the Y axis by theta I get:
x2=x1*COS(theta)=98.82
y2=y1=-146.16
z2=-x1*SIN(theta)=-117.73

Which doesn't appear to be where I expect it to be which is in the centre of the viewport.

So basically, I'm falling over at the first hurdle, which is to translate the point on the screen into World Coordinates so that I can project it onto the XY Plane.

Can anyone see what I'm doing wrong?

I'd say the first thing to understand is that a Matrix * Vector => Transformed Vector. and Transformed Vector * InverseMatrix => Origional Vector.
The modelview matrix give you your orientations and positions, and your projection matrix gives you your field of view perspective.

To transform a world point to screen space all you have to do is multiply the vector representing that location relative to the origin by your modelview*projection matrix.

To transform a screen space point to world space you need to take a vector pointing in the world at, transform it by the inverse of your modelview*projection matrix then intersect it with your world (ie the plane XY) to find the corrosponding world location.

oliii nicely provided the functions that do these transforms for you if you are in openGL.

The nice part about the matrix math is that there are no angles to play with. Which is good, because even if i did see what you were doing wrong with the trig, it would fall apart in different sectors and fail from odd things like the camera pointing straight down, and overall have a lot of edge cases you have to deal with.
Hi :-)
I don't know if this link can help you, but hopefully it can :-)

Perspective transform

cheers,
Paul
I've been working on this and this is where I have got to.

Free Image Hosting
fig 1.

Fig 1 shows my World Coordinate System (WCS).

Free Image Hosting
fig 2.

Fig 2 shows my view port which is 100 units x 80 units high. The four corners of the viewport (not marked on the figure) are Bottom-Left (0,0), Top-left (0,80), Top-right (100,80) and Bottom-right (100,0). Note that the Bl point is on the origin for convenience. If this is not the case, then the the viewport should be moved to make it so.

Free Image Hosting
fig 3.

Fig 3 shows that the view is not from directly overhead, but is tilted in 3 directions. The Normal to the viewport is given by the vector Zv (60.402300, -34.202000, 71.984600), which is drawn in blue.

Free Image Hosting
fig 4.

Fig 4 shows the viewport rotated about the X-axis and then rotated about the Y-Axis so that it is now a plane which is normal to the vector Zv (shown in blue) and which passes through the origin.

Free Image Hosting
fig 5.

First I have to rotate the points Top-right [Tr] and Top-left [Tl] about the X-axis. To do this I will look to the WCS from the left (fig 5).

The Vector Zv is 'flattened' into 2D and is shown as 'V', (-34.2020, 71.9846), with a length, c, given by Pythagoras sqrt(-34.202000 * -34.202000 + 71.984600 * 71.984600) = 79.6967.

Similar triangle mean that f/g = b/c. g = 80, the y-value of both Tl and Tr, so the rotated y-value of both points is f = 80 * 71.9846/79.6967, which is 72.2586.

Similarly e/g = a/c, so the z-value of both points is 80 * 34.2020/79.6967 = 34.3322

Note that the value of 'a' should be negative, but this works. If the y-value of the Normal was, positive, then the z-value of the rotated point would be negative, so I probably need this explained to me.

Free Image Hosting
fig 6.

The Vector Zv is 'flattened' into 2D in the Y-axis direction and is shown as 'V', (60.402300, 71.9846), with a length given by Pythagoras sqrt(60.4023* 60.4023 + 71.984600 * 71.984600) = 93.9693.

The angle by which the points are to be rotated (beta) is ARCSin(60.4023/93.9693) which is 0.6981 radians.

The Angle theta is arrived at by the following for the three points:

(In radians) theta = ARCSin(x-value/sqrt(x-value*x-value+z-value*z-value))

The final angle is theta + the angle to be rotated by, which is beta.

So the x-value of the rotated point would be:

Cos(beta + theta) * sqrt(x-value*x-value+z-value*z-value)

And the z-value would be:

Sin(beta + theta) * sqrt(x-value*x-value+z-value*z-value)

Bl is on the origin, so (0,0)
Tl is (x = 0, z = 34.3322), angle theta = 0,
new x = 22.0683
new y = 72.2586 (unchanged from rotation about X-axis)
new z = Cos(0.6981) * 34.3322 = 26.3
Tr is (x = 100, z = 34.3322), angle theta is 1.24 (from ARCSin(100/sqrt(10000 + 1178.7))
new x = Sin(0.6981 + 1.24) * 105.73 = 98.6727
new y = 72.2586 (unchanged from rotation about X-axis)
new z = Cos(0.6981 + 1.24) * 105.73 = -37.9788
Br is (x = 100, z = 0), angle theta is pi/2
new x = Sin(0.6981 + pi/2) * 100 = 76.6044
new y = 0 (unchanged from rotation about X-axis)
new z = Cos(0.6981 + pi/2) * 100 = -64.2788

So I'm part of the way there.

I now have my screen defined in World Coordinates. When the user clicks I can extrapolate the position of the mouse-click on the screen.

Now I need to find a formula to define a line which is defined by a vector (60.402300, -34.202000, 71.984600) which passes through the point extrapolated above.

And then I need to find a formula which will give me the intersection of that line and the X-Y plane, defined by the origin and the Z-axis (0,0,1)

This topic is closed to new replies.

Advertisement