Ray-Pick

Started by
7 comments, last by DrGUI 17 years, 4 months ago
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!
Advertisement
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.
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.

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
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...)
Quote:Original post by jyk
Also, doesn't DX already have an 'unproject' function? (I can't remember for sure...)

It certainly does. D3DXVec3Unproject was already mentioned in this thread.

Regards
Admiral
Ring3 Circus - Diary of a programmer, journal of a hacker.
Quote:Original post by TheAdmiral
It certainly does. D3DXVec3Unproject was already mentioned in this thread.
So it was - my apologies.
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
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!

This topic is closed to new replies.

Advertisement