Back to shaders

Shader test bench

Fragment

zero-x7444ff-shader-art pattern glsl runnable fragment MIT
Source
runnable fragment

Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.

Code

precision mediump float;
uniform float u_time;
varying vec2 v_texcoord;
varying vec3 vNormal;

#define TAU 2.0 * 3.142857

// Hash function uniforms
uniform vec2 uHashFract;
uniform float uHashDot;

// Rand01 uniforms
uniform vec3 uRandFract;
uniform float uRandDot;

// Noise uniforms
uniform float uNoiseSmoothness;

// FBM uniforms
uniform float uFbmAmp;
uniform float uFbmFreq;
uniform float uFbmFreqMult;
uniform float uFbmAmpMult;

// Voronoi uniforms
uniform float uVoronoiJitter;
uniform float uVoronoiAnimBase;
uniform float uVoronoiSinSpeed1;
uniform float uVoronoiSinSpeed2;
uniform float uVoronoiSinAmp1;
uniform float uVoronoiSinSpeed3;
uniform float uVoronoiSinSpeed4;
uniform float uVoronoiSinAmp2;
uniform float uVoronoiFbmScale1;
uniform float uVoronoiFbmSpeed1;
uniform float uVoronoiFbmScale2;
uniform float uVoronoiFbmSpeed2;
uniform float uVoronoiFbmDispl;

// Swirl uniforms
uniform float uSwirlSmoothStart;
uniform float uSwirlSmoothEnd;
uniform float uSwirlSpeedMult;
uniform float uSwirlNoiseAmp2;
uniform float uSwirlNoiseScale2;
uniform float uSwirlNoiseScale3;
uniform float uSwirlNoiseSpeed1;
uniform float uSwirlNoiseSpeed2;
uniform float uSwirlNoiseSpeed3;
uniform float uSwirlNoiseSpeed4;
uniform float uSwirlRadialFlow;

// Cell counts
uniform float uCellCount2;
uniform float uCellCount3;

// Layer speeds
uniform float uLayer2Speed;
uniform float uLayer2Twist;
uniform float uLayer2NoiseScale;
uniform float uLayer2NoiseAmp;
uniform float uLayer2TimeSpeed;
uniform float uLayer2Seed;

uniform float uLayer3Speed;
uniform float uLayer3Twist;
uniform float uLayer3NoiseScale;
uniform float uLayer3NoiseAmp;
uniform float uLayer3TimeSpeed;
uniform float uLayer3Seed;

// Edge uniforms
uniform float uEdgeNoiseScale;
uniform float uEdgeNoiseSpeed;
uniform float uEdgeWidthMin;
uniform float uEdgeWidthMax;
uniform float uBaseWidth;

uniform float uSecondaryEdgeWidth;
uniform float uSecondaryEdgePow;
uniform float uSecondaryEdgeStrength;

uniform float uTertiaryEdgeWidth;
uniform float uTertiaryEdgePow;
uniform float uTertiaryEdgeStrength;

// Glow uniforms
uniform float uGlow2Start;
uniform float uGlow2End;
uniform float uGlow2Pow;
uniform float uGlow2Strength;

uniform float uGlow3Start;
uniform float uGlow3End;
uniform float uGlow3Pow;
uniform float uGlow3Strength;

// Junction uniforms
uniform float uJunctionWidth;
uniform float uJunctionPow;
uniform float uJunctionStrength;

// Color noise uniforms
uniform float uColorNoise2Scale;
uniform float uColorNoise2Speed;
uniform float uColorNoise3Scale;
uniform float uColorNoise3Speed;
uniform float uEdgeBrightnessMin;
uniform float uEdgeBrightnessMax;

// Cell light uniforms
uniform float uCellLight2Mult;
uniform float uCellLight2Strength;
uniform float uCellLight3Mult;
uniform float uCellLight3Strength;

// Background uniforms
uniform float uBgNoiseScale;
uniform float uBgNoiseSpeed;
uniform float uBgDetailScale;
uniform float uBgDetailSpeed;
uniform float uBgValueMin;
uniform float uBgValueMax;
uniform float uBgNoiseStrength;
uniform float uBgDetailStrength;
uniform vec3 uBgColor;

// Caustic weights
uniform float uSecondaryWeight;
uniform float uTertiaryWeight;
uniform float uGlow2Weight;
uniform float uGlow3Weight;
uniform float uJunctionWeight;

// Color shift
uniform vec3 uColorShift;

// Color grading
uniform float uColorMultiplier;
uniform float uColorGamma;

// Fresnel
uniform float uFresnelPow;
uniform float uFresnelStrength;

float hash(vec2 p) {
    p = fract(p * uHashFract);
    p += dot(p, p + uHashDot);
    return fract(p.x * p.y);
}

vec2 rand01(vec2 p) {
    vec3 a = fract(p.xyx * uRandFract);
    a += dot(a, a + uRandDot);
    return fract(vec2(a.x * a.y, a.y * a.z));
}

float noise(vec2 p) {
    vec2 i = floor(p);
    vec2 f = fract(p);
    f = f * f * (uNoiseSmoothness - 2.0 * f);
    float a = hash(i);
    float b = hash(i + vec2(1.0, 0.0));
    float c = hash(i + vec2(0.0, 1.0));
    float d = hash(i + vec2(1.0, 1.0));
    return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

float fbm(vec2 p) {
    float value = 0.0;
    float amp = uFbmAmp;
    float freq = uFbmFreq;
    for (int i = 0; i < 1; i++) {
        value += amp * noise(p * freq);
        freq *= uFbmFreqMult;
        amp *= uFbmAmpMult;
    }
    return value;
}

vec3 voronoiF1F2F3(vec2 uv, float time, float seed, float cells) {
    float INF = 1e6;
    float min1 = INF;
    float min2 = INF;
    float min3 = INF;

    vec2 cellUv = fract(uv * cells) - 0.5;
    vec2 cellCoord = floor(uv * cells);

    for (float xo = -1.0; xo <= 1.0; xo += 1.0) {
        for (float yo = -1.0; yo <= 1.0; yo += 1.0) {
            vec2 off = vec2(xo, yo);
            vec2 nc = cellCoord + off;
            vec2 r = rand01(nc + seed);
            vec2 jitter = (r - 0.5) * uVoronoiJitter;

            vec2 sinPart = vec2(
                    sin(time * uVoronoiSinSpeed1 + r.x * TAU) + uVoronoiSinAmp1 * cos(time * uVoronoiSinSpeed2 + r.x * TAU * uVoronoiSinAmp1),
                    cos(time * uVoronoiSinSpeed3 + r.y * TAU) + uVoronoiSinAmp2 * sin(time * uVoronoiSinSpeed4 + r.y * TAU * uVoronoiSinSpeed2)
                ) * uVoronoiAnimBase;

            vec2 fb = vec2(
                    fbm(nc * uVoronoiFbmScale1 + vec2(time * uVoronoiFbmSpeed1)),
                    fbm(nc * uVoronoiFbmScale2 - vec2(time * uVoronoiFbmSpeed2))
                );
            vec2 fbDispl = (fb - 0.5) * uVoronoiFbmDispl;

            vec2 point = off + jitter + sinPart + fbDispl;
            float d = length(cellUv - point);

            if (d < min1) {
                min3 = min2;
                min2 = min1;
                min1 = d;
            } else if (d < min2) {
                min3 = min2;
                min2 = d;
            } else if (d < min3) {
                min3 = d;
            }
        }
    }

    return vec3(min1, min2, min3);
}

vec2 organicSwirlUV(vec2 uv, float speed, float twist, float noiseScale, float noiseAmp) {
    vec2 c = uv - 0.5;
    float r = length(c);
    float a = atan(c.y, c.x);

    float n = fbm(uv * noiseScale + vec2(u_time * uSwirlNoiseSpeed1));
    float n2 = fbm(uv * (noiseScale * uSwirlNoiseScale2) - vec2(u_time * uSwirlNoiseSpeed2));
    float n3 = fbm(uv * (noiseScale * uSwirlNoiseScale3) + vec2(u_time * uSwirlNoiseSpeed3, -u_time * uSwirlNoiseSpeed4));

    float fall = smoothstep(uSwirlSmoothStart, uSwirlSmoothEnd, r);
    a += speed * uSwirlSpeedMult * fall + twist * r * r + (n - 0.5) * noiseAmp + (n2 - 0.5) * noiseAmp * uSwirlNoiseAmp2;

    float radialFlow = (n3 - 0.5) * uSwirlRadialFlow * fall;
    r = max(0.0, r + radialFlow);

    vec2 rotated = vec2(cos(a), sin(a)) * r;
    return rotated + 0.5;
}

void main() {
    vec2 baseUV = v_texcoord;

    // Layer 2 - Secondary detail
    vec2 uv2 = organicSwirlUV(baseUV, uLayer2Speed, uLayer2Twist, uLayer2NoiseScale, uLayer2NoiseAmp);
    vec3 F2 = voronoiF1F2F3(uv2, u_time * uLayer2TimeSpeed, uLayer2Seed, uCellCount2);

    // Layer 3 - Large flowing shapes
    vec2 uv3 = organicSwirlUV(baseUV, uLayer3Speed, uLayer3Twist, uLayer3NoiseScale, uLayer3NoiseAmp);
    vec3 F3 = voronoiF1F2F3(uv3, u_time * uLayer3TimeSpeed, uLayer3Seed, uCellCount3);

    // Edge calculations
    float e12_2 = F2.y - F2.x;
    float e12_3 = F3.y - F3.x;

    // Dynamic edge width
    float edgeNoise = fbm(uv2 * uCellCount2 * uEdgeNoiseScale + vec2(u_time * uEdgeNoiseSpeed));
    float widthMod = mix(uEdgeWidthMin, uEdgeWidthMax, edgeNoise);
    float baseWidth = uBaseWidth * widthMod;

    // Edges
    float secondaryEdge = pow(1.0 - smoothstep(0.0, baseWidth * uSecondaryEdgeWidth, e12_2), uSecondaryEdgePow) * uSecondaryEdgeStrength;
    float tertiaryEdge = pow(1.0 - smoothstep(0.0, baseWidth * uTertiaryEdgeWidth, e12_3), uTertiaryEdgePow) * uTertiaryEdgeStrength;

    // Glows
    float glow2 = pow(1.0 - smoothstep(baseWidth * uGlow2Start, baseWidth * uGlow2End, e12_2), uGlow2Pow) * uGlow2Strength;
    float glow3 = pow(1.0 - smoothstep(baseWidth * uGlow3Start, baseWidth * uGlow3End, e12_3), uGlow3Pow) * uGlow3Strength;

    // Junction hotspots
    float junction2 = pow(1.0 - smoothstep(0.0, baseWidth * uJunctionWidth, e12_2 + (F2.z - F2.y)), uJunctionPow) * uJunctionStrength;

    // Animated color variation
    float colorNoise2 = noise(uv2 * uCellCount2 * uColorNoise2Scale + vec2(u_time * uColorNoise2Speed));
    float colorNoise3 = noise(uv3 * uCellCount3 * uColorNoise3Scale - vec2(u_time * uColorNoise3Speed));
    float edgeBrightness = mix(uEdgeBrightnessMin, uEdgeBrightnessMax, colorNoise2);

    // Rich background
    float cellLight2 = exp(-F2.x * F2.x * uCellLight2Mult) * uCellLight2Strength;
    float cellLight3 = exp(-F3.x * F3.x * uCellLight3Mult) * uCellLight3Strength;

    float bgNoise = fbm(uv2 * uCellCount2 * uBgNoiseScale + vec2(u_time * uBgNoiseSpeed));
    float bgDetail = fbm(uv3 * uCellCount3 * uBgDetailScale - vec2(u_time * uBgDetailSpeed)) * 0.5;

    float bgValue = mix(uBgValueMin, uBgValueMax, cellLight2 + cellLight3);
    bgValue += bgNoise * uBgNoiseStrength + bgDetail * uBgDetailStrength;

    vec3 baseColor = vec3(bgValue) * uBgColor;

    // Build up caustic layers
    vec3 causticColor = vec3(edgeBrightness);
    vec3 color = baseColor;

    // Add edge contributions
    color += causticColor * (secondaryEdge * uSecondaryWeight + tertiaryEdge * uTertiaryWeight);

    // Add glow layers
    color += causticColor * (glow2 * uGlow2Weight + glow3 * uGlow3Weight);

    // Add junction hotspots
    color += vec3(0.9, 0.96, 1.0) * (junction2 * uJunctionWeight);

    // Add color variation
    color *= uColorShift;

    // Color grading
    color = clamp(color, 0.0, 1.0) * uColorMultiplier;
    color = pow(color, vec3(uColorGamma));

    gl_FragColor = vec4(color, 1.0);
}