Hello, while writing a deferred renderer I ran into a weird issue with sRGB FBOs. If I enable framebuffer sRGB with glEnable(GL_FRAMEBUFFER_SRGB); and then try drawing onto an FBO with a sRGB color attachment, the colors aren’t converted to sRGB, they are written as-is, ONLY if there is also a depth buffer attached to the FBO. If I don’t attach the depth texture it works as expected. I’m on an nvidia 9600gt. I tested on a friend’s AMD card and it works fine either way there. Am I doing something wrong here, am I misinterpreting the spec (which doesn’t mention anything about this not working.) or is this a bug?
Here’s a test case project: http://dl.dropbox.com/u/1990844/2011-10/gamma-bug.7z
And the main file:
#include "gl/VertexArrayObject.hpp"
#include "gl/Shader.hpp"
#include "gl/ShaderProgram.hpp"
#include "gl/Framebuffer.h"
#include "gl/Texture.hpp"
#include "image/ImageLoader.hpp"
#include <iostream>
#include <fstream>
#include "gl3w.hpp"
#include <GL/glfw.h>
void APIENTRY debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam)
{
if ((type != GL_DEBUG_TYPE_PERFORMANCE_ARB && type != GL_DEBUG_TYPE_OTHER_ARB) || severity != GL_DEBUG_SEVERITY_LOW_ARB)
std::cerr << message << std::endl;
if ((type != GL_DEBUG_TYPE_PERFORMANCE_ARB && type != GL_DEBUG_TYPE_OTHER_ARB) || severity == GL_DEBUG_SEVERITY_HIGH_ARB)
__asm int 3; // Breakpoint
}
bool init_window()
{
if (glfwInit() != GL_TRUE)
{
char tmp;
std::cerr << "Failed to initialize GLFW." << std::endl;
std::cin >> tmp;
return false;
}
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
glfwOpenWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwOpenWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
if (glfwOpenWindow(800, 600, 8, 8, 8, 8, 24, 8, GLFW_WINDOW) != GL_TRUE)
{
char tmp;
std::cerr << "Failed to open window." << std::endl;
std::cin >> tmp;
return false;
}
glfwSwapInterval(1);
if (gl3wInit() != 0) {
char tmp;
std::cerr << "Failed to initialize gl3w." << std::endl;
std::cin >> tmp;
return false;
} else if (!gl3wIsSupported(3, 3)) {
char tmp;
std::cerr << "OpenGL 3.3 not supported." << std::endl;
std::cin >> tmp;
return false;
}
if (glDebugMessageCallbackARB) {
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
glDebugMessageCallbackARB(debug_callback, 0);
}
return true;
}
int main(int argc, char *argv[])
{
if (!init_window())
return 1;
{
gl::Shader vertex_shader(GL_VERTEX_SHADER);
gl::Shader frag_shader(GL_FRAGMENT_SHADER);
{
std::ifstream f("hack.vert");
vertex_shader.setSource(f);
}
{
std::ifstream f("hack.frag");
frag_shader.setSource(f);
}
vertex_shader.compile();
frag_shader.compile();
gl::ShaderProgram shader_program;
shader_program.attachShader(vertex_shader);
shader_program.attachShader(frag_shader);
shader_program.link();
if (!shader_program.linkSuccess())
shader_program.printInfoLog(std::cerr);
shader_program.use();
gl::VertexArrayObject vao;
vao.bind();
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
static const unsigned int foodata[] = {0, 1, 2};
glBufferData(GL_ARRAY_BUFFER, sizeof(foodata), &foodata, GL_STATIC_DRAW);
glVertexAttribPointer(0, 1, GL_UNSIGNED_INT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
gl::Texture source_tex;
{
image::Image img;
std::ifstream f("texture.png", std::ios::in | std::ios::binary);
if (!f)
return -1;
image::Image::loadPNGFileRGBA8(img, f);
source_tex.bind(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
source_tex.width = img.getWidth();
source_tex.height = img.getHeight();
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, source_tex.width, source_tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.getData());
}
glUniform1i(shader_program.getUniformLocation("tex"), 0);
gl::Texture depth_tex;
depth_tex.bind(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 800, 600, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
gl::Texture color_tex;
color_tex.bind(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
gl::Framebuffer fbo;
fbo.bind(GL_DRAW_FRAMEBUFFER);
// COMMENT THIS NEXT LINE!!!
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glClearColor(0.f, 0.f, 0.f, 1.f);
glEnable(GL_FRAMEBUFFER_SRGB);
bool running = true;
while (running) {
fbo.bind(GL_DRAW_FRAMEBUFFER);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
source_tex.bind(GL_TEXTURE_2D);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
color_tex.bind(GL_TEXTURE_2D);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers();
running = glfwGetWindowParam(GLFW_OPENED) != 0 && glfwGetKey(GLFW_KEY_ESC) != GLFW_PRESS;
}
}
glfwTerminate();
return 0;
}