FilmGrain2.fx (5298B)
1 /** 2 * Film Grain post-process shader v1.1 3 * Martins Upitis (martinsh) devlog-martinsh.blogspot.com 2013 4 * 5 * This work is licensed under a Creative Commons Attribution 3.0 Unported License. 6 * So you are free to share, modify and adapt it for your needs, and even use it for commercial use. 7 * 8 * Uses perlin noise shader by toneburst from http://machinesdontcare.wordpress.com/2009/06/25/3d-perlin-noise-sphere-vertex-shader-sourcecode/ 9 */ 10 11 #include "ReShadeUI.fxh" 12 13 uniform float grainamount < __UNIFORM_SLIDER_FLOAT1 14 ui_min = 0.0; ui_max = 0.2; 15 ui_label = "Amount"; 16 > = 0.05; 17 uniform float coloramount < __UNIFORM_SLIDER_FLOAT1 18 ui_min = 0.0; ui_max = 1.0; 19 ui_label = "Color Amount"; 20 > = 0.6; 21 uniform float lumamount < __UNIFORM_SLIDER_FLOAT1 22 ui_min = 0.0; ui_max = 1.0; 23 ui_label = "Luminance Amount"; 24 > = 1.0; 25 26 uniform float grainsize < __UNIFORM_SLIDER_FLOAT1 27 ui_min = 1.5; ui_max = 2.5; 28 ui_label = "Grain Particle Size"; 29 > = 1.6; 30 31 #include "ReShade.fxh" 32 33 uniform float timer < source = "timer"; >; 34 35 float4 rnm(in float2 tc) 36 { 37 // A random texture generator, but you can also use a pre-computed perturbation texture 38 float noise = sin(dot(tc, float2(12.9898, 78.233))) * 43758.5453; 39 40 float noiseR = frac(noise) * 2.0 - 1.0; 41 float noiseG = frac(noise * 1.2154) * 2.0 - 1.0; 42 float noiseB = frac(noise * 1.3453) * 2.0 - 1.0; 43 float noiseA = frac(noise * 1.3647) * 2.0 - 1.0; 44 45 return float4(noiseR, noiseG, noiseB, noiseA); 46 } 47 float pnoise3D(in float3 p) 48 { 49 // Perm texture texel-size 50 static const float permTexUnit = 1.0 / 256.0; 51 // Half perm texture texel-size 52 static const float permTexUnitHalf = 0.5 / 256.0; 53 54 // Integer part 55 // Scaled so +1 moves permTexUnit texel and offset 1/2 texel to sample texel centers 56 float3 pi = permTexUnit * floor(p) + permTexUnitHalf; 57 // Fractional part for interpolation 58 float3 pf = frac(p); 59 60 // Noise contributions from (x=0, y=0), z=0 and z=1 61 float perm00 = rnm(pi.xy).a; 62 float3 grad000 = rnm(float2(perm00, pi.z)).rgb * 4.0 - 1.0; 63 float n000 = dot(grad000, pf); 64 float3 grad001 = rnm(float2(perm00, pi.z + permTexUnit)).rgb * 4.0 - 1.0; 65 float n001 = dot(grad001, pf - float3(0.0, 0.0, 1.0)); 66 67 // Noise contributions from (x=0, y=1), z=0 and z=1 68 float perm01 = rnm(pi.xy + float2(0.0, permTexUnit)).a; 69 float3 grad010 = rnm(float2(perm01, pi.z)).rgb * 4.0 - 1.0; 70 float n010 = dot(grad010, pf - float3(0.0, 1.0, 0.0)); 71 float3 grad011 = rnm(float2(perm01, pi.z + permTexUnit)).rgb * 4.0 - 1.0; 72 float n011 = dot(grad011, pf - float3(0.0, 1.0, 1.0)); 73 74 // Noise contributions from (x=1, y=0), z=0 and z=1 75 float perm10 = rnm(pi.xy + float2(permTexUnit, 0.0)).a; 76 float3 grad100 = rnm(float2(perm10, pi.z)).rgb * 4.0 - 1.0; 77 float n100 = dot(grad100, pf - float3(1.0, 0.0, 0.0)); 78 float3 grad101 = rnm(float2(perm10, pi.z + permTexUnit)).rgb * 4.0 - 1.0; 79 float n101 = dot(grad101, pf - float3(1.0, 0.0, 1.0)); 80 81 // Noise contributions from (x=1, y=1), z=0 and z=1 82 float perm11 = rnm(pi.xy + float2(permTexUnit, permTexUnit)).a; 83 float3 grad110 = rnm(float2(perm11, pi.z)).rgb * 4.0 - 1.0; 84 float n110 = dot(grad110, pf - float3(1.0, 1.0, 0.0)); 85 float3 grad111 = rnm(float2(perm11, pi.z + permTexUnit)).rgb * 4.0 - 1.0; 86 float n111 = dot(grad111, pf - float3(1.0, 1.0, 1.0)); 87 88 // Blend contributions along x 89 float fade_x = pf.x * pf.x * pf.x * (pf.x * (pf.x * 6.0 - 15.0) + 10.0); 90 float4 n_x = lerp(float4(n000, n001, n010, n011), float4(n100, n101, n110, n111), fade_x); 91 92 // Blend contributions along y 93 float fade_y = pf.y * pf.y * pf.y * (pf.y * (pf.y * 6.0 - 15.0) + 10.0); 94 float2 n_xy = lerp(n_x.xy, n_x.zw, fade_y); 95 96 // Blend contributions along z 97 float fade_z = pf.z * pf.z * pf.z * (pf.z * (pf.z * 6.0 - 15.0) + 10.0); 98 float n_xyz = lerp(n_xy.x, n_xy.y, fade_z); 99 100 // We're done, return the final noise value. 101 return n_xyz; 102 } 103 104 float2 coordRot(in float2 tc, in float angle) 105 { 106 float rotX = ((tc.x * 2.0 - 1.0) * BUFFER_ASPECT_RATIO * cos(angle)) - ((tc.y * 2.0 - 1.0) * sin(angle)); 107 float rotY = ((tc.y * 2.0 - 1.0) * cos(angle)) + ((tc.x * 2.0 - 1.0) * BUFFER_ASPECT_RATIO * sin(angle)); 108 rotX = ((rotX / BUFFER_ASPECT_RATIO) * 0.5 + 0.5); 109 rotY = rotY * 0.5 + 0.5; 110 111 return float2(rotX, rotY); 112 } 113 114 float4 main(float4 vpos : SV_Position, float2 texCoord : TexCoord) : SV_Target 115 { 116 float3 rotOffset = float3(1.425, 3.892, 5.835); // Rotation offset values 117 float2 rotCoordsR = coordRot(texCoord, timer + rotOffset.x); 118 float3 noise = pnoise3D(float3(rotCoordsR * BUFFER_SCREEN_SIZE / grainsize, 0.0)).xxx; 119 120 if (coloramount > 0) 121 { 122 float2 rotCoordsG = coordRot(texCoord, timer + rotOffset.y); 123 float2 rotCoordsB = coordRot(texCoord, timer + rotOffset.z); 124 noise.g = lerp(noise.r, pnoise3D(float3(rotCoordsG * BUFFER_SCREEN_SIZE / grainsize, 1.0)), coloramount); 125 noise.b = lerp(noise.r, pnoise3D(float3(rotCoordsB * BUFFER_SCREEN_SIZE / grainsize, 2.0)), coloramount); 126 } 127 128 float3 col = tex2D(ReShade::BackBuffer, texCoord).rgb; 129 130 const float3 lumcoeff = float3(0.299, 0.587, 0.114); 131 float luminance = lerp(0.0, dot(col, lumcoeff), lumamount); 132 float lum = smoothstep(0.2, 0.0, luminance); 133 lum += luminance; 134 135 136 noise = lerp(noise, 0.0, pow(lum, 4.0)); 137 col = col + noise * grainamount; 138 139 return float4(col, 1.0); 140 } 141 142 technique FilmGrain2 143 { 144 pass 145 { 146 VertexShader = PostProcessVS; 147 PixelShader = main; 148 } 149 }