Search code examples
xna3d2dprojectvertex

Projecting 3D Vector to 2D screen coordinates


I am using XNA 4.0 and trying to take a Vector3 and turn it into a Vector2 which represents its coordinates on the screen. I know you can use Viewport.Project and this gives me perfect results, but I need to make my own method for doing this. I have to convert every vertex to 2D coords per frame and on CPU instead of GPU. Using the provided Project method is a little slower than what I'd like. It returns a "z" value too, which I won't even use. I just want to return a (x, y) in the simplest way possible.

Here is what I have come up with so far, and it gives nearly correct results. But not quite there yet

    private Vector2 ScreenCoords(Vector3 v)
    {
        return new Vector2(
            viewProj.M11 * v.X + viewProj.M21 * v.Y + viewProj.M31 * v.Z + viewProj.M41,
            viewProj.M12 * v.X + viewProj.M22 * v.Y + viewProj.M32 * v.Z + viewProj.M42);
    }

viewProj is just (viewMatrix * ProjectionMatrix)

A vertex at the center of the screen seems to always come back as (0, 0) but I get slightly messed up results as the vertex changes screen coordinates (as I move the camera) As the vertex moves left, the X of my projected result decreases, and as the vertex moves down, the Y of my projected result increases. So at least I have that right, but the rate they change at isn't correct or consistent.

EDIT: here is how it's done, probably the fastest way of doing it (note that this returns the coordinates in the range [-1, 1])

    private Vector2 ScreenCoords(Vector3 v) 
    {
        float 
            w = viewProj.M14 * v.X + viewProj.M24 * v.Y + viewProj.M34 * v.Z + viewProj.M44;
        return new Vector2(
            (viewProj.M11 * v.X + viewProj.M21 * v.Y + viewProj.M31 * v.Z + viewProj.M41) / w,
            (viewProj.M12 * v.X + viewProj.M22 * v.Y + viewProj.M32 * v.Z + viewProj.M42) / w);          
    }

Here is a comparison to using Viewport.Project to get the 2D coordinates -
FPS with Project - 81
FPS with ScreenCoords - 143
FPS without either one - 152
So I would recommend something like this for anyone else who is doing lots of projecting per frame.


Solution

  • You should realize that perspective projections need homogeneus coordinates, you should divide

    the components by the homegeneus factor W.

    This is the math:

    Vector4 position = new Vector4(pos3, 1);
    Vector4.Transform(ref position, ref worldViewProjection, out position);
    position /= position.W;
    

    Also you can find more info here: http://en.wikipedia.org/wiki/Transformation_matrix#Perspective_projection