diff --git a/docs/changelog.md b/docs/changelog.md index d67f555..5f86d1c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -16,6 +16,7 @@ * The **layout of the settings dialog** has been reworked to make it easier to expand it in the future. * The lower limit of the **animation time of the TV Effect** has been reduced. * The T-Rex-Attack effect is not shown in fully transparent regions of a window any more. +* The shaders now use a different noise implementation which produces better 3D noise. #### Fixes diff --git a/src/FireEffect.js b/src/FireEffect.js index a6542aa..3493692 100644 --- a/src/FireEffect.js +++ b/src/FireEffect.js @@ -323,9 +323,9 @@ if (utils.isInShellProcess()) { uv.y += uTime * FIRE_SPEED; #if ${settings.get_boolean('flame-3d-noise') ? 1 : 0} - float noise = noise3D(vec3(uv*7.5, uTime*FIRE_SPEED*3.0), 5); + float noise = simplex3DFractal(vec3(uv*4.0, uTime*FIRE_SPEED*1.5)); #else - float noise = noise2D(uv * 7.5, 5); + float noise = simplex2DFractal(uv * 4.0); #endif // Modulate noise by effect mask. diff --git a/src/MatrixEffect.js b/src/MatrixEffect.js index f4156e0..ae47a0f 100644 --- a/src/MatrixEffect.js +++ b/src/MatrixEffect.js @@ -165,7 +165,7 @@ if (utils.isInShellProcess()) { vec2 block = pixelCoords/LETTER_SIZE - uv; // Choose random letter. - uv += floor(hash2D(floor(hash2D(block)*vec2(12.9898,78.233) + LETTER_FLICKER_SPEED*uTime + 42.254))*LETTER_TILES); + uv += floor(hash22(floor(hash22(block)*vec2(12.9898,78.233) + LETTER_FLICKER_SPEED*uTime + 42.254))*LETTER_TILES); return texture2D(uFontTexture, uv/LETTER_TILES).r; } diff --git a/src/TRexEffect.js b/src/TRexEffect.js index 3773186..7268e80 100644 --- a/src/TRexEffect.js +++ b/src/TRexEffect.js @@ -159,7 +159,7 @@ if (utils.isInShellProcess()) { vec2 getClawUV(vec2 texCoords, float gridScale, vec2 seed) { // Shift coordinates by a random offset and make sure the have a 1:1 aspect ratio. - vec2 coords = texCoords + hash2D(seed); + vec2 coords = texCoords + hash22(seed); coords *= uSizeX < uSizeY ? vec2(1.0, 1.0 * uSizeY / uSizeX) : vec2(1.0 * uSizeX / uSizeY, 1.0); // Apply global scale. @@ -172,10 +172,10 @@ if (utils.isInShellProcess()) { vec2 cellID = coords-cellUV + vec2(362.456); // Add random rotation, scale and offset to each grid cell. - float scale = mix(0.8, 1.0, hash(cellID*seed*134.451)); - float offsetX = mix(0.0, 1.0 - scale, hash(cellID*seed*54.4129)); - float offsetY = mix(0.0, 1.0 - scale, hash(cellID*seed*25.3089)); - float rotation = mix(0.0, 2.0 * 3.141, hash(cellID*seed*2.99837)); + float scale = mix(0.8, 1.0, hash12(cellID*seed*134.451)); + float offsetX = mix(0.0, 1.0 - scale, hash12(cellID*seed*54.4129)); + float offsetY = mix(0.0, 1.0 - scale, hash12(cellID*seed*25.3089)); + float rotation = mix(0.0, 2.0 * 3.141, hash12(cellID*seed*2.99837)); cellUV -= vec2(offsetX, offsetY); cellUV /= scale; diff --git a/src/shaderSnippets.js b/src/shaderSnippets.js index 9b1a0c3..5fe74ea 100644 --- a/src/shaderSnippets.js +++ b/src/shaderSnippets.js @@ -33,93 +33,216 @@ function standardUniforms() { `; } -// These noise algorithms are based on implementations by Inigo Quilez which are -// available under the MIT License. -// https://www.shadertoy.com/view/lsf3WH -// https://www.shadertoy.com/view/4sfGzS -// https://www.shadertoy.com/view/Xsl3Dl -// https://www.shadertoy.com/view/Msf3WH +// These noise algorithms are based on implementations by various authors from +// shadertoy.com, which are all available under the MIT License. See the respective links +// in the comments below. function noise() { return ` - vec2 hash2D(vec2 p) { - p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3))); - return fract(sin(p) * 43758.5453123); + //////////////////////////////////////////////////////////////////////////////////////// + // Hash without Sine // + // MIT License, https://www.shadertoy.com/view/4djSRW // + // Copyright (c) 2014 David Hoskins. // + //////////////////////////////////////////////////////////////////////////////////////// + + // 1 out, 1 in... + float hash11(float p) { + p = fract(p * .1031); + p *= p + 33.33; + p *= p + p; + return fract(p); } - vec3 hash3D(vec3 p) { - p = vec3(dot(p, vec3(127.1, 311.7, 74.7)), - dot(p, vec3(269.5, 183.3, 246.1)), - dot(p, vec3(113.5, 271.9, 124.6))); - - return fract(sin(p) * 43758.5453123); + // 1 out, 2 in... + float hash12(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * .1031); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.x + p3.y) * p3.z); } - float hash(vec2 p) { - p = floor(p); - p = 50.0 * fract(p * 0.3183099 + vec2(0.71, 0.113)); - return fract(p.x * p.y * (p.x + p.y)); + // 1 out, 3 in... + float hash13(vec3 p3) { + p3 = fract(p3 * .1031); + p3 += dot(p3, p3.zyx + 31.32); + return fract((p3.x + p3.y) * p3.z); } - float hash(vec3 p) { - p = fract(p * 0.3183099 + 0.1) * 17.0; - return fract(p.x * p.y * p.z * (p.x + p.y + p.z)); + // 2 out, 1 in... + vec2 hash21(float p) { + vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973)); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.xx+p3.yz)*p3.zy); } - float noise2D(vec2 p) { - vec2 i = floor(p); - vec2 f = fract(p); - - vec2 u = f * f * (3.0 - 2.0 * f); - - return mix(mix(hash(i + vec2(0.0, 0.0)), - hash(i + vec2(1.0, 0.0)), u.x), - mix(hash(i + vec2(0.0, 1.0)), - hash(i + vec2(1.0, 1.0)), u.x), u.y); + // 2 out, 2 in... + vec2 hash22(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); + p3 += dot(p3, p3.yzx+33.33); + return fract((p3.xx+p3.yz)*p3.zy); } - float noise3D(vec3 p) { - vec3 i = floor(p); - vec3 f = fract(p); - - f = f * f * (3.0 - 2.0 * f); - - return mix(mix(mix(hash(i+vec3(0, 0 ,0)), - hash(i+vec3(1, 0 ,0)), f.x), - mix(hash(i+vec3(0, 1 ,0)), - hash(i+vec3(1, 1 ,0)), f.x), f.y), - mix(mix(hash(i+vec3(0, 0 ,1)), - hash(i+vec3(1, 0 ,1)), f.x), - mix(hash(i+vec3(0, 1 ,1)), - hash(i+vec3(1, 1 ,1)), f.x),f.y), f.z); + // 2 out, 3 in... + vec2 hash23(vec3 p3) { + p3 = fract(p3 * vec3(.1031, .1030, .0973)); + p3 += dot(p3, p3.yzx+33.33); + return fract((p3.xx+p3.yz)*p3.zy); } - float noise2D(vec2 p, int octaves) { - mat2 m = mat2( 1.6, 1.2, - -1.2, 1.6); + // 3 out, 1 in... + vec3 hash31(float p) { + vec3 p3 = fract(vec3(p) * vec3(.1031, .1030, .0973)); + p3 += dot(p3, p3.yzx+33.33); + return fract((p3.xxy+p3.yzz)*p3.zyx); + } + + // 3 out, 2 in... + vec3 hash32(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); + p3 += dot(p3, p3.yxz+33.33); + return fract((p3.xxy+p3.yzz)*p3.zyx); + } + + // 3 out, 3 in... + vec3 hash33(vec3 p3) { + p3 = fract(p3 * vec3(.1031, .1030, .0973)); + p3 += dot(p3, p3.yxz+33.33); + return fract((p3.xxy + p3.yxx)*p3.zyx); + } + + // 4 out, 1 in... + vec4 hash41(float p) { + vec4 p4 = fract(vec4(p) * vec4(.1031, .1030, .0973, .1099)); + p4 += dot(p4, p4.wzxy+33.33); + return fract((p4.xxyz+p4.yzzw)*p4.zywx); + } + + // 4 out, 2 in... + vec4 hash42(vec2 p) { + vec4 p4 = fract(vec4(p.xyxy) * vec4(.1031, .1030, .0973, .1099)); + p4 += dot(p4, p4.wzxy+33.33); + return fract((p4.xxyz+p4.yzzw)*p4.zywx); + } + + // 4 out, 3 in... + vec4 hash43(vec3 p) { + vec4 p4 = fract(vec4(p.xyzx) * vec4(.1031, .1030, .0973, .1099)); + p4 += dot(p4, p4.wzxy+33.33); + return fract((p4.xxyz+p4.yzzw)*p4.zywx); + } + + // 4 out, 4 in... + vec4 hash44(vec4 p4) { + p4 = fract(p4 * vec4(.1031, .1030, .0973, .1099)); + p4 += dot(p4, p4.wzxy+33.33); + return fract((p4.xxyz+p4.yzzw)*p4.zywx); + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // 2D Simplex Noise // + // MIT License, https://www.shadertoy.com/view/Msf3WH // + // Copyright © 2013 Inigo Quilez // + //////////////////////////////////////////////////////////////////////////////////////// + + float simplex2D(vec2 p) { + const float K1 = 0.366025404; // (sqrt(3)-1)/2; + const float K2 = 0.211324865; // (3-sqrt(3))/6; + + vec2 i = floor( p + (p.x+p.y)*K1 ); + vec2 a = p - i + (i.x+i.y)*K2; + float m = step(a.y,a.x); + vec2 o = vec2(m,1.0-m); + vec2 b = a - o + K2; + vec2 c = a - 1.0 + 2.0*K2; + vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 ); + vec3 n = h*h*h*h*vec3( dot(a,-1.0 + 2.0 * hash22(i+0.0)), + dot(b,-1.0 + 2.0 * hash22(i+o)), + dot(c,-1.0 + 2.0 * hash22(i+1.0))); + return 0.5 + 0.5 * dot( n, vec3(70.0) ); + } + + float simplex2DFractal(vec2 p) { + mat2 m = mat2( 1.6, 1.2, -1.2, 1.6 ); + float f = 0.5000*simplex2D( p ); p = m*p; + f += 0.2500*simplex2D( p ); p = m*p; + f += 0.1250*simplex2D( p ); p = m*p; + f += 0.0625*simplex2D( p ); p = m*p; - float f = 0; - - for (int i=1; i<=octaves; ++i) { - f += noise2D(p) / pow(2, i); - p = m * p; - } - return f; } - float noise3D(vec3 p, int octaves) { - const mat3 m = mat3( 0.00, 0.80, 0.60, - -0.80, 0.36, -0.48, - -0.60, -0.48, 0.64); - float f = 0; - - for (int i=1; i<=octaves; ++i) { - f += noise3D(p) / pow(2, i); - p = m * p * 2.01; - } - - return f; + + //////////////////////////////////////////////////////////////////////////////////////// + // 3D Simplex Noise // + // MIT License, https://www.shadertoy.com/view/XsX3zB // + // Copyright © 2013 Nikita Miropolskiy // + //////////////////////////////////////////////////////////////////////////////////////// + + float simplex3D(vec3 p) { + + // skew constants for 3D simplex functions + const float F3 = 0.3333333; + const float G3 = 0.1666667; + + // 1. find current tetrahedron T and it's four vertices + // s, s+i1, s+i2, s+1.0 - absolute skewed (integer) coordinates of T vertices + // x, x1, x2, x3 - unskewed coordinates of p relative to each of T vertice + + // calculate s and x + vec3 s = floor(p + dot(p, vec3(F3))); + vec3 x = p - s + dot(s, vec3(G3)); + + // calculate i1 and i2 + vec3 e = step(vec3(0.0), x - x.yzx); + vec3 i1 = e*(1.0 - e.zxy); + vec3 i2 = 1.0 - e.zxy*(1.0 - e); + + // x1, x2, x3 + vec3 x1 = x - i1 + G3; + vec3 x2 = x - i2 + 2.0*G3; + vec3 x3 = x - 1.0 + 3.0*G3; + + // 2. find four surflets and store them in d + vec4 w, d; + + // calculate surflet weights + w.x = dot(x, x); + w.y = dot(x1, x1); + w.z = dot(x2, x2); + w.w = dot(x3, x3); + + // w fades from 0.6 at the center of the surflet to 0.0 at the margin + w = max(0.6 - w, 0.0); + + // calculate surflet components + d.x = dot(-0.5 + hash33(s), x); + d.y = dot(-0.5 + hash33(s + i1), x1); + d.z = dot(-0.5 + hash33(s + i2), x2); + d.w = dot(-0.5 + hash33(s + 1.0), x3); + + // multiply d by w^4 + w *= w; + w *= w; + d *= w; + + // 3. return the sum of the four surflets + return dot(d, vec4(52.0)) * 0.5 + 0.5; } + + + // directional artifacts can be reduced by rotating each octave + float simplex3DFractal(vec3 m) { + + // const matrices for 3D rotation + const mat3 rot1 = mat3(-0.37, 0.36, 0.85,-0.14,-0.93, 0.34,0.92, 0.01,0.4); + const mat3 rot2 = mat3(-0.55,-0.39, 0.74, 0.33,-0.91,-0.24,0.77, 0.12,0.63); + const mat3 rot3 = mat3(-0.71, 0.52,-0.47,-0.08,-0.72,-0.68,-0.7,-0.45,0.56); + + return 0.5333333*simplex3D(m*rot1) + +0.2666667*simplex3D(2.0*m*rot2) + +0.1333333*simplex3D(4.0*m*rot3) + +0.0666667*simplex3D(8.0*m); + } + `; }