This is quite involved, and if you're new to shaders and FBO's it will take some time to understand it. So here is some minimal, untested OpenGL code that implements Danvil's answer. Error checking has been left out; it's complicated enough as it is. If you call this function many times, you should hold on to any or all of the objects created in this code.
If you don't care about speed, VilleK's answer that runs on the CPU is a lot easier...
// Create the target texture object.
GLuint target;
glGenTextures(1, &target);
// Bind the target texture.
glBindTexture(GL_TEXTURE_2D, target);
// Allocate texture memory. width and height must be the size of the original texture.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// Create a framebuffer object.
GLuint fbo;
glGenFramebuffers(1, &fbo);
// Bind the framebuffer object.
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Attach the target texture as the render target.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target, 0);
// Check framebuffer status.
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw "Framebuffer incomplete.";
// Create fragment shader to do the transformation.
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
// Set the shader's source code.
char const *source =
"uniform sampler2D input;\n"
"uniform float hShift, sMult, lMult;\n"
"void main() {\n"
" vec4 color = texture2D(input, gl_FragCoord.xy);\n"
" /* Do your HSL transformations here... */\n"
" gl_FragColor = color;\n"
"}\n";
glShaderSource(frag, 1, &source, 0);
// Compile the shader.
glCompileShader(frag);
// Check compilation result. Here, you probably want to use glGetShaderInfoLog() to get any compilation errors.
GLint status;
glGetShader(frag, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
throw "Shader compilation failed";
// Create the program.
GLuint program = glCreateProgram();
// Attach the shader to the program.
glAttachShader(program, frag);
// Link the program.
glLinkProgram(program);
// Check link result. Here, you probably want to use glGetProgramInfoLog() to get any link errors.
glGetProgram(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
throw "Program linking failed"
// Use the program for subsequent rendering.
glUseProgram(program);
// Set the values of the uniform parameters.
glUniform1i(glUniformLocation(program, "input"), 0);
glUniform1f(glUniformLocation(program, "hShift"), hShift);
glUniform1f(glUniformLocation(program, "sMult"), sMult);
glUniform1f(glUniformLocation(program, "lMult"), lMult);
// Bind the source texture to read from.
glBindTexture(GL_TEXTURE_2D, texture);
// Set up the viewport and matrices.
glPushAttrib(GL_VIEWPORT_BIT | GL_TRANSFORM_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, 1, 0, 1, 0, 1);
glViewport(0, 0, width, height);
// Render a quad.
glBegin(GL_QUADS);
glVertex2i(0, 0);
glVertex2i(1, 0);
glVertex2i(1, 1);
glVertex2i(0, 1);
glEnd();
// Restore the matrices and viewport.
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glPopAttrib();
// Stop using the program.
glUseProgram(0);
// Stop using the framebuffer object.
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Delete created objects.
glDeleteProgram(program);
glDeleteShader(frag);
glDeleteFramebuffers(1, &fbo);
// Optionally, delete the old, untransformed texture.
glDeleteTextures(1, &texture);
// Return the new texture.
return target;
Hope that helps. For pre-2.0 OpenGL, you need to load the proper extensions first, and slap on some EXT
s and/or ARB
s here and there.
If you're willing to go down this road, and it doesn't work out, don't hesitate to post a comment describing the problems you encounter. I'll be happy to help!