Sign in to follow this  

Ray-Pick

This topic is 4032 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

Hey guys, this question is about ray-picking - I have it working as I have described below but I want an explanation of why Dunlop's method works and mine doesn't if you could... thanks! So at this stage I have converted the screen-space coordinate into a projection-space coordinate. My first approach was to set the ray origin as the camera position and unproject the ray direction from projection space to world-space by multiplying it with the inverse of the view-projection concatenation. The ray origin worked; however, I could not get the ray direction to be directly under the mouse (having drawn a debug line to see where the ray was being projected). I searched for ray picking using Google and found an article by Robert Dunlop, a Microsoft MVP (http://www.mvps.org/directx/articles/improved_ray_picking.htm); the sample code was creating a ray under the mouse position. The difference was in transforming from projection space to view space – I multiplied by the inverse projection while he just divided the point’s (x, y) coordinate by the (1,1) and (2,2) projection matrix elements respectively. He then multiplied the projected point by the inverse of the view matrix, as I did, to give the ray direction. So why does my method not work and how does Dunlop's work? Many thanks guys!

Share this post


Link to post
Share on other sites
Personally, I'd recommend sticking with the matrix-based approach. It's more general and will handle different types of projections uniformly.

I can't tell you why your implementation of this method isn't working, but concatenation of transforms in an incorrect order is a common error. Are you sure you're combining the view and projection transforms in the right order?

Also, the typical 'unprojection' algorithm involves a division by w, so perhaps you're missing that step.

Share this post


Link to post
Share on other sites
Could also be that the direction of your ray is not correct. In case it helps, my code for picking includes something like this:


D3DXVECTOR3 inP1(mouseX, mouseY, viewport.MinZ);
D3DXVec3Unproject(&outP1, &inP1, &viewport, &projMatrix, &viewMatrix, &worldMatrix);
D3DXVECTOR3 inP2(mouseX, mouseY, viewport.MaxZ);
D3DXVec3Unproject(&outP2, &inP2, &viewport, &projMatrix, &viewMatrix, &worldMatrix);



The ray direction to intersect with geometry is then outP2-outP1.

Share this post


Link to post
Share on other sites

Thanks guys, I'll try the matrix method again when I can.

Quote:

...unproject the ray direction from projection space to world-space by multiplying it with the inverse of the view-projection concatenation...

I know in normal maths matrices, (MN)^-1 = N^-1 M^-1, but with DX using left-hand matrices, do you think I took the inverse wrongly?

Cheers

Share this post


Link to post
Share on other sites
Quote:
Original post by DrGUI

Thanks guys, I'll try the matrix method again when I can.

Quote:

...unproject the ray direction from projection space to world-space by multiplying it with the inverse of the view-projection concatenation...

I know in normal maths matrices, (MN)^-1 = N^-1 M^-1, but with DX using left-hand matrices, do you think I took the inverse wrongly?
Handedness has nothing to do with it; the issue is whether the matrices are intended for use with row vectors or column vectors.

In DX it's the former, so the 'forward' transformation would be:
v' = v*V*P
Therefore the unprojection transformation would be:
v' = v*(V*P)^-1 = v*P^-1*V^-1
Where V is the view matrix and P is the projection matrix.

Also, doesn't DX already have an 'unproject' function? (I can't remember for sure...)

Share this post


Link to post
Share on other sites
Thanks guys

So the following was the original code using Dunlop's algorithm:


private Vector3 PickRayFromScreen(Vector2 screenCoordinate, Direct3D.Viewport wholeViewport, out Vector3 rayDirection)
{
//Convert the point's coords from screen space (0 to wholeViewport width/height) to proj space (-1 to 1)
Vector3 projPoint = new Vector3((screenCoordinate.X - wholeViewport.X) / wholeViewport.Width * 2 - 1, -((screenCoordinate.Y - wholeViewport.Y) / wholeViewport.Height * 2 - 1), 1);

projPoint.X /= m_RenderingCameraProxy.Projection.M11;
projPoint.Y /= m_RenderingCameraProxy.Projection.M22;
Matrix viewInverse = Matrix.Invert(m_RenderingCameraProxy.View);
Vector3 rayOrigin;
// Transform the screen space pick ray into 3D space
rayDirection.X = projPoint.X * viewInverse.M11 + projPoint.Y * viewInverse.M21 + projPoint.Z * viewInverse.M31;
rayDirection.Y = projPoint.X * viewInverse.M12 + projPoint.Y * viewInverse.M22 + projPoint.Z * viewInverse.M32;
rayDirection.Z = projPoint.X * viewInverse.M13 + projPoint.Y * viewInverse.M23 + projPoint.Z * viewInverse.M33;
rayDirection.Normalize();
rayOrigin.X = viewInverse.M41;
rayOrigin.Y = viewInverse.M42;
rayOrigin.Z = viewInverse.M43;
rayOrigin += rayDirection * m_RenderingCameraProxy.Viewport.CameraProjection.ZNear;

return rayOrigin;
}



but even leaving the ray origin unchanged and at the last minute saying
rayDirection = normalize( point in projection space * (V * P)^-1 )

or in code
rayDirection = Vector3.TransformNormal(projPoint, Matrix.Invert(Matrix.Multiply(m_RenderingCameraProxy.View, m_RenderingCameraProxy.Projection)));
rayDirection.Normalize();

seems to make the direction point from the ray origin to (0,0), whichever direction the camera is facing.


I'll have a go at Unproject now, but I really wanted to do it myself.

Cheers

Share this post


Link to post
Share on other sites
Unproject works:


private Vector3 PickRayFromScreen(Vector2 screenCoordinate, Direct3D.Viewport wholeViewport, out Vector3 rayDirection)
{
Vector3 rayOrigin = Vector3.Unproject(new Vector3(screenCoordinate.X, screenCoordinate.Y, 0), wholeViewport, m_RenderingCameraProxy.Projection, m_RenderingCameraProxy.View, Matrix.Identity);
Vector3 rayEnd = Vector3.Unproject(new Vector3(screenCoordinate.X, screenCoordinate.Y, 1), wholeViewport, m_RenderingCameraProxy.Projection, m_RenderingCameraProxy.View, Matrix.Identity);
rayDirection = Vector3.Normalize(rayEnd - rayOrigin);
return rayOrigin;
}




However I would still like to know how it works!

Share this post


Link to post
Share on other sites

This topic is 4032 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this