Search code examples
c#openglglslopentk

OpenGL - Accessing a cubemap when a different FBO is bound


I having difficulties switching my shadow mapping implementation from forward rendering to deferred rendering. I've located the issue and it's the cubemap not being sent to the shader. I suspect it's because my "GBuffer" FBO is bound instead of the cubemap FBO.

If GLSL can access colour attachments from two different FBOs in a single draw call, I'd like to go that route, is this possible?

Below, the working forward rendering code.

public void NormalPass(Shader shader)
{
    // Reset state
    GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
    GL.BindTexture(TextureTarget.Texture2D, 0);

    GL.UseProgram(shader.ID);
    GL.Viewport(0, 0, Width, Height);
    GL.CullFace(CullFaceMode.Back);
    GL.ClearColor(0.5f, 0.5f, 0.5f, 1f);

    // Uniforms
    Matrix4 viewMatrix = player.GetViewMatrix();
    Matrix4 projectionMatrix;
    Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, (float)Width / (float)Height, 0.1f, 100.0f, out projectionMatrix);
    GL.UniformMatrix4(shader.viewMatrixLocation, false, ref viewMatrix); 
    GL.UniformMatrix4(shader.projectionMatrixLocation, false, ref projectionMatrix);
    GL.Uniform3(shader.lightPositionLocation, lightPos);

    GL.BindTexture(TextureTarget.TextureCubeMap, cubeTex);
    DrawScene(shader, false);                               // False = Not shadowpass
    GL.BindTexture(TextureTarget.TextureCubeMap, 0);
}

And here, the failing deferred rendering modification

public void Composite(Shader shader)
{
    //Set up FBO reading/writing
    GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0);
    GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, gBuffer.FBO);

    // Bind textures
    GL.ActiveTexture(TextureUnit.Texture2);
    GL.BindTexture(TextureTarget.Texture2D, gBuffer.positionTexture);
    GL.ActiveTexture(TextureUnit.Texture1);
    GL.BindTexture(TextureTarget.Texture2D, gBuffer.normalTexture);

    GL.UseProgram(shader.ID);
    GL.Disable(EnableCap.DepthTest);
    GL.Viewport(0, 0, Width, Height);
    GL.CullFace(CullFaceMode.Back);
    GL.ClearColor(0.5f, 0.5f, 0.5f, 1f);
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    // Uniforms
    Matrix4 viewMatrix = player.GetViewMatrix();
    GL.UniformMatrix4(shader.viewMatrixLocation, false, ref viewMatrix);
    GL.Uniform3(shader.lightPositionLocation, lightPos);

    GL.BindTexture(TextureTarget.TextureCubeMap, cubeTex);
    Quad2D.drawScreenSizedQuad();   // Draw the combined lighting and diffuse to screen
    GL.BindTexture(TextureTarget.TextureCubeMap, 0);
}

Happy to provide any more information on request.


Solution

  • If GLSL can access colour attachments from two different FBOs in a single draw call, I'd like to go that route, is this possible?

    The question betrays a lack of understanding of what is going on. GLSL access textures, not color attachments of FBOs.

    Now obviously, textures can be used as color attachments. But the distinction is important because, no matter what, GLSL is accessing the bound textures, not whatever is in an FBO.

    The only rules OpenGL has about reading from textures that are attached as color attachments to an FBO is this: it will work so long as those textures are not actually attached to the particular framebuffer that is the active draw framebuffer during the rendering command. And even then, OpenGL will only care if you break the feedback loop rules.

    So that binding you do to the read framebuffer is pointless. That's not making the textures available for reading or somesuch; that's not what the read framebuffer binding point is for. It's only used for framebuffer reading commands and framebuffer blitting operations.

    So simply binding the default framebuffer to FramebufferTarget.Framebuffer, as you did in the forward case, would have been fine.

    Your principle problem is this:

    GL.BindTexture(TextureTarget.TextureCubeMap, cubeTex);
    

    You didn't use glActiveTexture to specify a texture unit to bind that cubemap to. Which means it will use the last texture unit you set: TextureUnit.Texture1, and you already bound a 2D texture there. It is illegal in GLSL to read two textures of two different types from the same texture unit.

    So odds are good that's not what you meant. Always use glActiveTexture before any glBindTexture calls. Unless you're using glBindTextures from GL 4.4/ARB_multibind or glBindTextureUnit from GL 4.5/ARB_DSA.