You can use the following function directly it returns a ray for your mouse coordinates in world space.
A ray of course has a position which should be usable for your purposes. As well as a direction the direction is a ray into the world from that point in the correspondingly correct direction into the world.
This works for any sort of matrix concation which is why all the matrices are passed as well.
/// Near plane is typically just 0 in this function or some extremely small value. Far plane cant be more then one and so i just folded it automatically. In truth this isnt the near plane its the min clip but whatever.
public Ray GetScreenVector2AsRayInto3dWorld(Vector2 screenPosition, Matrix projectionMatrix, Matrix viewMatrix, Matrix cameraWorld, float near, GraphicsDevice device)
//if (far > 1.0f) // this is actually a misnomer which caused me a headache this is supposed to be the max clip value not the far plane.
// throw new ArgumentException("Far Plane can't be more then 1f or this function will fail to work in many cases");
Vector3 nearScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, near); // must be more then zero.
Vector3 nearWorldPoint = Unproject(nearScreenPoint, projectionMatrix, viewMatrix, Matrix.Identity, device);
Vector3 farScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, 1f); // the projection matrice's far plane value.
Vector3 farWorldPoint = Unproject(farScreenPoint, projectionMatrix, viewMatrix, Matrix.Identity, device);
Vector3 worldRaysNormal = Vector3.Normalize((farWorldPoint + nearWorldPoint) - nearWorldPoint);
return new Ray(nearWorldPoint, worldRaysNormal);
/// Note the source position internally expects a Vector3 with a z value.
/// That Z value can Not excced 1.0f or the function will error. I leave it as is for future advanced depth selection functionality which should be apparent.
public Vector3 Unproject(Vector3 position, Matrix projection, Matrix view, Matrix world, GraphicsDevice gd)
if (position.Z > gd.Viewport.MaxDepth)
throw new Exception("Source Z must be less than MaxDepth ");
Matrix wvp = Matrix.Multiply(view, projection);
Matrix inv = Matrix.Invert(wvp);
Vector3 clipSpace = position;
clipSpace.X = (((position.X - gd.Viewport.X) / ((float)gd.Viewport.Width)) * 2f) - 1f;
clipSpace.Y = -((((position.Y - gd.Viewport.Y) / ((float)gd.Viewport.Height)) * 2f) - 1f);
clipSpace.Z = (position.Z - gd.Viewport.MinDepth) / (gd.Viewport.MaxDepth - gd.Viewport.MinDepth); // >> Oo <<
Vector3 invsrc = Vector3.Transform(clipSpace, inv);
float a = (((clipSpace.X * inv.M14) + (clipSpace.Y * inv.M24)) + (clipSpace.Z * inv.M34)) + inv.M44;
return invsrc / a;
I can only tell you i have tested the method i posted in 3d with a free camera and a billboarded object that changed shape when i put the mouse over it i tested it all directions and at distances as well as when objects were behind the camera.
I debugged out the problems i had with it and the notes to that still remain. The ray is basically both the position and the direction into the world and so it gives more information then just a position anyways.
This is a part of my utility class methods now.
You can look to the unproject function to see how it inverts which is actually right out of the matrix class i may have minorly altered it just to make it clearer when i was debugging a problem with my function.
If you want to create your own basic Effect that matches spritebatch and then pass that to spriteBatch.Begin You can see the equivalent matrices and how to make them and pass them to spritebatch begin, in the below post were i put a full example that can be copy pasted.
This also includes a second example of how to make your own custom effect that matches spritebatch matrice setup.
Edit sorry that might not be the best example. If you look at set states it shows how to set up a basic effect though in the actual draw using spritebatch you add the basic effect to begin.
Although it did mess up my sprite layerDepth. Am I correct in saying that the layerDepth must be a value between 0 and 1? When I set the scale.z to 1 all of my sprites disappeared. I found that I can either set the scale.z to a really low value (0.0000001f) or divide my layerDepth by a large value (2000000). Before implementing your fix my layerDepth was set to the sprites Y position. Is this the correct way to do this?
That’s correct; layerDepth must be between 0 and 1 if you’re using a FrontToBack or BackToFront sorting mode. If the depth is out of this range, a sprite may not render. A sprite’s Y position will be outside of that range fairly quickly, so for the depth, divide the Y by a value you don’t think it’ll ever go over.
The method above gives me inaccurate results (the ray direction seems a bit offset). I came up with the following code using the Unproject method of the GraphicsDevice.Viewport and the result is perfect. I also added the groundplane-ray-intersection stuff.