Problem with screenspace projection

Hi all,
looks like I’m a litte bit lost with my Math. I try to project a vertex from world to screen space but get strange values in some configurations. My current setup is like this:

Windows 10 UWP, DirectX
Worldmatrix = Identity-Matrix
Viewmatrix:
1 0.0000000 0.0000000 1.0000000
0 0.8660260 -0.4999989 -350.2188
0 0.4999989 0.8660260 -793.4045
0 0.0000000 0.0000000 1.0000000

Projectionmatrix:
0.6841934 0.000000 0.000 0.000
0.0000000 1.303225 0.000 0.000
0.0000000 0.000000 -1.001 -1.001
0.0000000 0.000000 -1.000 0.000

Those where created from the cameraposition at (-1, 700, 512) looking at straight along the negative z-Axis (that is e.g. at the target point (-1, 700, 0). Than the camera was rotated 30 Degrees towards the ground. The Up-Vector is (0, 1, 0). My vertival field of view is 150 degrees, near clipping plane 1.0 and far clipping plane 10000.0.
My current viewport is 1920x1008 pixels.

Projecting a very large quad with the basic effect works as expected. I than try to project the corners of this quad to my screenspace to calculate the extent of the quad on screen. For some points the projections (either via Viewport.Project() or by manually Vector4.Translate(WorldViewProjectionMatrix). But for others the values are really strange.

World-Position Corner 1: (0, 0, 0) -> Screen-Position: (960.8279, 214.0689) -> seems reasonably
World-Position Corner 2: (65536, 0, 0) -> Screen-Position: (55215.27, 214.0689) -> seems reasonably
World-Position Corner 3: (0, 0, 65536) -> Screen-Position: (959.9883, 115.2958) -> this point is to the left of Corner 1 on my screen?! It is definitely not drawn there.
World-Position Corner 4: (65536, 0, 65536) -> Screen-Position: (190.7992, 115.2958) -> this Point is really strange too.

Since the Basic effect shader draws everything Right I guess I have some misconception in projecting the Vertices to screenspace myself. Or is this some form of buffer overflow? Maybe someone with deeper math / graphics knowledge can give me a hint what is going on here? Thanks a lot!

This discussion regarding 3d world position to screen space might help ^_^y

Hi Dexter,
thanks a lot! Of Course those weird results are from points behind the camera. Should have been obvious to me :frowning:
I am still not quite sure how to deduce the correct screen size of a triangle in such a case but your solution (dividing by the absolute amount of the W component) gave me a good start and a working solution for my special usecase (LOD refinement for terrain). Thanks again!

1 Like

I’m not sure if I can answer your question any better than the last post, but here’s my two cents.

It sounds like you’re already passing these world-space Vector4 vertices (where the W coordinate is 1) through your view and projection matrices, as expected. As you stated in your last post, you then want to divide XYZ by the resulting W coordinate.

However! As you noted, this will only work when the vertex in question is in front of the camera. At and behind the camera, the results are meaningless. (A vertex at the camera’s position will have a W value of 0, division by which is undefined or infinite, and behind the camera will have a negative W.)

So, my first piece of advice is, don’t use vertices behind the camera, and this problem won’t occur. If you can’t avoid this, however, you will have to take a few additional steps to correct it.

In the GPU, before dividing by W, it will clip any polygons that extend behind the near plane (which, in projection space, is Z=0). I think this is what you’re going to have to do to handle this scenario.

Let’s ditch the quad issue and focus on a triangle instead to simplify. There are four cases to consider.
If 0/3 vertices are behind the camera (none have projection Z < 0), then no problem.
If 3/3 vertices are behind the camera (all have projection Z < 0), then there’s no visible polygon anyway.
If 2/3 vertices are behind, we’ll have to clip them and work instead with a triangle that ends at the near plane (projection Z = 0).
If 1/3 vertices are behind, we have to clip the triangle, thereby making a quad (two triangles).

In either of these last two cases, we’ll have to calculate the locations of the two new vertices. It’s pretty easy; just use Vector4.Lerp between the vertex that is in front of the camera to the one behind. The amount of the interpolation will be the ratio of distance to the Z=0 plane from the front vertex to that to the behind vertex. Something like this:

Vector4 front;
Vector4 back;
Vector4 atPlane = Vector4.Lerp(front, back, (0 - front.Z) / (back.Z - front.Z));

Do this for each of your unknown vertices - that is, those terminating at this plane - and you can find the vertices of a valid triangle or quad that is entirely in front of the camera. From that point, you divide by W as before. Voila!