The following is an implementation of an overlay blend algorithm in GLSL, drawn from the OpenGL Shading® Language, Third Edition:
19.6.12 Overlay
OVERLAY first computes the luminance
of the base value.If the luminance value is less than
0.5, the blend and base values are multiplied together.If the luminance value is greater than
0.5, a screen operation is performed.The effect is that the base value is
mixed with the blend value, rather
than being replaced. This allows
patterns and colors to overlay the
base image, but shadows and highlights
in the base image are preserved.A discontinuity occurs where luminance
= 0.5. To provide a smooth transition, we actually do a linear blend of the
two equations for luminance in the
range [0.45,0.55].
float luminance = dot(base, lumCoeff);
if (luminance < 0.45)
result = 2.0 * blend * base;
else if (luminance > 0.55)
result = white - 2.0 * (white - blend) * (white - base);
else {
vec4 result1 = 2.0 * blend * base;
vec4 result2 = white - 2.0 * (white - blend) * (white - base);
result = mix(result1, result2, (luminance - 0.45) * 10.0);
}
How would I implement something similar in OpenGL ES 1.1 (targeting the iPhone 3G), without using shaders? Can I use a blend function or texture combine to implement this?
For the purposes of leaving an answer on the record and supposing there are no further optimisations you can make, you could:
1) Load luminance value into the alpha channel
Set up a framebuffer object, the same size as the original texture. Use glColorMask to enable or disable writing to different channels. First of all, enable red, green and blue channels and disable the alpha channel. Draw the texture normally. This will duplicate the texture’s colour information.
Then enable the alpha channel and disable the red, green and blue channels. Use the dot3 extension (which has been supported on the iPhone since the beginning) to fill the target alpha channel with luminance values.
2) Split the texture into three textures, based on luminance
A simple scheme would be just to split at luminance = 0.5 and ignore the linear blend. If you were to do that, you could again use framebuffer objects to split a texture on the GPU. This time use the alpha function (glAlphaFunc and be sure to enable it) to pass all those areas with an alpha greater than 0.50 when drawing to one texture, to pass all those features with an alpha less than 0.50 when drawing to another.
Although you can do only one alpha test per pixel, meaning that you can’t separate out the range 0.45 to 0.55 in a single step, you could do that in two steps.
3) Use normal blend modes to composite the two or three textures onto your framebuffer
You can subvert the lighting system to offset and scale the alpha channels during rendering if necessary.
Obviously you’d optimise by performing those steps that are identical every draw just once, at startup. Which probably means permanently storing what is currently one texture as two or three.