Getting a mouse position in 3D

Started by
4 comments, last by ChristianFrantz 10 years, 10 months ago

I've looked at a few tutorials on picking and the Unproject method, so I have an idea of how this is supposed to work. My method does give me back a mouse position, it's just incredibly wrong. A position at 0, 0, 0 would end up to be something like 36, -1, 65.


 public Vector3 FindWhereClicked(MouseState ms)
    {
        Vector3 nearScreen = new Vector3(ms.X, ms.Y, 0);
        Vector3 farScreen = new Vector3(ms.X, ms.Y, 1);
        Vector3 nearWorld = device.Viewport.Unproject(nearScreen, cam.proj, cam.view, Matrix.Identity);
        Vector3 farWorld = device.Viewport.Unproject(farScreen, cam.proj, cam.view, Matrix.Identity);

        Vector3 direction = farWorld - nearWorld;

        float zFactor = -nearWorld.Y / direction.Y;

        Vector3 zeroWorldPoint = nearWorld + direction * zFactor;

        return zeroWorldPoint;
    }



Variables grabbed from the camera method


  public ThirdPersonCam()
    {
        proj = Matrix.CreatePerspectiveFieldOfView(0.78f, 1.7777f, 1f, 10000f);
    }

    public void CameraUpdate(Matrix objectToFollow)
    {
        Vector3 camPosition = objectToFollow.Translation + (objectToFollow.Backward * 10) + (objectToFollow.Up * 2);
        Vector3 camTarget = objectToFollow.Translation;

        view = Matrix.CreateLookAt(camPosition, camTarget, Vector3.Up);
    }

Is the problem coming from my camera? Or from my FindWhereClicked method?

If you see a post from me, you can safely assume its C# and XNA :)

Advertisement
Finding out where a mouse clicked is not a Vector3 result.

The best mouse result is a volume. Imagine four rays, one at each corner of the pixel, being projected to infinity. Or at least projected to your near and far planes.

An alternative mouse result is a ray through the middle of the pixel, or a line between the near and far plane. You basically have that in your code snippit above. It looks like you correctly computed the two intersections of the mouse point with the near and far plane. I'm not sure why you want to have near and far planes at z=0 and z=1. Seems like a mighty shallow world, but that's your call.

As soon as you computed nearWorld and farWorld your work is done. That is your line. That is the result. Yay!


After that, though, your code drifts to dangerous territory, with divide-by-zeros and INF/NAN propagation when direction.Y == 0. At that point it looks like you are trying to project on to a point on that line, but I'm not really sure why you would want to be doing that.

The code I have was taken from a tutorial I found lol. It looked alright in theory... So with my method I should be returning a ray instead of a Vector3?

If you see a post from me, you can safely assume its C# and XNA :)

Yes. When your mouse points at one pixel, in the 3d world it's pointing at it actually covers a line (or ray).

With the ray you received you can now calculate intersections with objects or planes to achieve what you probably really want: The first solid object under the mouse.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

The code I have was taken from a tutorial I found lol. It looked alright in theory... So with my method I should be returning a ray instead of a Vector3?

It can be, if that is what you need.

The point I was trying to make is that a mouse in 3D is not a point in space. A mouse in 3D is either a line or a volume.

That leads directly to a question: What are you going to do with the results?

Right now nearWorld and farWorld are at the projected origin and a point just past the projected origin. Does that meet your needs?


If you are going to use the results for a bunch of ray-volume intersection tests, then the pair of points will probably work for you. You can use them in conjunction with a spatial tree and mesh volume tests for an object picker, for example. It can get into expensive tests against meshes if you are looking for a first-hit case and objects can have holes. (Also this method of picking won't work too well when vertex and geometry shaders are in use, but that is beyond For Beginners.) So it may or may not be a good solution if that is your usage.

If you are going to use the results for a bunch of line-line intersection tests, that is probably bad because there is a slim chance they will overlap. In that case you probably want the volume that the pixel covers, and you will probably want to use the actual near and far planes rather than 0 and 1 past the mouse.

If your purpose is something else entirely, the results may or may not be adequate.

There's going to be a lot of things done eventually. Right now, I'm just trying to place blocks like you would in Minecraft, having them snap to a grid somehow on top of other blocks. But eventually the mouse will be used for a user interface and for the player. The player will be able to choose a spot on the map in front of them and fire a bolt or some kind of projectile. These all seem like things that could be done with a simple ray though. There won't be any clicking and dragging to select more than one object or anything like that. I think I'm pretty much just having the mouse intersect with points on the map is what I'm trying to get at. At those points, something will happen once the mouse has been clicked

If you see a post from me, you can safely assume its C# and XNA :)

This topic is closed to new replies.

Advertisement