I’d like to render a colour texture in gray. Doing it in ES 2.0 by using a shader is a piece of cake, but is it possible to do in ES 1.x?
UPDATE
Thanks to @datenwolf, I’m doing it like that:
GLfloat weights_vector[4] = {0.2126, 0.7152, 0.0722, 1.0};
GLfloat additions_vector[4] = {0.5, 0.5, 0.5, 0.0};
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
/* First part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, weights_vector);
/* Second part */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, additions_vector);
The first part renders fine if I leave it on its own, but if I add the second one, it uses “black” as the previous colour and so I get only gray pixels. Am I doing it wrong here?
SECOND UPDATE
If I try to use GL_TEXTURE0 instead of GL_PREVIOUS I indeed get the same result as GL_TEXTURE. However, if I used GL_TEXTURE1 I get not even gray pixels, but black. I’m getting lost here…
THIRD UPDATE
The second part is working now. Should’ve just used the name of the previous texture as @datenwolf had suggested!
However, the output was still not correct as it was inverted. I fixed that by adding:
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
Now, it’s too dark. And I can’t get it right. Tried and didn’t work:
- multiplying the weights by two
- adding 0.5
- modulating the whole image by 2
- multiplying the weights by 2 and then subtracting by 0.5
FOURTH UPDATE (Sorry there are so many)
I begin to suspect it’s not possible due to the bias and multiplication in DOT3_RGB. The correct way to do it in a combiner is:
- Add 0.5 to the input texture
- Calculate to weighting factors: weight_new = (weight_real + 2.0) / (4.0);
- Do a
GL_DOT3_RGB
For instance, instead of using:
GLfloat weights_vector[4] = {0.30, 0.59, 0.11, 0.0};
Use:
GLfloat weights_vector[4] = {0.575, 0.6475, 0.5275, 0.0};
This is indeed getting almost the right result, but some of the contrast is lost, due to the first step and the fact that numbers are clamped in the range [-1.0, 1.0]
Why the calculations? Well, according to the API:

So I don’t see any other way different from the one I showed and because of the accuracy limitation. Of course, I could first divide the input by 2.0, then add 0.5, do a dot3 and then again multiply by 2, thus effectively having all values in the range [-1.0, 0.0]. But I fear it would still loose accuracy because of the division.
I suspect that the DOT wasn’t intended for this purpose, more likely only for bumpmapping or something. Too bad. I hope I’m wrong, but I don’t see how.
This is possible using either 2 or 3 texture combiners. Most likely you are looking to support an older device (such as the iPhone 3G) which only supports 2 texture combiners. You can determine how many texture combiners your device supports using the following code:
The basic steps to achieve rendering a grayscale texture without shaders is this:
Now, each of these steps can be performed with a Texture Combiner. Or, alternatively, you can use only two texture combiners by replacing step 1 with custom code that adjusts your pixel values when you read in the texture. If you choose to use only two texture combiners you can still render your adjusted texture in color, you will just need a single texture combiner that doubles your RGB values before rendering.
The reason we are adding .5 to all of our pixel values is because the GL_DOT3_RGB equation we are using to calculate luminance will subtract .5 from each of our pixel values.
The reason we are dividing all pixel values by 2 is so that our values are not clamped when moving from step 2 to step 3. If we had a RGB value of (.5, .6, .7) and we added .5 to each of the RGB values, our resulting RGB value going into step 3 would be (1.0, 1.0, 1.0). After the DOT3 equation subtracts .5 from each value, it will be calculating luminance based on (.5, .5, .5).
Here is sample code that will render a texture grayscale using 3 texture units: