views:

861

answers:

3

I want to change the brightness and contrast of a texture on the iPhone. I've been looking at the sample provided by apple (GLImageProcessing) but it only is able to do one at a time (calling both methods in the sample overwrites the previous result).

Brightness works great:

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
if (brightness >= 1.0f) {
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
    glColor4f(brightness-1, brightness-1, brightness-1, brightness-1);
} else {
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
    glColor4f(1-brightness, 1-brightness, 1-brightness, 1-brightness);
}
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

How would i go with adding contrast to the texture after this? Alternatively would it be a better choice to move to OpenGL ES 2.0 and do this with shaders?

Here's the code for contrast in the sample from apple:

glActiveTexture(GL_TEXTURE0);
glVertexPointer  (2, GL_FLOAT, sizeof(V2fT2f), &quad[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &quad[0].s);

glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_INTERPOLATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);

glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD_SIGNED);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 2);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);

glColor4f(h, h, h, 0.75 - 0.5 * h); // 2x extrapolation
validateTexEnv();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// Restore state
glDisable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1);
glActiveTexture(GL_TEXTURE0);

As you can probably guess my experience with OpenGL (ES) is pretty limited so anything that points me in the right direction is greatly appreciated

A: 

This is much easier to do with a fragment shader, at least in OpenGL 2.1 (I haven't used OpenGL ES). The book "OpenGL Shading Language" covers image processing in chapter 19. They give shaders for adjusting brightness and contrast. Here is my version, which combines the two half-and-half:

uniform sampler2D Texture;
uniform float Brightness;
uniform float Contrast;
uniform vec4 AverageLuminance;

void main(void)
{
   vec4 texColour = texture2D(Texture, gl_TexCoord[0].st);
   gl_FragColor = mix(texColour * Brightness, 
      mix(AverageLuminance, texColour, Contrast), 0.5);
}

Edit: some more details for completeness...

Brightness and contrast have a base value of 1.0, so passing that in for both will leave the texture unchanged. A value less than 1.0 will decrease brightness, and a value greater than 1.0 will increase it. A value less than 1.0 for contrast will make the texture more grey (less contrast), and a value greater than 1.0 will make it less grey (more contrast).

You can pass in (0.5, 0.5, 0.5, 1.0) for AverageLuminance for a quick-and-dirty grey value, but better results will be obtained by calculating a proper average luminance for your texture.

Incredulous Monk
I prefer to stick to OpenGL ES 1.1 (due to device support), which doesn't support fragment shaders. Any thoughts on doing this without shaders? Thanks for this though, it's good to have as a backup
Antti
You're welcome. It gave me a good excuse to do some more playing around with shaders :-) Sorry, I probably won't be of much help with a non-shader solution. Texture combiners give me a headache at the best of times, and those Apple ones are pretty gruesome.
Incredulous Monk
A: 

My GL is a bit rusty, but from the sample code these are both creating a texture to blend with the existing scene. Therefore you may be able to just create both textures, making sure they don't stomp on each other and then blend both of them in place. They're doing different things though so I doubt you'll be able to combine them in the same texture. I'd order the contrast first since it's a more complicated blend.

Ben Lachman
A: 

You could render the brightness-adjusted image to a framebuffer texture of the same size, then perform the contrast adjustment as you render this texture to the screen.

Image -> [Brightness] -> Texture -> [Contrast] -> Screen

Some useful reading on framebuffers and render-to-texture:

gavinb