Reputation: 3705
I have a 3D iPhone game done with OpenGL ES.
It's a big world but with some tiny, first-person-view bits I need to paint up close, so I can't reduce the depth range (zNear vs zFar) that glFrustumf() takes any further.
When surfaces meet for a Z-fight, I paint them slightly apart to stop them flickering. I'm also making the camera's distance determine how far apart I adjust them, in cases where this is useful and needed.
It's mostly OK, but there are some things whose perspective suffers by the separation, and making the separation smaller causes flicker. I'd love to paint surfaces closer together.
Is there any way to increase the depth buffer precision, so surfaces can be closer together without a narrower depth range?
If not, is there any other way around this?
I'm still using OpenGL ES 1.1 in the app, but am willing to upgrade if it's worth it.
Thanks for your help.
Here's how I create the depth buffer...
In init method:
// Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
glGenFramebuffersOES(1, &defaultFramebuffer);
glGenRenderbuffersOES(1, &colorRenderbuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer);
//Added depth buffer
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
In resizeFromLayer method:
// Allocate color buffer backing based on the current layer size
glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
//Added depth buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
Here's how I create the frustum...
const GLfloat zNear = 2.2;
const GLfloat zFar = 30000;
const GLfloat fieldOfView = 60.0;
GLfloat size = zNear * tanf(degreesToRadian(fieldOfView) / 2.0);
if (LANDSCAPE) { //for landscape clip & aspect ratio.
//parameters are: left, right, bottom, top, near, far
glFrustumf(-size/(backingWidth/backingHeight),
size/(backingWidth/backingHeight),
-size, size,
zNear, zFar);
}
Upvotes: 1
Views: 2927
Reputation: 1421
What worked for ME was to adjust near-far values. The difference between far and near value defines how precise your depth buffer is. By example. Let's say you have a far of 10000 and a near of 500. That will have a total depth of: 9500 With a 16 bits DepthBuffer you have 65536 possible combinations of depth. (This value is calculated with the geometry differently depending on GPU and OpenGl implementation ) Then you'll have approximately 65536/9500 ~= 7 possible depths for each unit of space. Then you'll have 1/7 ~= .14 of depth precision. If your objects have a distance between them of .14 or less you'll probably get z-fighting. In real life this is more complex, but the idea is the same.
Maybe your far value is to long and you don't need it. Also increasing the near value helps with z-fighting in objects that are more closer to the camera (the ones that are more visible).
Upvotes: 1
Reputation: 100662
Standard answers revolve around use of glPolygonOffset
. What that does is add an offset to the polygon depth values before comparing to those already in the buffer. The offset is calculated allowing for screen depth and angle, so it's independent of the size of your world and it doesn't affect the identities of the pixels to be painted.
The issue is deciding when to use it. If your scene is, say, lots of discrete objects with no unifying broad data structure (like a quadtree or a BSP tree) then you're probably going to have to use something like a bucket system to spot when objects are very close (relative to their distance) and give a bump to the closer. If the problem is internal to individual meshes and you've no higher level structures then obviously the problem is more complicated.
At the other end, if your scene is entirely or overwhelmingly static then a structure like a BSP tree that can do most of the drawing without even needing a depth buffer might be an advantage. At a desperate end you could render back to front with depth writing but no comparisons then do the moving objects as an extra layer; in practice that'll give you massive overdraw (though a PVS solution would help) versus front-to-back with modern early depth culling — especially on a deferred tile based renderer like the PowerVR — so again it's not an easy win.
As a separate idea, is there any way you can simplify distant geometry?
Upvotes: 0
Reputation: 3705
Apparently 32-bit depth buffers aren't supported in OpenGL ES 1.x.
Also, it seems that 16-bit depth buffers aren't supported on iOS, so using GL_DEPTH_COMPONENT16_OES
was just behaving as 24-bit, which is why I didn't see any improvement when I used GL_DEPTH_COMPONENT24_OES
instead!
I confirmed this by checking GL_DEPTH_BITS
after trying to set the depth buffer to 16 bit:
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
GLint depthBufferBits;
glGetIntegerv(GL_DEPTH_BITS, &depthBufferBits );
NSLog(@"Depth buffer bits: %d", depthBufferBits );
Outputs:
Depth buffer bits: 24
Oh well, at least now I know. Hope this helps someone else.
Upvotes: 0