17

I have a 3D scene and a camera defined using gluPerspective. I have a fixed FOV, and I know the minimum distance of any geometry to the camera (it is a first-person view, so that is the minimum distance from the viewpoint to the character's collision volume).

How can I choose the farthest near clip plane (for the best depth buffer resolution) which will will not cause any clipping, no matter how the player moves and looks?

These distances are not simply equal because the corners of the near plane are farther from the origin than the center.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108

3 Answers3

16

Formula:

nearPlane = nearestApproachToPlayer / sqrt(1 + tan(fov/2)2 · (aspectRatio2 + 1)))

JavaScript code:

  var nearPlane = nearestApproachToPlayer 
                  / Math.sqrt(1 + Math.pow(Math.tan(fov/180*Math.PI/2), 2)
                                  * (Math.pow(aspectRatio, 2) + 1));

Derivation:

Geometrically, consider the pyramid whose base is the near clip plane and tip is the origin. Let nearPlane be the height of the pyramid, and w and h the width and height of the pyramid's base.

w = aspectRatio · h

The FOV determines the slope of the height-axis sides of the pyramid:

slope = tan(fov/2)

h/nearPlane = 2 tan(fov/2)

h/2 = nearPlane tan(fov/2)

Any corner point of the near clip plane is offset from the center of the clip plane by (w/2, h/2), so the distance is sqrt((w/2)2 + (h/2)2). The distance from the origin of this corner point is the hypotenuse of the right triangle formed by nearPlane and the former distance, so is sqrt((w/2)2 + (h/2)2 + nearPlane2).

We want that distance to the corner point to be equal to the closest approach of any geometry.

nearestApproachToPlayer = sqrt((w/2)2 + (h/2)2 + nearPlane2)

Applying straightforward algebra produces the formula given above.

I have not checked my algebra, but I have empirically tested the formula: if I multiply nearPlane by 1.1, then it produces a clip plane which is just a bit too far, for various aspect ratios. I have not tried different FOVs than 60°.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
  • I have checked it, makes sense. – Christian Rau Nov 12 '11 at 00:59
  • One of the parameters you'll carry around in a 3D rendering program is the view vector, i.e. the direction the camera points to. Say you have this vector normalized (i.e. unit length), then taking the scalar (=dot) product with the objects positions gives you the planar distance to the origin. This is a more direct approach, as it gives you the desired value directly, without the need for a squares, square root, and divides. It's only multiply-sum, i.e. MAD instructions, which are directly supported by SIMD instruction sets. – datenwolf Nov 12 '11 at 01:09
  • @datenwolf I don't see how to use that to solve this problem. I don't have a collection of specific objects; I have a minimum distance, and I'm trying to compute a projection *once* rather than dynamically. Or you could say I have a hypothetical object placed on the line through the upper-left corner of the viewport at the minimum distance. Can you compute that object's position, in order to use the procedure you describe, with only multiply/add? – Kevin Reid Nov 12 '11 at 01:29
  • 1
    @KevinReid: The best practice for choosing the near and far clipping plane distances is to have them snugly envelope the visible scene, to make maximum use of the available depth buffer resolution. The projection, just like so many other OpenGL state, should be set in the drawing function. The window resizing handler is a very bad place for doing projection setup. – datenwolf Nov 12 '11 at 02:02
  • @datenwolf Good point. It's impractical for my application, because I don't have good volumes to test against to to compute the envelope. However, it's good information for *the question I actually asked*, so could you write it up as an answer, including all the information in these comments? – Kevin Reid Nov 12 '11 at 02:44
8

The best practice for choosing the near and far clip distances is to have them snugly envelope the scene, i.e. the near clip plane as far as possible and the far clip plane as near as possible.

The standard frustum projection employed by the majority of 3D applications for a perspective transformation is a parallel plane projection. That means determining the planar distance from the point of view. This is actually rather easy:

One of the parameters you'll carry around in a 3D rendering program is the view vector, i.e. the direction the camera points to. Say you have this vector normalized (i.e. unit length), then taking the scalar (=dot) product with the objects positions gives you the planar distance to the origin. This is a more direct approach, as it gives you the desired value directly, without the need for a squares, square root, and divides. It's only multiply-sum, i.e. MAD instructions, which are directly supported by SIMD instruction sets.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
7

How can I choose the farthest near clip plane (for the best depth buffer resolution) which will will not cause any clipping, no matter how the player moves and looks?

Simple: turn off near clipping. I can't believe that people haven't heard of this yet. You enable depth clamping, which causes near (and far) values that are less than the near (or greater than the far) to be clamped, rather than clipped.

This won't prevent objects that actually go behind the camera from being clipped though. And since the depths are clamped, you lose the utility of a depth buffer for those regions where clamping occurs. It's still a good idea to use it where reasonable, if you can't choose a near clip that's too far away otherwise.

It's generally better to enable clamping and push the near clip out than it is to pick the absolute minimum near clip.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I think you misunderstand. I'm not rendering a character model. The character's collision volume is just a region in which nothing else can enter, so there's known to be nothing to be clipped there. I wasn't aware of the depth clamping trick (though I'd implement it directly as I'm using GL ES); thanks for that. I can't use it to gain extra range because it is quite possible that objects near the camera have intersecting geometry, but it is a good contribution to the question I actually asked. – Kevin Reid Nov 12 '11 at 02:41
  • 1
    @KevinReid: Actually, unless there's an OES extension for it, you can't actually use depth clamping. There's no way to implement it manually. Oh, btw, if you're using OpenGL ES, it's usually a good idea to put that in the tags. OpenGL and GL ES aren't the same, after all. – Nicol Bolas Nov 12 '11 at 03:06
  • ...right. I was thinking I could do it in the fragment shader, which I can't since that's too late, and doing it in the vertex shader would create visible distortion. – Kevin Reid Nov 12 '11 at 03:13