This is part of the WebGL image processing series and it relies on information in previous articles. See all articles here.
It is designed to be used on desktop.
What is vignetting?
Vignetting is the reduction of an image's brightness or saturation towards its edges. While this was historically an undesired effect in photography – an indicator of incorrect camera settings or issues with the lens – it can also be used to lead the viewer's eye to a specific point on an image.
We look at code from Tyler Lindberg's glsl-vignette here, which is licensed on a MIT license.
In its simplest form we can take the distance from the center of the image and create a radial gradient that darkerns towards the edges. I've included an additional preview of the vignette over a white background to get a complete picture of the vignette's shape.
float vignette(vec2 uv, float radius){
return radius - distance(uv, vec2(0.5));
}
void main() {
// map uv between 0 -> 1
vec2 uv = gl_FragCoord.xy/u_resolution;
// load image
vec4 texel = texture(u_image, uv);
// apply vignette to texel (image)
outColor = texel * vignette(uv, u_radius);
}
Smoothstep
The main issue with the above approach is that we lose a lot of detail from the center of the image. We can use smoothstep to control how smooth the fade is between our image and the darkness at the edges.
// Based on https://github.com/TyLindberg/glsl-vignette/blob/master/simple.glsl
float vignette(
vec2 uv,
float radius, // (0 - 1)
float smoothness // (0 - 1)
) {
float diff = radius - distance(uv, vec2(0.5));
return smoothstep(-smoothness, smoothness, diff);
}
void main() {
// map uv between 0 -> 1
vec2 uv = gl_FragCoord.xy/u_resolution;
// load image
vec4 texel = texture(u_image, uv);
// apply vignette to texel (image)
outColor = texel * vignette(uv, u_radius, u_smoothness);
}
Vignette shape
We can also use separate values for our width and height to control the shape of our vignette.
// Based on https://github.com/TyLindberg/glsl-vignette/blob/master/advanced.glsl
// Based on distance functions found at:
// http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
float sdSquare(vec2 point, float width) {
vec2 d = abs(point) - width;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}
float vignette(
vec2 uv,
vec2 size, // (0 - 0.5)
float roundness, // (0 - 1)
float smoothness // (0 - 1)
) {
// Center UVs
uv -= 0.5;
// Shift UVs based on the larger of width or height
float minWidth = min(size.x, size.y);
uv.x = sign(uv.x) * clamp(abs(uv.x) - abs(minWidth - size.x), 0.0, 1.0);
uv.y = sign(uv.y) * clamp(abs(uv.y) - abs(minWidth - size.y), 0.0, 1.0);
// Signed distance calculation
float boxSize = minWidth * (1.0 - roundness);
float dist = sdSquare(uv, boxSize) - (minWidth * roundness);
return 1.0 - smoothstep(0.0, smoothness, dist);
}
void main() {
// map uv between 0 -> 1
vec2 uv = gl_FragCoord.xy/u_resolution;
vec4 texel = texture(u_image, uv);
outColor = texel * vignette(uv, vec2(u_width, u_height), u_roundness, u_smoothness);
}
In the next article, we'll be exploring how to simulate the effect of chromatic aberration.