Search code examples
copenglgraphicsshaderrender-to-texture

Writing to a GL_R8 texture


I am having some trouble writing to a texture (GL_R8 Format) attached to an FBO while reading from another texture attached to the same FBO (GL_RGB32F). I believe the problem is with the output type in my fragment shader.

Texture Initialization:

    glGenFramebuffers(1, &rBuffer->fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, rBuffer->fbo);


    glGenTextures(RayBuffer_TextureType_NUMTEXTURES, rBuffer->textures);
    ...
    glBindTexture(GL_TEXTURE_2D, rBuffer->textures[RayBuffer_TextureType_SHADOW]);
    glTexImage2D
    (
            GL_TEXTURE_2D,  
            0,         
            GL_R8,        
            textureWidth,
            textureHeight,
            0,
            GL_RED,
            GL_UNSIGNED_BYTE,
            NULL
    );

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glFramebufferTexture2D
    (
            GL_FRAMEBUFFER,                                
            GL_COLOR_ATTACHMENT0 + RayBuffer_TextureType_SHADOW,
            GL_TEXTURE_2D,
            rBuffer->textures[RayBuffer_TextureType_SHADOW],
            0
    );

Binding:

    glBindFramebuffer(GL_FRAMEBUFFER, rBuffer->fbo);

    glDrawBuffer(GL_COLOR_ATTACHMENT0 + RayBuffer_TextureType_SHADOW);

    glActiveTexture(GL_TEXTURE0 + RayBuffer_TextureType_POSITION);
    glBindTexture(GL_TEXTURE_2D, rBuffer->textures[RayBuffer_TextureType_POSITION]);

Geometry Shader:

#version 330
layout(triangles) in; 
layout (triangle_strip, max_vertices=4) out;
uniform sampler2D positionTexture;
uniform vec3 lightDirection;
uniform vec3 rightDirection;
uniform vec3 upDirection;
uniform vec2 screenSize;
void main()
{



    gl_Position = vec4(1.0f, 1.0f, 0.0f, 1.0f);
    EmitVertex();

    gl_Position = vec4(-1.0f, 1.0f, 0.0f, 1.0f);
    EmitVertex();

    gl_Position = vec4(1.0f, -1.0f, 0.0f, 1.0f);
    EmitVertex();

    gl_Position = vec4(-1.0f, -1.0f, 0.0f, 1.0f);
    EmitVertex();

    EndPrimitive();
}

Fragment Shader:

#version 330
layout (location = 3) out float out_shadow;
void main()
{
    out_shadow = 1.0f;
}

Blitting the texture to the screen:

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, members->rBuffer->fbo);
    glReadBuffer(GL_COLOR_ATTACHMENT0 + RayBuffer_TextureType_SHADOW);
    glBlitFramebuffer
    (
            0, 0, eBuffer->windowWidth, eBuffer->windowHeight,
            0, 0, eBuffer->windowWidth, eBuffer->windowHeight,
            GL_COLOR_BUFFER_BIT, GL_LINEAR
    );

It is safe to assume that RayBuffer_TextureType_SHADOW is 3. Furthermore, it should be noted I have stripped out all complexities of the geometry shader to try and find the origin of the problem. The code produces a completely black screen while I was expecting a completely red screen.


Solution

  • I believe the problem is with the way you bind your output buffer. The critical line is here:

    layout (location = 3) out float out_shadow;
    

    You seem to assume that the value 3 is needed to match the index of the color attachment of the FBO you render to:

    glFramebufferTexture2D
    (
        GL_FRAMEBUFFER,                                
        GL_COLOR_ATTACHMENT0 + RayBuffer_TextureType_SHADOW,
    

    with a value of 3 for RayBuffer_TextureType_SHADOW.

    This is not how the association of fragment shader output to FBO attachment works. The value you specify with the layout(location=...) qualifier is called the color number in most parts of the spec. For example on page 190 of the OpenGL 3.3 spec, while describing glBindFragDataLocationIndexed(), it talks about:

    The binding of a user-defined varying out variable to a fragment color number [..]

    and on the next page (emphasis added by me):

    When a program is linked, any varying out variables without a binding specified either through BindFragDataLocationIndexed or BindFragDataLocation, or explicitly set within the shader text will automatically be bound to fragment colors and indices by the GL.

    Now, these "color numbers" match up with the index of the draw buffers you specified. From the description of glDrawBuffer() on page 210 of the same document:

    defines the set of color buffers to which fragment color zero is written.

    So with your call:

    glDrawBuffer(GL_COLOR_ATTACHMENT0 + RayBuffer_TextureType_SHADOW);
    

    you specify that color 0 produced by the fragment shader is written to attachment 3 of your FBO.

    What this all means is that you need to specify color number 0 for the output of the fragment shader:

    layout (location = 0) out float out_shadow;
    

    Color numbers larger than 0 are only useful if you produce more than one output from your fragment shader. In that case, the location values specify the index of the color buffer within the list passed to glDrawBuffers() that the output is written to.