Back to shaders
Shader test bench
Old Film.fs
runnable fragment
Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.
Code
uniform sampler2D inputImage;
uniform vec4 shadowColor;
uniform vec4 highlightColor;
uniform float gamma;
uniform float flickerAmount;
uniform float flickerRate;
uniform float randomness;
uniform int enableScratches;
uniform float scratchNumber;
uniform float scratchSpeed;
uniform float scratchBrightness;
uniform float scratchThickness;
uniform float scratchFlickerRate;
uniform float scratchWiggle;
uniform float scratchPosition;
uniform float vignetteAmount;
uniform int enableGrain;
uniform float grainAmount;
/*{
"CREDIT": "ProjectileObjects / VIDVOX",
"DESCRIPTION": "Old film look with sepia false-color, gamma, flicker, optional solid moving film scratches, vignette, and grain overlays.",
"ISFVSN": "2",
"CATEGORIES": ["Filter"],
"INPUTS": [
{
"NAME": "inputImage",
"TYPE": "image"
},
{
"NAME": "shadowColor",
"TYPE": "color",
"DEFAULT": [0.2, 0.15, 0.07, 1.0],
"DESCRIPTION": "False color for shadow regions (e.g. sepia shadows)"
},
{
"NAME": "highlightColor",
"TYPE": "color",
"DEFAULT": [1.0, 0.9, 0.7, 1.0],
"DESCRIPTION": "False color for highlight regions (e.g. sepia highlights)"
},
{
"NAME": "gamma",
"TYPE": "float",
"DEFAULT": 1.0,
"MIN": 0.1,
"MAX": 3.0,
"DESCRIPTION": "Gamma correction"
},
{
"NAME": "flickerAmount",
"TYPE": "float",
"DEFAULT": 0.1,
"MIN": 0.0,
"MAX": 1.0,
"DESCRIPTION": "How strong the flicker effect is"
},
{
"NAME": "flickerRate",
"TYPE": "float",
"DEFAULT": 10.0,
"MIN": 1.0,
"MAX": 15.0,
"DESCRIPTION": "Frequency of the flicker effect (up to 15 FPS)"
},
{
"NAME": "randomness",
"TYPE": "float",
"DEFAULT": 0.5,
"MIN": 0.0,
"MAX": 1.0,
"DESCRIPTION": "Adds randomness to flicker and scratches"
},
{
"NAME": "enableScratches",
"TYPE": "bool",
"DEFAULT": false,
"DESCRIPTION": "Toggle film scratch lines overlay"
},
{
"NAME": "scratchNumber",
"TYPE": "float",
"DEFAULT": 1.0,
"MIN": 1.0,
"MAX": 20.0,
"DESCRIPTION": "Number of scratch lines"
},
{
"NAME": "scratchSpeed",
"TYPE": "float",
"DEFAULT": 1.0,
"MIN": 0.1,
"MAX": 5.0,
"DESCRIPTION": "Speed of scratch movement"
},
{
"NAME": "scratchBrightness",
"TYPE": "float",
"DEFAULT": 0.9,
"MIN": 0.0,
"MAX": 1.0,
"DESCRIPTION": "Brightness of the scratches"
},
{
"NAME": "scratchThickness",
"TYPE": "float",
"DEFAULT": 0.01,
"MIN": 0.001,
"MAX": 0.05,
"DESCRIPTION": "Thickness of the scratch lines"
},
{
"NAME": "scratchFlickerRate",
"TYPE": "float",
"DEFAULT": 5.0,
"MIN": 1.0,
"MAX": 15.0,
"DESCRIPTION": "Flicker rate of scratches (up to 15 FPS)"
},
{
"NAME": "scratchWiggle",
"TYPE": "float",
"DEFAULT": 0.05,
"MIN": 0.0,
"MAX": 0.1,
"DESCRIPTION": "Amount of scratch wiggle along X-axis"
},
{
"NAME": "scratchPosition",
"TYPE": "float",
"DEFAULT": 0.0,
"MIN": -0.5,
"MAX": 0.5,
"DESCRIPTION": "Base X position of the scratches"
},
{
"NAME": "vignetteAmount",
"TYPE": "float",
"DEFAULT": 0.5,
"MIN": 0.0,
"MAX": 1.0,
"DESCRIPTION": "Amount of vignette (dark edges)"
},
{
"NAME": "enableGrain",
"TYPE": "bool",
"DEFAULT": true,
"DESCRIPTION": "Toggle film grain overlay"
},
{
"NAME": "grainAmount",
"TYPE": "float",
"DEFAULT": 0.2,
"MIN": 0.0,
"MAX": 1.0,
"DESCRIPTION": "Intensity of film grain"
}
]
}*/
#ifdef GL_ES
precision mediump float;
#endif
// Calculate the luminance of a color (perceived brightness)
float luminance(vec3 c) {
return dot(c, vec3(0.299, 0.587, 0.114));
}
// Generate pseudo-random noise based on a seed
float randNoise(float seed) {
return fract(sin(seed) * 43758.5453);
}
// Generate vertical scratch lines
float scratchOverlay(vec2 uv, float scratchCount, float randomness, float speed, float thickness, float flickerRate, float wiggle, float position) {
float lines = 0.0;
// Scroll value to move scratches vertically
float scroll = mod(u_time * speed, 1.0);
// Loop to generate multiple scratches
for (float i = 0.0; i < 20.0; i++) {
if (i >= scratchCount) break; // Limit to the desired number of scratches
// Generate a random X position for the scratch with wiggle
float xPos = position + randNoise(i * 0.1234 + randomness) * wiggle * sin(u_time + i);
// Calculate the distance of the pixel from the scratch line
float lineDist = abs(uv.x - xPos);
// Create a solid vertical line with adjustable thickness
float lineVal = smoothstep(thickness, 0.0, lineDist);
// Apply flicker effect to scratches
float flicker = step(0.5, randNoise(i + floor(u_time * flickerRate)));
// Combine the flicker effect with the scratch line
lineVal *= flicker;
lines = max(lines, lineVal); // Keep the strongest line value
}
return lines;
}
void main() {
// Normalize pixel coordinates
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
// Sample the input image at the current coordinates
vec4 col = texture2D(inputImage, uv);
// Apply false color effect
float lum = luminance(col.rgb); // Calculate luminance of the pixel
vec3 falseColor = mix(shadowColor.rgb, highlightColor.rgb, lum); // Map luminance to shadow/highlight colors
col = vec4(falseColor, col.a);
// Apply gamma correction
col.rgb = pow(col.rgb, vec3(1.0 / max(0.0001, gamma)));
// Apply flicker effect by modulating brightness over time
float flicker = 1.0 + flickerAmount * sin(u_time * flickerRate);
col.rgb *= flicker;
// Apply film scratches if enabled
if (enableScratches) {
float lines = scratchOverlay(uv, scratchNumber, randomness, scratchSpeed, scratchThickness, scratchFlickerRate, scratchWiggle, scratchPosition);
float scratchEffect = mix(1.0, 0.0, scratchBrightness); // Adjust scratch brightness
col.rgb = mix(col.rgb, vec3(scratchEffect), lines); // Overlay scratches on the image
}
// Apply vignette effect
vec2 centeredUV = uv - 0.5;
centeredUV.x *= u_resolution.x / u_resolution.y; // Adjust for aspect ratio
float dist = length(centeredUV); // Distance from the center
float vig = smoothstep(0.7, 0.3, dist * (1.0 + vignetteAmount)); // Create vignette gradient
col.rgb *= mix(1.0, vig, vignetteAmount); // Darken edges based on vignette amount
// Apply grain effect if enabled
if (enableGrain) {
float noiseVal = randNoise(gl_FragCoord.x * gl_FragCoord.y + u_time * 50.0); // Generate random noise
noiseVal = (noiseVal - 0.5) * grainAmount; // Adjust grain intensity
col.rgb += noiseVal; // Add noise to the color
}
// Output the final color
gl_FragColor = vec4(col.rgb, 1.0);
}