3
votes

I have checked this similar question, but the suggestions did not solve my problem: Low precision and clamping when writing to floating point FBO.

I am rendering a float image using a framebuffer object as render target. However, the values read by glReadPixels are clamped between 0 and 1. If I try to render my image using normalized values in this range, it works, but values outside this range are clamped to 1 or 0. Follows some parts of the code.

Here is how I created the texture that I attached to the FBO:

    glGenTextures(1, &color_tex); 
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, color_tex);
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
        GL_NEAREST);
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
        GL_NEAREST);
    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB32F_ARB, ResX, ResY, 0,
        GL_RGB, GL_FLOAT, NULL);

How I read it after painting:

    glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
    glReadPixels(0, 0, ResX, ResY, GL_RGB, GL_FLOAT, data);

I have painted a triangle in the image using

    glColor3f(0.5f, 2.0f, -0.5f);

And the pixel values appears as (0.5f, 1.0f, 0.0f) in the read buffer.

How can I return values out of this range?

Follows a simplified version of my code (I kept the depth buffer because I use it in the original one):

Ps. I corrected the code bellow based on the answer. It works now.

#include <GL/glew.h>
#include <GL/glut.h>

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#define WINDOW_WIDTH                    800
#define WINDOW_HEIGHT                   600

bool initGL(int argc, char *argv[])
{
    // initialize GLUT and glexts
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutInitWindowPosition(100, 100);
    glutCreateWindow("FBO");

    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        /* Problem: glewInit failed. */
        printf("Error: %s\n", glewGetErrorString(err));
        return 0;
    }

    if (!GLEW_EXT_framebuffer_object)
    {
        printf("Error: no extension GL_EXT_framebuffer_object.");
        return 0;
    }

    if (!GLEW_ARB_color_buffer_float)
    {
        printf("Error: no extension ARB_color_buffer_float.");
        return 0;
    }

    glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);
    glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE);
    glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);

    return 1;
}

void glErrorCheck()
{
    int errCode;
    if ((errCode = glGetError()) != GL_NO_ERROR)
    {
        printf("Failure in OpenGL %d", errCode);
        exit(0);
    }
}

bool renderFBO()
{

    GLenum status;
    GLuint color_tex, depth_rb, fb;

    int ResX, ResY;

    ResY = 4;
    ResX = 4;

    float *data;

    //gen renderbuffer
    glGenRenderbuffers(1, &depth_rb);
    glErrorCheck();
    // initialize depth renderbuffer
    glBindRenderbuffer(GL_RENDERBUFFER, depth_rb);
    glErrorCheck();
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32, ResX,
            ResY);

    glErrorCheck();

    // make a texture
    glGenTextures(1, &color_tex);
    // initialize texture that will store the framebuffer image (BGRA type)
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, color_tex);
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER,
            GL_NEAREST);
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER,
            GL_NEAREST);
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB32F_ARB, ResX, ResY, 0,
            GL_RGB, GL_FLOAT, NULL);
    glErrorCheck();

    // gen the framebuffer object
    glGenFramebuffers(1, &fb);
    // bind the framebuffer, fb, so operations will now occur on it
    glBindFramebuffer(GL_FRAMEBUFFER, fb);

    // bind this texture to the current framebuffer obj. as color_attachment_0
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_RECTANGLE_ARB, color_tex, 0);
    glErrorCheck();

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
            GL_RENDERBUFFER, depth_rb);
    glErrorCheck();

    //check framebuffer status
    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE)
    {
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        {
            printf("Failure in OpenGL Framebuffer %d", status);
            return 0;
        }
    }
    glErrorCheck();

    //render to GL_TEXTURE_2D
    glBindFramebuffer(GL_FRAMEBUFFER, fb);
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glViewport(0, 0, ResX, ResY);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, (float) ResX, 0.0, (float) ResY, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glBegin(GL_TRIANGLES);

    printf("Original color: %f %f %f\n", 0.5f, 2.0f, -0.5f);

    glColor3f(0.5f, 2.0f, -0.5f);

    glVertex3f(0.0f, 0.0f, 0.0f);
    glVertex3f((float) ResX, 0.0f, 0.0f);
    glVertex3f((float) ResX, (float) ResY, 0.0f);

    glEnd();

    // read framebuffer
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    glErrorCheck();

    // allocate memory for texture data
    data = new float[ResX * ResY * 3];
    if (data == NULL)
    {
        printf("Out of memory\n");
        return false;
    }

    //glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);
    glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);

    glReadPixels(0, 0, ResX, ResY, GL_RGB, GL_FLOAT, data);
    glErrorCheck();

    //print the last pixel of the buffer
    int i = ResX * ResY - 1;
    printf("Buffer color: %f %f %f\n", data[i], data[i + 1], data[i + 2]);

    // Re-enable rendering to the window
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glErrorCheck();

    //deallocate data
    delete[] data;

    //delete buffers
    glDeleteTextures(1, &color_tex);
    glDeleteRenderbuffers(1, &depth_rb);
    glDeleteFramebuffers(1, &fb);
    glErrorCheck();

    return true;
}

int main(int argc, char **argv)
{

    if (!initGL(argc, argv))
    {
        printf("Failure during GLUT/GLEXT initialization.");
        return 0;
    }

    if (!renderFBO())
        printf("Fail!\n");

    printf("End!\n");
    return 0;

}
2

2 Answers

3
votes

Do a full set of glClampColor() operations in your initGL() function:

glClampColor(GL_CLAMP_READ_COLOR, GL_FALSE);
glClampColor(GL_CLAMP_VERTEX_COLOR, GL_FALSE);
glClampColor(GL_CLAMP_FRAGMENT_COLOR, GL_FALSE);

On my system I only needed the GL_CLAMP_VERTEX_COLOR.

1
votes

You must use glClampColor to state whether the colors you read with glReadPixels will be clamped or not.

Note that this is just for the reading of the color. What gets written by the fragment shader will always be unclamped.