I have implemented the Z pre-pass technique into my engine, leading to a 2x performance improvement in a test scene.
However, the technique darkens the objects (but not the skybox), introduces z-fighting, and causes weird artifacts at the border of objects:
Here is the same picture without z pre-pass (performance divided by 2, but no visual bug):
Here is the depth framebuffer setup:
GLCall(glGenFramebuffers(1, &cameraDepthMapFBO)); GLCall(glGenTextures(1, &cameraDepthMapTexture)); GLCall(glBindTexture(GL_TEXTURE_2D, cameraDepthMapTexture)); // If I put GL_FLOAT instead of GL_UNSIGNED_INT here, it doesn't work. GLCall(glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); GLCall(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GLCall(glBindFramebuffer(GL_FRAMEBUFFER, cameraDepthMapFBO)); GLCall(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, cameraDepthMapTexture, 0)); GLCall(glDrawBuffer(GL_NONE)); GLCall(glReadBuffer(GL_NONE)); GLCall(glBindFramebuffer(GL_FRAMEBUFFER, 0));
I then do a z pre-pass where I render the scene on the cameraDepthMapFBO framebuffer:
GLCall(glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT)); GLCall(glBindFramebuffer(GL_FRAMEBUFFER, cameraDepthMapFBO)); GLCall(glClear(GL_DEPTH_BUFFER_BIT)); // ... all draw commands GLCall(glBindFramebuffer(GL_FRAMEBUFFER, 0));
Finally, in the main rendering pass, I copy the depth data from the pre-pass into the main depth buffer, and I render the scene.
// Clear screen, define viewport GLCall(glClearColor(0.01f, 0.01f, 0.01f, 1.0f)); GLCall(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); GLCall(glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT)); // Copy depth framebuffer onto main depth buffer glEnable(GL_DEPTH_BUFFER_BIT); GLCall(glBindFramebuffer(GL_READ_FRAMEBUFFER, cameraDepthMapFBO)); GLCall(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); GLCall(glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST)); // Set depth settings glEnable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDepthFunc(GL_LEQUAL); // ... all draw commands // Restore depth settings glDepthMask(GL_TRUE);
- I have tested to copy the cameraDepthMapFBO via a shader instead of simply using glBlitFramebuffer. It leads to the same result.
- The depth map itself seems correct, because I already use it for SSAO.
- In the framebuffer setup phase, I have noted that it stops working if I put "GL_FLOAT" instead of "GL_UNSIGNED_INT" as glTexImage2D parameter.
Any idea of what could be wrong?
invariantqualifier on your vertex position, to ensure the floating point math is not optimized differently between the depth-only and real-colour shader passes. A small change in the ordering of operations can change which way a bit rounds, which is enough to throw off an
LEQUALtest. But again I'd need an OpenGL expert to weigh in as I haven't experimented with this myself. I hope those leads are useful to you or other readers who can take this further. \$\endgroup\$