In our third shaders tutorial we will use the knowledge we have gained from the second tutorial and we use it to create gray scaled textures. You can create a texture that is already gray scaled and use it with our simple texture shader and you are right, however, there are some situation in which you want to be able to do it runtime. We will show you various forms of gray scaling available using shaders.

# Gray scale methods

We have this system of graying out visuals in our game ‘Castles Under Siege‘. As you can see in the screenshot below we have grayed out the cards that the player can not play due to a lack of resources. This means that we need to change the card from a colored textured quad to a grayscale textured quad. We can not do this beforehand in Gimp or Photoshop. Well we could, but that would mean we have to make each resource 2 times, once colored, once gray scaled. This is not what we want, so we need (or should use) shaders for this.

Making an image gray is not a difficult task, you simply take the red, green and blue component of a pixel, sum them up and divide them by 3. You can use the resulting value for each component making the pixel gray. But is it really so easy? Well, yes and no. This simple function may work fine and all is well and simple, but it is not the answer to all situations, therefor, we will present you with the most used types of gray scaling functions as we know it:

### The vertex shader

For all the gray scaled functions listed here we use the same vertex shader. Our sourcecode also uses only one string for the vertex shader which is:

public static final String vs_GrayGeneral = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "attribute vec2 a_texCoord;" + "varying vec2 v_texCoord;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + " v_texCoord = a_texCoord;" + "}";

Almost all the shaders are very basic code-wise. You should be able to understand all this code if you have done the previous tutorials but we will add some explanation to the code if we use new functions.

## Grayscale: Average

The average gray scaling takes the three attributes of the RGB color, sum them up and then divide them by three. The shader:

The shader code:

public static final String fs_GrayAverage = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " float c;" + " c = (tex.r + tex.g + tex.b) / 3.0;" + " vec4 pixel = vec4( c, c, c, tex.a);" + " gl_FragColor = vec4(pixel);" + "}";

## Grayscale: Seperate Channel

You can use just one color channel instead of all three like with the average version. So lets say we use the red channel of a pixel. We use that value for the green and blue as well, making it gray again. This can be useful in all sorts of situations but it really comes down to your specific situation. Because we only use one channel, the quality of the resulting gray scaled image may show some unwanted effects. Full green or full blue will show the same resulting color for example when using the red channel. The shaders:

public static final String fs_GrayChannelRed = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " vec4 pixel = vec4( tex.r, tex.r, tex.r, tex.a);" + " gl_FragColor = vec4(pixel);" + "}";

public static final String fs_GrayChannelGreen = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " vec4 pixel = vec4( tex.g, tex.g, tex.g, tex.a);" + " gl_FragColor = vec4(pixel);" + "}";

public static final String fs_GrayChannelBlue = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " vec4 pixel = vec4( tex.b, tex.b, tex.b, tex.a);" + " gl_FragColor = vec4(pixel);" + "}";

## Grayscale: Lightness (desaturate)

The lightness tends to lower the contrast in comparison to the other gray scaled versions.

public static final String fs_GrayLightness = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " float max = max(tex.r, tex.g);" + " max = max(max, tex.b);" + " float min = min(tex.r, tex.g);" + " min = min(min, tex.b);" + " float r = (max + min) / 2;" + " vec4 pixel = vec4( r, r, r, tex.a);" + " gl_FragColor = vec4(pixel);" + "}";

This code uses 2 functions we have not yet encountered; min and max. These functions are quite easy to understand though. The max function returns the highest float value passed to it. The min function returns the lowest float value passed to it. You can use the min and max functions also with other types such as vec2 etc. so beware that you pass the correct types for which you want to use the functions.

## Grayscale: Decomposition Maximum

public static final String fs_GrayDecomposeMax = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " float h = max(tex.r, tex.g);" + " h = max(h, tex.b);" + " gl_FragColor = vec4( h, h, h, tex.a);" + "}";

## Grayscale: Decomposition Minimum

public static final String fs_GrayDecomposeMin = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " float h = min(tex.r, tex.g);" + " h = min(h, tex.b);" + " gl_FragColor = vec4( h, h, h, tex.a);" + "}";

## Grayscale: Luminosity (BT709)

The idea of luminosity is that green light is the largest contributor of the intensity that is perceived by us humans. Red less and Blue the least. The shader:

public static final String fs_GrayLuminosity = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " vec4 pixel = vec4( tex.r * 0.2126, tex.g * 0.7152, tex.b * 0.0722, tex.a);" + " gl_FragColor = vec4(pixel);" + "}";

## Grayscale: Luminosity (CCIR 601)

The idea of luminosity is that green light is the largest contributor of the intensity that is perceived by us humans. Red less and Blue the least. There is much debate on which luminosity formula is the best so we just list them both. The shader:

public static final String fs_GrayCCIR = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " float c;" + " c = (tex.r * 0.299 + tex.g * 0.587 + tex.b * 0.114);" + " gl_FragColor = vec4(c, c, c, tex.a);" + "}";

## Grayscale: RMY

Just another gray scale that takes the color attributes by a certain formula:

public static final String fs_GrayRMY = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " vec4 tex = texture2D( s_texture, v_texCoord );" + " float c;" + " c = (tex.r * 0.5 + tex.g * 0.419 + tex.b * 0.081);" + " gl_FragColor = vec4(c, c, c, tex.a);" + "}";

# Complex multipass systems

There are also a lot of great algorithms to make well looking gray images from colored ones, but this list is not usable for real-time calculation but for those who are making a photoshop kind of app, here is a list with the corresponding websites to learn more about those techniques:

### The source code:

### Tutorial index

- OpenGL ES 2.0 2D Shaders series: 001 Basic shaders
- OpenGL ES 2.0 2D Shaders series: 002 Colored Textures Shaders
- OpenGL ES 2.0 2D Shaders series: 003 Gray Scaled Texture Shaders