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.
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