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