如何在现有渲染流程中同时将场景渲染至Scene FBO与指定尺寸的Depth FBO以获取阴影映射深度图
Hey there! Let's walk through how to add shadow map rendering (to your 1024x1024 Depth FBO) alongside your existing Scene FBO workflow without breaking anything. Here's the step-by-step breakdown:
Shadow mapping requires rendering your scene from the light's perspective first to capture depth values, then using that depth texture during your main scene render (to the Scene FBO) to calculate shadows. Since your existing pipeline uses Renderer::begin()/end() to handle the Scene FBO, we'll insert the shadow pass before this main render.
1. Prep Your Depth FBO First
Make sure you've properly initialized your Depth FBO for shadow mapping:
- Create a 1024x1024 depth texture with a suitable format like
GL_DEPTH_COMPONENT32F(no color attachment needed—we only care about depth here). - Attach this texture to your Depth FBO, then validate the FBO with
glCheckFramebufferStatus(GL_FRAMEBUFFER)to ensure it's complete.
2. Adjust Your Render Flow
Your original flow is:
Renderer::begin(); drawCube(); drawSphere(); Renderer::end();
We'll modify this to add the shadow pass before the main scene render:
// Step 1: Render shadow map to Depth FBO first renderShadowMap(); // Step 2: Proceed with your original main scene render Renderer::begin(); drawCube(); drawSphere(); Renderer::end();
3. Implement the renderShadowMap() Function
This function handles all the setup for rendering from the light's perspective to your Depth FBO:
void renderShadowMap() { // Bind your Depth FBO as the active render target glBindFramebuffer(GL_FRAMEBUFFER, m_depthFBO); // Set viewport to match the Depth FBO's 1024x1024 size (critical for correct depth texture output) glViewport(0, 0, 1024, 1024); // Set up render states for shadow mapping glEnable(GL_DEPTH_TEST); glClear(GL_DEPTH_BUFFER_BIT); // No need to clear color—we don't have a color attachment // Switch to your shadow mapping shader (simpler than main shader—only needs to output depth) glUseProgram(m_shadowShader); // Pass the light's view-projection matrix to the shader // (This transforms world-space vertices to light clip space) glm::mat4 lightView = calculateLightViewMatrix(); // Implement based on your light's position/orientation glm::mat4 lightProj = glm::ortho(-15.0f, 15.0f, -15.0f, 15.0f, 0.1f, 100.0f); // Ortho for directional light, perspective for point/spot glm::mat4 lightVP = lightProj * lightView; glUniformMatrix4fv(glGetUniformLocation(m_shadowShader, "lightVP"), 1, GL_FALSE, glm::value_ptr(lightVP)); // Draw the same objects as your main scene—cube and sphere drawCube(); drawSphere(); // Clean up: unbind FBO and reset states (or let Renderer::begin() handle state reset) glBindFramebuffer(GL_FRAMEBUFFER, 0); glUseProgram(0); }
4. Key Things to Watch For
- Viewport Sync: Always set the viewport to match the FBO's size when switching render targets—if you forget, your depth texture will be stretched or misaligned. Most
Renderer::begin()implementations reset the viewport to the Scene FBO's size, but double-check. - Shader Separation: Your shadow shader doesn't need to output color. It can either let OpenGL automatically write depth, or calculate the vertex's position in light space and output that (for advanced shadow techniques like PCF).
- State Isolation: Make sure shadow pass states (like depth test, culling, shader) don't leak into your main render. If
Renderer::begin()resets render states, you're good—if not, manually reset states after the shadow pass. - Shadow Sampling: In your main scene shader, bind the Depth FBO's depth texture as a uniform, then sample it to check if each fragment is in shadow (compare the fragment's light-space depth to the texture's stored depth).
Why Not Render to Both FBOs at Once?
OpenGL only lets you bind one FBO as the active render target at a time. Even if you used multi-render targets (MRT), shadow mapping requires rendering the scene from a completely different perspective (light's view vs camera's view)—so you can't do it in a single pass. The two-pass approach (shadow first, main scene second) is the standard way to handle this.
内容的提问来源于stack exchange,提问作者G.Azma

