Post-processing for 3D applications involves rendering a scene to a texture, referred to as a frame buffer object, and manipulating the data contained in the texture such that it looks different.
This week I completed 3 post-processing shaders. I completed a shader for controlling the brightness and contrast of an image, a shader for intensity profiles, and a curves adjustment shader. This blog will focus on the on the shader for intensity profiles.
Intensity Profiles/Levels Control
Levels control is a more precise way of controlling brightness and contrast in an image. To control levels the shader requires a minimum input (sometimes referred to as the input black-level), a maximum input (input white-level), a minimum output (output black-level), a maximum output (output white-level) and a value for gamma (middle grey).
Below is the shader code for levels
uniform sampler2D scene;
uniform float minInput;
uniform float maxInput;
uniform float gamma;
uniform float minOutput;
uniform float maxOutput;
void main()
{
//sample scene texture
vec3 color = texture2D(scene,gl_TexCoord[0].st).rgb;
//levels input range
color = min( max(color - vec3(minInput), vec3(0.0)) / (vec3(maxInput) - vec3(minInput)), vec3(1.0));
//gamma correction
color = pow(color, vec3(1.0 / gamma));
//levels output range
color = mix(vec3(minOutput), vec3(maxOutput), color);
gl_FragColor.rgb = color;
}
The levels input range line essentially rejects RGB values outside of the range of the minimum and maximum input exchanging them for either black or white (black if the color is less than the minimum input specified, and white if it's greater than the maximum input specified). Below is an example substituting the following values:
minInput = 0.2
maxInput = 0.9
gamma = 0.6;
minOutput = 0.2
maxOutput = 0.9
color = (0.1,0.1,0.1)
color = min( max(color - vec3(minInput), vec3(0.0)) / (vec3(maxInput) - vec3(minInput)), vec3(1.0));
color = min( max((0.1,0.1,0.1) - vec3(0.2), vec3(0.0)) / (vec3(0.9) - vec3(0.2)), vec3(1.0));
= min( max( (-0.1,-0.1,-0.1), (0.0,0.0,0.0) ) / (0.7,0.7,0.7), (1,1,1))
= min( (0.0,0.0,0.0)/(0.7,0.7,0.7), (1,1,1) )
= min( (0.0,0.0,0.0), (1,1,1) )
= (0,0,0)
As seen above, the value was below the minimum input and so it was rejected and exchanged for black. Had the color been (1.0,0.9,0.9) the color would have been rejected and replaced with white since the color was greater than the maximum input. If the color was within the range the final result of the above equation would simply be the original color with an adjustment. For example an original pixel color of (0.3,0.3,0.3) would return a color of (0.1428,0.1428,0.1428), and a color of (0.8,0.3,0.5) would result in a color of (0.8571, 0.1428,0.4285).
Next is a simple gamma correction. Gamma correction is used to adjust the luminance of pixels such that they are either brighter or darker. In the case of my shader increasing the gamma value will make the image brighter and decreasing the gamma value will make the image darker. Usually the equation is the same except we do not set our exponent to 1.0/gamma, it's usually just gamma. My small issue with this is that increasing the gamma value results in a darker image and decreasing it results in a lighter image, and that just didn't feel natural to me so I reversed it.
The levels output range uses the color value, adjusted from the levels input range and gamma, to linearly interpolate between the minimum and maximum outputs. The GLSL mix function does this for us:
color = mix(vec3(minOutput), vec3(maxOutput), color);
The mix function itself however uses the following logic:
mix(x,y,a) = vec3(dot(x,(1 - a)) + dot(y,a))
If we substitute a value for color of (0.1428,0.1428,0.1428) and use the minimum and maximum outputs stated above:
color = dot((0.2,0.2,0.2),(1,1,1) - (0.1428,0.1428,0.1428) ) + dot((0.9,0.9,0.9),(0.1428,0.1428,0.1428))
= dot((0.2,0.2,0.2),(0.8572,0.8572,0.8572)) + 0.38556
= 0.51432 + 0.38556
= vec3(0.89988)
Below are screenshots of an image with my shader effect being applied, and one without any effects.
Next is a simple gamma correction. Gamma correction is used to adjust the luminance of pixels such that they are either brighter or darker. In the case of my shader increasing the gamma value will make the image brighter and decreasing the gamma value will make the image darker. Usually the equation is the same except we do not set our exponent to 1.0/gamma, it's usually just gamma. My small issue with this is that increasing the gamma value results in a darker image and decreasing it results in a lighter image, and that just didn't feel natural to me so I reversed it.
The levels output range uses the color value, adjusted from the levels input range and gamma, to linearly interpolate between the minimum and maximum outputs. The GLSL mix function does this for us:
color = mix(vec3(minOutput), vec3(maxOutput), color);
The mix function itself however uses the following logic:
mix(x,y,a) = vec3(dot(x,(1 - a)) + dot(y,a))
If we substitute a value for color of (0.1428,0.1428,0.1428) and use the minimum and maximum outputs stated above:
color = dot((0.2,0.2,0.2),(1,1,1) - (0.1428,0.1428,0.1428) ) + dot((0.9,0.9,0.9),(0.1428,0.1428,0.1428))
= dot((0.2,0.2,0.2),(0.8572,0.8572,0.8572)) + 0.38556
= 0.51432 + 0.38556
= vec3(0.89988)
Below are screenshots of an image with my shader effect being applied, and one without any effects.
No comments:
Post a Comment