14
\$\begingroup\$

I'm rolling my own 3D engine, in JavaScript, and using only canvas drawing, no WebGL. This is another Minecraft clone; I love boxes, don't judge me.

So far, everything works wonderfully, except for one thing: in 3D, when some vertices go behind the near clipping plane, their projection on the screen comes out weird (assuming other vertices used to trace a plane are in front).

I tried clipping these points but then I can see trough the surfaces that use these vertices. In WebGL/OpenGL the graphics card takes care of these points and the plane is rendered correctly, but I don't have access to hardware so I must code this myself.

I'm not quite sure what to make of it, currently the last thing that came to mind is to reverse the projection of points behind the player's near clipping plane, which seems logical since I must project a point to a screen which is in front of the vertex.

Here are my thoughts:

enter image description here

Here are some images to illustrate what happens:

enter image description here

From distance the blue box renders perfectly well.

enter image description here

When some of the vertices go behind the player's near clipping plane I do reverse projection, but it doesn't look right:

focalLength *= -1;
2d.x = x*focalLength/z;
2d.y = y*focalLength/z;

Note that the gray box behind is completely removed since all the vertices used to draw its faces are behind the player.

enter image description here

This is what happens when look up or down.

I don't know what to make of the math behind this, I'm hoping somebody has encountered the same problem already and can help me.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ If the points are closer to the eye than the near clip plane, they should be clipped -- and this might indeed let you see "through" an object. This is the typical behavior; collision normally prevents that particular visual artifact. Was that the only thing wrong with your clipping solution? \$\endgroup\$
    – user1430
    Commented Jan 12, 2012 at 16:02
  • \$\begingroup\$ @JoshPetrie: I understand that the points must be clipped, but if I do that the whole square will disappear as one or two of the vertices by which the drawing routine must pass are missing (in 2d) and the player would be able to see through that square. I'd like them to be "outside" of the canvas (on the projection) so that the square could still be drawn. I'm not sure if I'm clear enough. \$\endgroup\$
    – Solenoid
    Commented Jan 12, 2012 at 16:12
  • \$\begingroup\$ You are -- I'm saying that is normal behavior and you want to prevent this from happening by preventing the player from getting that close to one of the cubes. If you really wanted to do this you should clip but reconstruct the triangles (possibly). This will still look unusual, especially if you have textures. If what I'm saying doesn't make sense, you can join us in the chat so we don't create a really chatty comment thread. \$\endgroup\$
    – user1430
    Commented Jan 12, 2012 at 16:17
  • \$\begingroup\$ What you're saying makes sense, reconstructing takes too much time so that's not the solution. I hoped there was a way to still draw that vertex on the 2d plane that was behind the player so the lineTo(x,y) function could still be called, only I don't know how it behaves... it's a bizarre dimension, I agree. \$\endgroup\$
    – Solenoid
    Commented Jan 12, 2012 at 16:43
  • \$\begingroup\$ Have a look at this tutorial, it explains how to compute the clipping of individual triangles, and objects as a whole \$\endgroup\$ Commented Mar 11, 2020 at 19:46

3 Answers 3

2
\$\begingroup\$

The purpose of the near clipping plane is that it is a clipping plane. Triangles that are outside of a clipping plane are clipped: cut into pieces so that each piece that is left is within the clipping region.

You can attempt to ignore the near clip if you wish. Indeed OpenGL and D3D have ways of turning off near plane clipping altogether (though the depth buffer still has a minimum near value). The problem is not the near clip.

The problem is with vertices that are behind the camera.

You cannot render triangles that are behind the camera. Not with a perspective projection. Such triangles do not make sense under the math behind perspective projections. Furthermore, they are also outside of the frustum.

Turning off near clipping turns a frustum into a pyramid. The reason the pyramid stops at the point is because points above the pyramid are behind all four of the sides of the pyramid. So any point behind the camera (the tip of the pyramid) is above, below, to the left, and to the right of the visible region of the screen. All at the same time.

As I said: vertices under a perspective projection that are behind the camera don't make sense.

You must implement clipping. You must detect when any vertex of a triangle, in clip-space (before the perspective divide) is behind the camera. If it is, then you must clip that triangle, generating only triangles that are in front of the camera.

This is not a simple process. It will involve math that only makes sense if you have a full understanding of homogenous coordinate systems. Alternatively, you could just straight-up cull any triangle if any vertex of it is behind the camera.

\$\endgroup\$
1
  • \$\begingroup\$ Up until now I did cull the entire triangle, but then I saw through the plane (see pictures above). I really needed a picture to understand why it doesn't make sense geometrically. The only solution is to calculate the plane-line intersection when one of the vertices is behind the clipping plane and use that intersection to trace the line from the vertex that is in front, unfortunately this is expensive. \$\endgroup\$
    – Solenoid
    Commented Jan 14, 2012 at 16:02
2
\$\begingroup\$

I know this is probably a bit late, but I have a solution to this.

The transformation from world coordinates to points on a screen involves multiple parts, which involve subtracting the camera's coordinates, rotating around the camera, and then dividing by the axis perpendicular to the camera. (generally the Z axis.)

The problem with the clipping happens with the divide, because division by negative z indexes and coordinates close to 0 result in huge values, so the clipping algorithm is done just before that, but after the coordinates have been rotated and transformed.

Having the points of the face you want to clip in an array helps for organization. The algorithm goes through each point, sees if it's clipped or not, and then creates new points based off of that.

I apologize if I wasn't completely clear. Here's my code:

//loop through all points
for (var y=0;y<tempPoints.length;y++) {
    //if the selected point will be clipped, run the algorithm
    if (tempPoints[y][2] < render_clipDistance) {
        //freefriends is the number of adjacent non-clipped points
        var freeFriends = (tempPoints[(y+(tempPoints.length-1))%tempPoints.length][2] >= render_clipDistance) + (tempPoints[(y+1)%tempPoints.length][2] >= render_clipDistance);

        if (freeFriends == 0) {
            //if there are no free friends, there's no point in attempting, so just move on
            tempPoints.splice(y, 1);
            y -= 1;
        } else {
            //move towards friends
            var friendCoords = tempPoints[(y+(tempPoints.length-1))%tempPoints.length];
            var moveAmount = getPercentage(friendCoords[2], tempPoints[y][2], render_clipDistance)
            var newPointCoords = [linterp(friendCoords[0], tempPoints[y][0], moveAmount), linterp(friendCoords[1], tempPoints[y][1], moveAmount), render_clipDistance + 0.05];

            tempPoints.splice(y, 0, newPointCoords);

            y += 1;

            friendCoords = tempPoints[(y+1)%tempPoints.length];
            moveAmount = getPercentage(friendCoords[2], tempPoints[y][2], render_clipDistance)
            newPointCoords = [linterp(friendCoords[0], tempPoints[y][0], moveAmount), linterp(friendCoords[1], tempPoints[y][1], moveAmount), render_clipDistance + 0.05];
            tempPoints.splice(y, 1);
            tempPoints.splice(y, 0, newPointCoords);
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

If part of the triangle in behind the near plane could you then do a per-pixel check to see if the pixel position is behind the clipping plane?

You might treat the near plane like any other clipping plane. For example clipping planes are used for things like water planes (for reflections and refractions). I would think that this clipping plane would work just like the near clipping plane, and clip on a per-pixel basis.

I know how to handle clipping planes in HLSL with DirectX, but their implementation could be proprietary. If you could get a hold of the info for that it might be helpful.

Additionally here is a link that might help you: http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter42.html

\$\endgroup\$
1
  • \$\begingroup\$ Per pixel testing is extremely expensive in an interpreted language like Javascript, I'm getting barely acceptable fps now as it is. \$\endgroup\$
    – Solenoid
    Commented Jan 14, 2012 at 16:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .