0
votes

I'm trying to implement HDR in OpenGL.

Here the way i make the HDR framebuffer:

glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

glGenTextures(1, &fboTex);
glBindTexture(GL_TEXTURE_2D, fboTex);
glTexImage2D( GL_TEXTURE_2D, 0,GL_RGBA16F,screen->w, screen->h,0,GL_RGBA,GL_FLOAT,NULL);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);

glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, screen->w, screen->h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);


GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0 };
glDrawBuffers(1, DrawBuffers);

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
    std::cout<<"Error loading the Framebuffer"<<std::endl;
    return;
}



First step, i use this pixel shader to render my scene and test the HDR framebuffer:

void main (void)
{

 gl_FragColor = vec4(1.1,1.1,1.1,0.0);//1.1 just for test

}



And i finally render to screen with this pixel shader:

uniform sampler2D tex;

void main(void) 
{

 float color = texture2D(tex, gl_TexCoord[0].st).x;

 if(color<1.1)gl_FragColor = vec4(1.0,0.0,0.0,0.0); //bad

 else gl_FragColor = vec4(0.0,1.0,0.0,0.0);//yes what we want

}



According to my second shader, i am supposed to obtain a green fullscreen if HDR works. But it seems my values are clamped during the first step, and i obtain a red screen ( if(color<1.1)gl_FragColor = vec4(1.0,0.0,0.0,0.0); )

1
1.1 can't be precisely represented by a float. Can you try comparing with 1.05? Or setting the value to 1.2?Reto Koradi

1 Answers

4
votes

Your code is a victim of limited floating point precision. Many values that can be represented precisely in the decimal system, like the 1.1 in your example, cannot be represented exactly in the binary-based floating point formats used by CPUs/GPUs.

This means that when you write 1.1 to your output, the actual value will be slightly more or slightly less. When you then compare the value with 1.1 in the second pass, the result will be more or less arbitrary, depending on how the value was rounded.

This might have worked if you stored the values as R32F, since the precision of the stored format would be the same as the precision of the arithmetic used during the comparison. But I would be nervous even in that case. Doing almost anything with float values, and hoping that the final result will be identical to the expected value, is dangerous unless you understand exactly what is done to the value.

In this case, since the value is stored as R16F (half-float), and then compared to the original value in 32-bit float precision, it will almost certainly not be the same.