6

I am trying to create a simple 3D graphics engine and have found and used the equations I found here: http://en.wikipedia.org/wiki/3D_projection#cite_note-0. (I have calculations for Dx, Dy, Dz and Bx, By)

I works, but when I rotate the camera enough lines start flying all over the place and eventually you see the polygons that went off screen start to come back on the opposite side of the screen (you can go here: http://mobile.sheridanc.on.ca/~claassen/3d.html and use the W, A, S and D keys to rotate the camera to see what I'm talking about)

I read this discussion: How to convert a 3D point into 2D perspective projection? where he talked about using a clip matrix but Im still a little confused as to how exactly to use one. Also I'm not sure if I am using 'homogeneous coordinates' as described in the discussion.

Community
  • 1
  • 1
mclaassen
  • 5,018
  • 4
  • 30
  • 52

2 Answers2

29

After multiplying by the perspective projection matrix (aka clip matrix) you end up with a homogenious 4-vector [x,y,z,w]. This is called npc (normalized projection coordinates), and also called clip coordinates. To get the 2D coordinates on the screen you typically use something like

xscreen = (x/w) * screen_width
yscreen = (y/w) * screen_width

For points in front of the camera this gives you what you want. But points behind the camera will have w<0 and you will get values that map to valid screen coordinates even though the point is behind the camera. To avoid this you need to clip. Any vertex that has a w<0 needs to be clipped.

A quick thing to try is to just not draw any line if either vertex has w<0. This should fix the strange polygons that show up in your scene. But it will also remove some lines that should be visible.

TO completely fix the problem you need to clip all the lines which have one vertex in front of the camera and one vertex behind the camera. Clipping means cutting the line in half and throwing away the half that is behind the camera. The line is "clipped" by a plane that goes through the camera and is parallel to the display screen. The problem is to find the point on the line that corresponds to this plane (i.e. where the line intersects the plane). This will occur at the point on the line where w==0. You can find this point, but then when you try to find the screen coordinates

xscreen = (x/w) * screen_width
yscreen = (y/w) * screen_width

you end up dividing by 0 (w==0). This is the reason for the "near clipping plane". The near clipping plane is also parallel to the display screen but is in front of the camera (between the camera and the scene). The distance between the camera and the near clipping plane is the "near" parameter of the projection matrix:

[    near/width   ][        0        ][         0              ][        0       ]
[        0        ][    near/height  ][         0              ][        0       ]
[        0        ][        0        ][(far+near)/(far-near)   ][        1       ]
[        0        ][        0        ][-(2*near*far)/(far-near)][        0       ]

To clip to the near plane you have to find the point on the line that intersects the near clipping plane. This is the point where w == near. So if you have a line with vertices v1,v2 where

v1 = [x1, y1, z1, w1]
v2 = [x2, y2, z2, w2]

you need to check if each vertex is in front of or behind the near clip plane. V1 is in front if w1 >= near and behind if w1 < near. If v1 and v2 are both in front then draw the line. If v1 and v2 are both behind then don't draw the line. If v1 is in front and v2 is behind then you need to find vc where the line intersects the near clip plane:

n = (w1 - near) / (w1 - w2)
xc = (n * x1) + ((1-n) * x2)
yc = (n * y1) + ((1-n) * y2)
zc = (n * z1) + ((1-n) * z2)
wc = near
vc = [xc, yc, zc, wc]

Now draw the line between v1 and vc.

Acorn
  • 768
  • 6
  • 15
  • I can not thank you enough for the well written answer. This has helped me tremendously... can't up-vote enough either. – shbi Mar 11 '15 at 12:59
  • This is a great answer, but there are a few things I'm unclear on. First, are you missing the line `wc = (n * w1) + ((1 - n) * w2)`? Also, is your projection matrix P multiplied on the right i.e. v_e * P? More importantly, no renderer I've used requires me to specify the near plane at draw time - how can you perform this clipping when the near value is not known? – Qualia Apr 22 '17 at 00:46
  • I may tentatively have found an answer. Clearly, clipping z, you constrain w, as they are linearly dependent. The problem I had is, if you're interpolating w, you are interpolating the clip value against which you test z. In fact, the equations aren't as hard as I thought. For a vertex v_3 given from v_1 and v_2 whose line intersects the clip plane, `w_3 = w_1 + r*(w_2-w_1)` and `a_3 = a_1 + r*(a_2-a_3)`, where `a_n = dot(plane,v_n)`. But a_3 is equal to the clip value, w_3, so `a_1 + r*(a_2-a_1) = w_1 + r*(w_2-w_1)`. Solve for r, and you're done. (I hope). – Qualia Apr 22 '17 at 01:37
  • Having implemented the above, I now realise that this (of course) doesn't solve the -w problem, as you get clipped by the wrong clip plane. However, you no longer need to take w to the near plane, just to >= 0 – Qualia Apr 22 '17 at 02:40
  • As Qualia pointed out I didn't define wc. It is equal to near (since this is the point that intersects the plane where w=near). (I added that to the answer). Yes, the projection (aka clip) matrix is multiplied times a column-major point in view coordinates on its right: Projection * Pview = Pnpc. AFAIK It is not really possible to do projection without a near plane, so if your renderer does not ask you for a near value then it is probably picking a near value for you. – Acorn Apr 30 '19 at 07:17
4

This might be a misunderstanding of the terminology. The clip matrix is more appropriately known as a projection matrix. In OpenGL at least, the projection matrix transforms 4D homogeneous coordinates in view coordinate space (VCS) to clipping coordinate space (CCS). Projection from the CCS to normalized device coodinate space (NDCS) requires the perspective division, i.e., dividing each component by the W component. Clipping is correctly done before this step. So, a 'clipping matrix' doesn't remove the need to clip the geometry prior to projection. I hope I've understood you, and this doesn't sound condescending.

That said, I think you've obviously got the projection matrix right - it works. I suspect that the vertices passing behind the eye have negative W, which means they should be clipped; but I also suspect they have negative Z, so the division is yielding a positive Z value. If you really want to clip the geometry, rather than discard whole triangles, do a search for 'homogeneous clipping'. If you're not really working in 4D homogeneous space, you might start by looking at 'Sutherland-Hodgman' 3D clipping.

Brett Hale
  • 21,653
  • 2
  • 61
  • 90