Photographic Noise Shader
attribute vec3 in_Position; // (x,y,z)
//attribute vec3 in_Normal; // (x,y,z) unused in this shader.
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec2 v_vPosition; //pass position data
void main()
{
vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
v_vColour = in_Colour;
v_vTexcoord = in_TextureCoord;
v_vPosition = in_Position.xy; //get position
}
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec2 v_vPosition;
uniform float seed; //seed passed in (increment in small values, ~0.0015 per step, for slower/more subtle effect)
float rand( vec2 p)
{
return fract( cos( dot( p, vec2(5.237,6.378)))* (8463.648 + seed)); //Xor's randomizing math with added seed modifier for animation
}
float noise( vec2 p) //noise function as found on https://xorshaders.weebly.com/tutorials/vertex-shader-4
{
float x1 = rand(vec2(floor(p.x),floor(p.y)));
float x2 = rand(vec2(ceil(p.x),floor(p.y)));
float top = mix(x1,x2,smoothstep(0.0,1.0,fract(p.x)));
x1 = rand(vec2(floor(p.x),ceil(p.y)));
x2 = rand(vec2(ceil(p.x),ceil(p.y)));
float bottom = mix(x1,x2,smoothstep(0.0,1.0,fract(p.x)));
return mix(top,bottom,smoothstep(0.0,1.0,fract(p.y)));
}
void main()
{
gl_FragColor = texture2D( gm_BaseTexture, v_vTexcoord ); //incoming color data
float weighted = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114)); //weighted brightness value as found on
//https://gmshaders.com/tutorials/tipsandtricks/
float n =
(noise(v_vPosition/1.0)*0.4 //smallest noise 40% of mix
+noise(v_vPosition/2.0)*0.3 //each line scales up and reduces % of mix to total 100% with 6 passes
+noise(v_vPosition/4.0)*0.1
+noise(v_vPosition/8.0)*0.1
+noise(v_vPosition/16.0)*0.1)
+ (sqrt(weighted)); //weighted brightness added to noise to blow out the brighter reas (reducing noise)
float dark_strength = 0.7; //increase to darken noise in bright areas
float light_strength = 0.5; //increase to lighten noise in dark areas
gl_FragColor *= (vec4(vec3(clamp(n,-0.2,1.2)),1.0)) * dark_strength; //controls the dark noise as it shows up in the brighter areas (multiply blending)
gl_FragColor += (vec4(vec3(clamp(1.0 - (n * 1.2),0.0,1.0) * 0.5),1.0)) * (1.0 - weighted) * light_strength; //additive pass to apply noise to dark areas
}
shader_set(sh_noise);
var shader_seed = shader_get_uniform(sh_noise,"seed"); //get seed uniform id
shader_set_uniform_f(shader_seed,seed); //send seed to shader
draw_surface(application_surface,0,0); //redraw application surface with shader applied
shader_reset();
The seed is set here so that it can be animated. Simply increment the variable each frame, smaller increments are better.
For a subtle effect I use seed += 0.0015; each step.
https://xorshaders.weebly.com/tutorials/vertex-shader-4
https://gmshaders.com/tutorials/tipsandtricks/
I would also like to thank the Xor Discord server for their assistance.
Feedback is appreciated, especially if you have any suggestions or improvements that will make the shader better.
Related Assets
Browse these game resources from the community
The great Jammening of 2022
A variety of assets made by me, Petra! For gm48 2022 this includes music, and Tiles!
Menus - buttons, sliders, and carousels
A struct based menu system with support for various element types.
Input System - binding, input buffering, and controller support
Take control of your game in a comprehensive, automated way.
State Machines, the struct-based approach
There's many ways to make a state machine. This approach let's you use structs to manage your states.