I'm writing a flight simulator and got stuck with the classic problem of this genre: the near plane of the viewing frustum must be close enough to make aircraft cockpit visible, the far plane has to be far away to enable visible distances with up to 40km.
The visibility distance or near/far ratio is indeed beyond the z-buffer precision ability of opengl and distant objects flicker violently. This is the place a fancy 3d engine leaves you alone with your problem and you need a true knowledge of opengl :). Probably i found the right way to fix the problem (OpenGL experts correct me if i'm wrong), but my solution misses the important bit. Altered renderer performs double pass rendering:
- in the first pass distant objects and background has to be displayed, near plane moved away, z-buffer is happy, terrain looks good but near objects are clipped away.
- in the second pass the projection matrix is adjusted for close range objects and should display the cockpit.
The unsolved problem: in the second pass all distant objects and background are invisible, hence i have the cockpit and black background behind it. The result of the second pass totally wastes the result of the first pass. Ergo planned overlay doesn't happen. Question: how to force the opengl to ignore the background color in the second pass so both pass results create desired overlay?
P.S. Here is the image of the status quo (near/far planes are at extremes to make all details visible, single pass without projection adjustments).
http://www.flickr.com/photos/43342833@N04/5995604542/sizes/l/in/photostream/
The buffer clearing happens only once per rendering cycle and and not involved between the 2 passes. Here the clearing code:
JoglContext jctx = (JoglContext) ctx;
GLContext context = context(ctx);
GL gl = context.getGL();
// Mask of which buffers to clear, this always includes color & depth
int clearMask = GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT;
gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT);
gl.glDepthMask(true);
gl.glClearColor(r, g, b, jctx.getAlphaClearValue());
gl.glClear(clearMask);
gl.glPopAttrib();
The approach you describe is also used in the 2 passes: 1. "long distance" projection and terrain 2. "short distance" and the cockpit no clearing between, but after the second pass the background behind the cockpit is black. Maybe the glDepthRange function helps, got to check the manuals.
The zbuffer depth is 24 bits.
Don't clear the screen between the two passes. If you just want to clear the depth buffer, then clear just the depth buffer. Don't pass GL_COLOR_BUFFER_BIT
to glClear
.
In any case, a better way to do this (so that you don't have to re-render everything) is to employ a proper depth range. Since your cockpit cannot intersect with the scene, there's no reason to draw it into the scene's depth range.
So first, you draw your scene, using a reasonable perspective matrix (ie: one with a z-near that is reasonably large. On the order of several feet or so). Your scene does not consist of your cockpit. The glDepthRange
for this rendering should be something like [0.05, 1.0].
After that, you draw your cockpit, using a reasonable perspective matrix for just the cockpit. The glDepthRange
for this would be [0, 0.05]. This should give you plenty of depth bits of precision for both the scene and the cockpit.
Oh, and make sure you're getting a 24-bit depth buffer.
Cockpit versus outside is a classic case for the stencil buffer, much like HUD vs. scene. The advantage of stencil is that you don't need z at all, so you can set the near plane much farther away. Also, the Windows that you see through don't change (except when you rotate your virtual head inside the cockpit), so that's a draw-once-reuse-many thing.
Also, logarithmic z may be an interesting read for you.
来源:https://stackoverflow.com/questions/6892489/how-to-get-the-rid-of-the-z-fighting-problem-in-opengl