TIGSource Forums

Developer => Technical => Topic started by: kamac on November 05, 2013, 06:20:28 AM



Title: Projecting 2D screen-space coords to 3D space [finding a ray]
Post by: kamac on November 05, 2013, 06:20:28 AM
Hey.

I couldn't find proper answer in them webs (Everybody suggest using math libs / etc. for these kind of things), so I'd like to ask here.

Assuming I've:
-View width, height
-2D click position (coords range from 0 to view_width or view_height for Y)
-View matrix
-Projection matrix

How do I get:
-Ray origin (I suppose I could just put camera XYZ position here)
-Ray direction (I suppose that would be a normalized vector which "begins" in origin)

Could somebody provide me explanation?


Title: Re: Projecting 2D screen-space coords to 3D space [finding a ray]
Post by: soryy708 on November 05, 2013, 06:38:22 AM
You know that the "camera" is a frustum (http://techpubs.sgi.com/library/dynaweb_docs/0630/SGI_Developer/books/Perf_PG/sgi_html/figures/04.3.frustum.gif), right?
Well you have the "eyepoint" (tip of the frustum).

You need math to find out in which vector do you need to send a ray from tip for it to pass through the point generated from the click X/Y co-ordinates.


Title: Re: Projecting 2D screen-space coords to 3D space [finding a ray]
Post by: kamac on November 05, 2013, 06:48:22 AM
Quote
You know that the "camera" is a frustum, right?
Well you have the "eyepoint" (tip of the frustum).

Well, yes. That's why I wrote this:

Quote
-Ray origin (I suppose I could just put camera XYZ position here)

Quote
You need math to find out in which vector do you need to send a ray from tip for it to pass through the point generated from the click X/Y co-ordinates.

That's the point of my question. I don't know how to do just that.
I read that one could multiply screen-space coords by inverted projection, then multiply *something* by inverted view matrix - I don't know the details.


Title: Re: Projecting 2D screen-space coords to 3D space [finding a ray]
Post by: rundown on November 05, 2013, 08:00:48 AM
If the game is a topdown game you could do this easy with trigoniometrics.


Title: Re: Projecting 2D screen-space coords to 3D space [finding a ray]
Post by: wbahnassi on November 05, 2013, 08:06:09 AM
Ok, here is some explanation:

First, you have to know that there is missing information in the 2D coordinate (the z coordinate obviously :durr:). So you must think about how to restore this value. Under your context, you want a ray shooting from the screen pixel and into the scene. For common 2D picking, you can assume that the depth of the pixel is 0 (falls on the near camera plane). So now you have a clean 3D coordinate that can be fed into the inverted view-projection matrix.

Second, screen coordinates are not what you get when you transform a 3D coordinate by your view/projection matrix. Hence, you cannot feed a screen coordinate into the inverted view/projection matrix and expect a correct result. The output of view/projection transformation is in "clip space", which goes [-1,1] with Y+ going up not down. This means you have to convert your screen coordinate into this "clip space" and _then_ feed the value to the inverted view/projection matrix. This will give you back a 3D coordinate in world space.

And now to the code. This is the Unproject function from the DSK3D engine (my engine):

Code:
struct VIEWPORT
{
UINT16 uLeft,uTop,uRight,uBottom;
float fMinZ,fMaxZ;

UINT16 Width(void) const;
UINT16 Height(void) const;
float Depth(void) const;
DSK::VEC3 Unproject(const DSK::VEC3& vScreenPos,const DSK::MAT44& matXViewProjInv) const;
};

VEC3 VIEWPORT::Unproject(const VEC3& vScreenPos,const MAT44& matXViewProjInv) const
{
return matXViewProjInv.XFormProjective(VEC3(
(vScreenPos.x-uLeft) / Width() * 2.0f - 1.0f,
-((vScreenPos.y-uTop) / Height() * 2.0f - 1.0f),
(vScreenPos.z-fMinZ) / Depth()));
}

You can call it like so:

Code:
// Replace 640,480 by any screen coordinate you like
VEC3 vRayPos1 = viewport.Unproject(VEC3(640,480,0), matViewProjInverted);
VEC3 vRayPos2 = viewport.Unproject(VEC3(640,480,1), matViewProjInverted);
VEC3 vRayDir = (vRayPos1-vRayPos2).Normalize();

// vRayPos1 is the origin, and vRayDir is the direction

The function MAT44::XFormProjective() does a regular transformation of a (vector3,1) which results in a vector4, then it returns the xyz divided by the w as a vector3.

This should get you going :)


Title: Re: Projecting 2D screen-space coords to 3D space [finding a ray]
Post by: kamac on November 05, 2013, 10:32:50 AM
In the end, I found the solution following this guys' explanation on stackoverflow:
http://stackoverflow.com/questions/19485046/2d-screen-coordinate-to-3d-position-directx-9-box-select

:durr: