Intermediate Computer Graphics Week 10: Misc Post-Processing Effects

For my last blog entry for this course, I figured I would just give a few miscellaneous post-processing effects, with explanations of how they work and a bit of code. The effects that I will talk about are color correction and hatching.

Color Correction

Color correction is simply the process of substituting certain colors in the scene for other colors. For example, you may want to add a “colorblind mode” to your game, which changes all of the colors so that they are more distinguishable to people who have certain types of colorblindness. Rather than adding new textures for all of the objects in the game for such modes, we can use a color correction shader with the appropriate color ramp to take care of this for us. A color ramp is a 1 by 256 pixel texture that simply indicates how the colors will be mapped. It is usually created in an image editing program by first creating a gradient that goes from black to white. Then, the ‘levels’ feature of the image editing software is used to modify the levels of each color channel. This ramp is used in our shader by first sampling the scene to get the current color, then for each color channel, the color ramp is sampled to obtain the output color.

The code for this effect would look something like this:

#version 420

in vec2 texCoord;

out vec4 color;

uniform sampler2D scene;
uniform sampler1D colorCorrectionMap;

void main()
{
	vec4 source = texture(scene, texCoord);

	color.r = texture(colorCorrectionMap, source.r).r;
	color.g = texture(colorCorrectionMap, source.g).g;
	color.b = texture(colorCorrectionMap, source.b).b;
	color.a = 1.0;
}

Here is an example of what a scene looks like before and after color correction, along with the ramp that I used:

ColorCorrection

The scene before and after applying color correction.

 

The color correction map that I used for the above scene.

The color correction map that I used for the above scene.

Hatching

Hatching is the process of using only lines to draw a scene, such that it looks like a sketch. The space between the lines is changed depending on the luminance of that particular fragment, with darker areas having less space between the lines. This effect is achieved in a post-processing fragment shader by sampling the scene, taking the luminance of the sample, and then outputting black only if the location of the fragment matching the spacing requirement with respect to the luminance. This is a somewhat difficult concept to grasp just by description, so the easiest way is just to show you some code:

#version 420

#define HATCH_Y_OFFSET_1 10.0
#define HATCH_Y_OFFSET_2 5.0
#define HATCH_Y_OFFSET_3 15.0

in vec2 texCoord;

out vec4 color;

uniform sampler2D tex;

void main()
{
	vec3 source = texture(tex, texCoord).rgb;
	color = vec4(1.0, 1.0, 1.0, 1.0);

	// I found that this way of calculating the luminance looks better for hatching
	// than the way we did it in the grayscale post processor from week 5.
	float luminance = length(source);

	// First level of lines that happens every 20 pixels for light colors.
	if(luminance < 0.8)
	{
		if(mod(gl_FragCoord.x + gl_FragCoord.y, 20.0) == 0.0)
		{
			color = vec4(0.0, 0.0, 0.0, 1.0);
		}
	}

	// Second level of lines that repeat every 10 pixels for mid-level colors.
	if(luminance < 0.5)
	{
		if (mod(gl_FragCoord.x + gl_FragCoord.y - HATCH_Y_OFFSET_1, 20.0) == 0.0)
		{
			color = vec4(0.0, 0.0, 0.0, 1.0);
		}
	}  

	// Third level of lines that repeat every 5 pixels for dark colors.
	if (luminance < 0.25)
	{
		if (mod(gl_FragCoord.x + gl_FragCoord.y - HATCH_Y_OFFSET_2, 20.0) == 0.0 || mod(gl_FragCoord.x + gl_FragCoord.y - HATCH_Y_OFFSET_3, 10.0) == 0.0)
		{
			color = vec4(0.0, 0.0, 0.0, 1.0);
		}
	}
}

The below image shows what this effect looks like in a game scene. This effect can be extrapolated by the reader to create a cross-hatching effect, which is really just adding lines at the same spacing, but reversing their direction.

Hatching

Leave a comment