This is my import of the Film Grain shader for the Godot Game Engine - original shader is from Martinsh
/*
Film Grain post-process shader v1.1
Martins Upitis (martinsh) devlog-martinsh.blogspot.com
2013
imported to Godot by spl!te 2017
--------------------------
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
So you are free to share, modify and adapt it for your needs, and even use it for commercial use.
I would also love to hear about a project you are using it.
Have fun,
Martins
--------------------------
Perlin noise shader by toneburst:
http://machinesdontcare.wordpress.com/2009/06/25/3d-perlin-noise-sphere-vertex-shader-sourcecode/
*/
uniform vec2 screen_size;
uniform bool enabled = false;
float grain_amount = 0.05; //grain amount
bool colored = false; //colored noise?
float color_amount = 0.6;
float grain_size = 1.6; //grain particle size (1.5 - 2.5)
float lum_amount = 1.0;
//a random texture generator, but you can also use a pre-computed perturbation texture
vec4 rnm(vec2 tc)
{
float noise = sin(dot(tc + vec2(TIME,TIME),vec2(12.9898,78.233))) * 43758.5453;
float noiseR = fract(noise)*2.0-1.0;
float noiseG = fract(noise*1.2154)*2.0-1.0;
float noiseB = fract(noise*1.3453)*2.0-1.0;
float noiseA = fract(noise*1.3647)*2.0-1.0;
return vec4(noiseR,noiseG,noiseB,noiseA);
}
float fade(float t) {
return t*t*t*(t*(t*6.0-15.0)+10.0);
}
float pnoise3D(vec3 p)
{
vec3 pi = 0.00390625*floor(p);
pi = vec3(pi.x+0.001953125, pi.y+0.001953125, pi.z+0.001953125);
vec3 pf = fract(p); // Fractional part for interpolation
// Noise contributions from (x=0, y=0), z=0 and z=1
float perm00 = rnm(pi.xy).a ;
vec3 grad000 = rnm(vec2(perm00, pi.z)).rgb * 4.0;
grad000 = vec3(grad000.x - 1.0, grad000.y - 1.0, grad000.z - 1.0);
float n000 = dot(grad000, pf);
vec3 grad001 = rnm(vec2(perm00, pi.z + 0.00390625)).rgb * 4.0;
grad001 = vec3(grad001.x - 1.0, grad001.y - 1.0, grad001.z - 1.0);
float n001 = dot(grad001, pf - vec3(0.0, 0.0, 1.0));
// Noise contributions from (x=0, y=1), z=0 and z=1
float perm01 = rnm(pi.xy + vec2(0.0, 0.00390625)).a ;
vec3 grad010 = rnm(vec2(perm01, pi.z)).rgb * 4.0;
grad010 = vec3(grad010.x - 1.0, grad010.y - 1.0, grad010.z - 1.0);
float n010 = dot(grad010, pf - vec3(0.0, 1.0, 0.0));
vec3 grad011 = rnm(vec2(perm01, pi.z + 0.00390625)).rgb * 4.0;
grad011 = vec3(grad011.x - 1.0, grad011.y - 1.0, grad011.z - 1.0);
float n011 = dot(grad011, pf - vec3(0.0, 1.0, 1.0));
// Noise contributions from (x=1, y=0), z=0 and z=1
float perm10 = rnm(pi.xy + vec2(0.00390625, 0.0)).a ;
vec3 grad100 = rnm(vec2(perm10, pi.z)).rgb * 4.0;
grad100 = vec3(grad100.x - 1.0, grad100.y - 1.0, grad100.z - 1.0);
float n100 = dot(grad100, pf - vec3(1.0, 0.0, 0.0));
vec3 grad101 = rnm(vec2(perm10, pi.z + 0.00390625)).rgb * 4.0;
grad101 = vec3(grad101.x - 1.0, grad101.y - 1.0, grad101.z - 1.0);
float n101 = dot(grad101, pf - vec3(1.0, 0.0, 1.0));
// Noise contributions from (x=1, y=1), z=0 and z=1
float perm11 = rnm(pi.xy + vec2(0.00390625, 0.00390625)).a ;
vec3 grad110 = rnm(vec2(perm11, pi.z)).rgb * 4.0;
grad110 = vec3(grad110.x - 1.0, grad110.y - 1.0, grad110.z - 1.0);
float n110 = dot(grad110, pf - vec3(1.0, 1.0, 0.0));
vec3 grad111 = rnm(vec2(perm11, pi.z + 0.00390625)).rgb * 4.0;
grad111 = vec3(grad111.x - 1.0, grad111.y - 1.0, grad111.z - 1.0);
float n111 = dot(grad111, pf - vec3(1.0, 1.0, 1.0));
// Blend contributions along x
vec4 n_x = mix(vec4(n000, n001, n010, n011), vec4(n100, n101, n110, n111), fade(pf.x));
// Blend contributions along y
vec2 n_xy = mix(n_x.xy, n_x.zw, fade(pf.y));
// Blend contributions along z
float n_xyz = mix(n_xy.x, n_xy.y, fade(pf.z));
// We're done, return the final noise value.
return n_xyz;
}
//2d coordinate orientation thing
vec2 coordRot(vec2 tc, float angle)
{
float aspect = screen_size.x/screen_size.y;
float rotX = ((tc.x*2.0-1.0)*aspect*cos(angle)) - ((tc.y*2.0-1.0)*sin(angle));
float rotY = ((tc.y*2.0-1.0)*cos(angle)) + ((tc.x*2.0-1.0)*aspect*sin(angle));
rotX = ((rotX/aspect)*0.5+0.5);
rotY = rotY*0.5+0.5;
return vec2(rotX,rotY);
}
if (enabled)
{
vec3 rotOffset = vec3(1.425,3.892,5.835); //rotation offset values
vec2 rotCoordsR = coordRot(UV, TIME + rotOffset.x);
vec3 noise = vec3(pnoise3D(vec3(rotCoordsR*vec2(screen_size.x/grain_size,screen_size.y/grain_size),0.0)));
if (colored)
{
vec2 rotCoordsG = coordRot(UV, TIME + rotOffset.y);
vec2 rotCoordsB = coordRot(UV, TIME + rotOffset.z);
noise.g = mix(noise.r,pnoise3D(vec3(rotCoordsG*vec2(screen_size.x/grain_size,screen_size.y/grain_size),1.0)),color_amount);
noise.b = mix(noise.r,pnoise3D(vec3(rotCoordsB*vec2(screen_size.x/grain_size,screen_size.y/grain_size),2.0)),color_amount);
}
vec3 col = tex(TEXTURE, UV).rgb;
vec3 lumcoeff = vec3(0.299,0.587,0.114);
float luminance = mix(0.0,dot(col, lumcoeff),lum_amount);
float lum = smoothstep(0.2,0.0,luminance);
lum += luminance;
noise = mix(noise,vec3(0.0),pow(lum,4.0));
col = col+noise*grain_amount;
COLOR = vec4(col,1.0) * SRC_COLOR;
} else {
COLOR = tex(TEXTURE, UV) * SRC_COLOR;
}
Please note that this depends heavily on the structure of your game, there really isnt any "one solution fit them all!"
All you need is the Viewport
and the ViewportSprite
.
- Set
Viewport.RenderTarget
totrue
. - Set
Viewport.Rect
to your game default resolution - Set your
ViewportSprite.Viewport
property toViewport
- Add your scene as node under
Viewport
- Add new material, then add new shader to your
ViewportSprite
and set this shader as fragment shader (not vertex nor lightning) - Set property on material: ScreenSize (Vec2, same as
OS.get_window_size()
) - Set property on material: Enabled (bool)
- Enjoy.
You can do this by simple (build-in, why not...) script:
extends ViewportSprite
func _ready():
# if you have one resolution:
self.get_viewport().set_as_render_target(true)
self.get_viewport().set_rect(Rect2(Vector2(0,0), OS.get_window_size()))
self.get_material().set_shader_param("screen_size", OS.get_window_size())
self.get_material().set_shader_param("enabled", true)
# or write your own "resolution changed" listener (you got the idea...)
- It looks differently in-editor and ingame (becouse you does not edit in your game resolution) so just run your game before you throw it out. You can simply set
enabled=false
on your material andenabled=true
via script. - You can play with
grain_amount
,colored
,color_amount
,grain_size
andlum_amount
variables for different effects - Consider support of disabling Film Grain at all via ingame menu (some guys are alergic about this effect)
Martinsh released his shader under Creative Commons Attribution 3.0 (CC-by-30). You have to attribute him.
My tutorial is licensed under Public Domain (CC0). Do what you want.