diff --git a/film/shaders/film-grain.slang b/film/shaders/film-grain.slang index 96ecc080..1d911406 100644 --- a/film/shaders/film-grain.slang +++ b/film/shaders/film-grain.slang @@ -41,7 +41,7 @@ layout(push_constant) uniform Push #pragma parameter GRAIN_AMOUNT "Grain amount" 0.02 0.01 0.2 0.01 #pragma parameter COLOR_AMOUNT "Color amount" 0.6 0.0 1.0 0.1 #pragma parameter GRAIN_SIZE "Grain particle size" 1.6 1.5 2.5 0.1 -#pragma parameter LUM_AMOUNT "Luminance amount" 1.0 0.0 2.0 0.1 +#pragma parameter LUM_AMOUNT "Luminance amount" 1.0 0.0 1.0 0.1 #pragma parameter COLORED "Use colored noise" 0.0 0.0 1.0 1.0 #define GRAIN_AMOUNT params.GRAIN_AMOUNT @@ -50,13 +50,15 @@ layout(push_constant) uniform Push #define LUM_AMOUNT params.LUM_AMOUNT #define COLORED params.COLORED -float timer = mod(params.FrameCount, 4268.); // Global timer +#define NOISE(x) (sin(dot(x+vec2(timer,timer),vec2(12.9898,78.233)))*43758.5453) -const float permTexUnit = 1.0/256.0; // Perm texture texel-size -const float permTexUnitHalf = 0.5/256.0; // Half perm texture texel-size -const vec3 rotOffset = vec3(1.425,3.892,5.835); // Rotation offset values -const vec3 lumcoeff = vec3(0.299,0.587,0.114); +float timer = mod(params.FrameCount, 4268.); // Global timer +const float permTexUnit = 1.0 / 256.0; // Perm texture texel-size +const float permTexUnitHalf = 0.5 / 256.0; // Half perm texture texel-size +const vec3 rotOffset = vec3(1.425, 3.892, 5.835); // Rotation offset values +const vec3 lumcoeff = vec3(0.299, 0.587, 0.114); +const vec4 nRGBA = vec4(1.0, 1.2154, 1.3453, 1.3647); layout(std140, set = 0, binding = 0) uniform UBO { @@ -74,85 +76,62 @@ void main() vTexCoord = TexCoord; } - #pragma stage fragment layout(location = 0) in vec2 vTexCoord; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; //a random texture generator, but you can also use a pre-computed perturbation texture -vec4 rnm(vec2 tc) -{ - float noise = sin(dot(tc + vec2(timer,timer),vec2(12.9898,78.233))) * 43758.5453; +float rnmA(vec2 tc) {return fract(NOISE(tc) * nRGBA.a ) * 2.0 - 1.0;} +vec2 rnmRG(vec2 tc) {return fract(NOISE(tc) * nRGBA.rg ) * 2.0 - vec2(1.0);} - 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); +vec2 fade(vec2 t) {return t * t * t * (t * (t * 6.0 - vec2(15.0)) + vec2(10.0));} + +float noise2D_contr(vec2 pi, vec2 pf, vec2 pxy, vec2 ptu) +{ + // Noise contributions from (pxy) + float perm = rnmA(pi.xy + ptu); + vec2 grad = rnmRG(vec2(perm, 0.0)) * 4.0 - vec2(1.0); + + return dot(grad, pf - pxy); } -float fade(float t) { - return t*t*t*(t*(t*6.0-15.0)+10.0); -} - -float pnoise3D(vec3 p) +float pnoise2D(vec2 p) { - vec3 pi = permTexUnit*floor(p)+permTexUnitHalf; // Integer part, scaled so +1 moves permTexUnit texel - // and offset 1/2 texel to sample texel centers - vec3 pf = fract(p); // Fractional part for interpolation + // Integer part, scaled so +1 moves permTexUnit texel + vec2 pi = permTexUnit * floor(p) + permTexUnitHalf; - // 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 - 1.0; - float n000 = dot(grad000, pf); - vec3 grad001 = rnm(vec2(perm00, pi.z + permTexUnit)).rgb * 4.0 - 1.0; - float n001 = dot(grad001, pf - vec3(0.0, 0.0, 1.0)); + // and offset 1/2 texel to sample texel centers. Fractional part for interpolation + vec2 pf = fract(p); - // Noise contributions from (x=0, y=1), z=0 and z=1 - float perm01 = rnm(pi.xy + vec2(0.0, permTexUnit)).a ; - vec3 grad010 = rnm(vec2(perm01, pi.z)).rgb * 4.0 - 1.0; - float n010 = dot(grad010, pf - vec3(0.0, 1.0, 0.0)); - vec3 grad011 = rnm(vec2(perm01, pi.z + permTexUnit)).rgb * 4.0 - 1.0; - float n011 = dot(grad011, pf - vec3(0.0, 1.0, 1.0)); + // Noise contributions from (x=0, y=0), (x=0, y=1), (x=1, y=0), (x=1, y=1). + float n00 = noise2D_contr(pi, pf, vec2(0.0, 0.0), vec2( 0.0, 0.0)); + float n01 = noise2D_contr(pi, pf, vec2(0.0, 1.0), vec2( 0.0, permTexUnit)); + float n10 = noise2D_contr(pi, pf, vec2(1.0, 0.0), vec2(permTexUnit, 0.0)); + float n11 = noise2D_contr(pi, pf, vec2(1.0, 1.0), vec2(permTexUnit, permTexUnit)); - // Noise contributions from (x=1, y=0), z=0 and z=1 - float perm10 = rnm(pi.xy + vec2(permTexUnit, 0.0)).a ; - vec3 grad100 = rnm(vec2(perm10, pi.z)).rgb * 4.0 - 1.0; - float n100 = dot(grad100, pf - vec3(1.0, 0.0, 0.0)); - vec3 grad101 = rnm(vec2(perm10, pi.z + permTexUnit)).rgb * 4.0 - 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(permTexUnit, permTexUnit)).a ; - vec3 grad110 = rnm(vec2(perm11, pi.z)).rgb * 4.0 - 1.0; - float n110 = dot(grad110, pf - vec3(1.0, 1.0, 0.0)); - vec3 grad111 = rnm(vec2(perm11, pi.z + permTexUnit)).rgb * 4.0 - 1.0; - float n111 = dot(grad111, pf - vec3(1.0, 1.0, 1.0)); + vec2 fd = fade(pf); // Blend contributions along x - vec4 n_x = mix(vec4(n000, n001, n010, n011), vec4(n100, n101, n110, n111), fade(pf.x)); + vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fd.x); // Blend contributions along y - vec2 n_xy = mix(n_x.xy, n_x.zw, fade(pf.y)); + float n_xy = mix(n_x.x, n_x.y, fd.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; + // We're done, return the final noise value + return n_xy; } //2d coordinate orientation thing -vec2 coordRot(in vec2 tc, in float angle, float width, float height) +vec2 coordRot(vec2 tc, float angle, float width, float height) { - float aspect = width/height; - 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); + float aspect = width / height; + float rotX = ((tc.x * 2.0 - 1.0) * aspect * cos(angle)) - ((tc.y * 2.0 - 1.0) * sin(angle)); + float rotY = ((tc.x * 2.0 - 1.0) * aspect * sin(angle)) + ((tc.y * 2.0 - 1.0) * cos(angle)); + rotX = ((rotX / aspect) * 0.5 + 0.5); + rotY = rotY * 0.5 + 0.5; + + return vec2(rotX, rotY); } void main() @@ -166,26 +145,26 @@ void main() vec2 texCoord = vTexCoord; vec2 rotCoordsR = coordRot(texCoord, timer + rotOffset.x, width, height); - vec3 noise = vec3(pnoise3D(vec3(rotCoordsR*vec2(width/GRAIN_SIZE,height/GRAIN_SIZE),0.0))); + vec3 noise = vec3(pnoise2D(vec2(rotCoordsR * vec2(width / GRAIN_SIZE, height / GRAIN_SIZE)))); if (COLORED == 1.0) { vec2 rotCoordsG = coordRot(texCoord, timer + rotOffset.y, width, height); vec2 rotCoordsB = coordRot(texCoord, timer + rotOffset.z, width, height); - noise.g = mix(noise.r,pnoise3D(vec3(rotCoordsG*vec2(width/GRAIN_SIZE,height/GRAIN_SIZE),1.0)),COLOR_AMOUNT); - noise.b = mix(noise.r,pnoise3D(vec3(rotCoordsB*vec2(width/GRAIN_SIZE,height/GRAIN_SIZE),2.0)),COLOR_AMOUNT); + noise.g = mix(noise.r, pnoise2D(vec2(rotCoordsG * vec2(width / GRAIN_SIZE, height / GRAIN_SIZE))), COLOR_AMOUNT); + noise.b = mix(noise.r, pnoise2D(vec2(rotCoordsB * vec2(width / GRAIN_SIZE, height / GRAIN_SIZE))), COLOR_AMOUNT); } vec3 col = texture(Source, texCoord).rgb; //noisiness response curve based on scene luminance - float luminance = mix(0.0,dot(col, lumcoeff),LUM_AMOUNT); - float lum = smoothstep(0.2,0.0,luminance); + 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; + noise = mix(noise, vec3(0.0), pow(lum, 4.0)); + col = col + noise * GRAIN_AMOUNT; - FragColor = vec4(col,1.0); + FragColor = vec4(col, 1.0); }