float noise_clouds_base_simple(vec3 p) {
    vec2 Wind = frameTimeCounter * vec2(5, 0) + cloudStartOffset;
    float Base = textureNice(noisetex, (p.xz + Wind) * 0.00001).g;
    Base *= Base;
    Base *= Base;
    Base *= Base;
    return Base;
}

float noise_clouds_base(vec3 p) {
    vec2 Wind = frameTimeCounter * vec2(5, 0) + cloudStartOffset;
    float HeightNoise = texture(noisetex, p.xz * 0.00005).r;
    float Base = textureNice(noisetex, (p.xz + Wind) * 0.00001).g;
    Base *= Base;
    Base *= Base;
    Base *= Base;

    float HeightR = abs(rescale(CLOUD_LOWER_PLANE + 30, CLOUD_UPPER_PLANE - HeightNoise * 70, p.y));
    HeightR = smoothstep(0, 1, HeightR);

    float HeightFalloff = 1 - HeightR;
    return HeightFalloff * Base;
}

float noise_clouds(vec3 p) {
    float Base = noise_clouds_base(p);
    if (Base < 0.001) return 0;

    vec2 Wind = frameTimeCounter * vec2(2, 3.75);

    vec2 D = vec2(textureNice(noisetex, (p.xy + Wind) * 0.000033).g, textureNice(noisetex, (p.xz + Wind) * 0.000025).g);
    vec3 Displacement = vec3(D.x, D.y, 1 - (D.x * D.x + D.y * D.y)) * 0.07;

    float Detail = 1 - texture(worleyNoiseTexture, p / vec3(256, 32, 256) * 1.25 + Displacement).r * 0.5;
    Detail *= 1 - texture(worleyNoiseTexture, p / vec3(256, 32, 256) * 0.75 + Displacement + 0.33).r * 0.75;
    Detail *= 1 - texture(worleyNoiseTexture, p / vec3(256, 32, 256) * 0.25 + Displacement + 0.5).r;

    return clamp(Base - Detail * 0.25, 0, 1);
}

float noise_smoke(vec3 p) {
    p.y *= 0.33;
    vec3 Wind = frameTimeCounter * vec3(0, 5.75, 0);

    vec2 D = vec2(textureNice(noisetex, (p.xy) * 0.00005).g, textureNice(noisetex, (p.xz) * 0.000066).g);
    vec3 Displacement = vec3(D.x, D.y, 1 - (D.x * D.x + D.y * D.y)) * 0.17;

    float Detail = texture(worleyNoiseTexture, (p - Wind * 0.3) / vec3(256, 32, 256) * 7.25 + Displacement).r;
    Detail *= texture(worleyNoiseTexture, (p - Wind) / vec3(256, 32, 256) * 0.75 + 0.5 + Displacement).r;

    return Detail * 1.5;
}

vec3 hash3(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.5453);
}
vec2 hash2(vec2 p) {
    p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
    return fract(sin(p) * 43758.5453);
}
float hash3D(vec3 p) {
    vec3 h = hash3(p);
    return fract(h.x + h.y * 1e-1 + h.z * 1e-2);
}

// https://www.shadertoy.com/view/4ssfWM
float bayer8(vec2 a) {
    uvec2 b = uvec2(a);
    uint c = (b.x ^ b.y) << 1u;
    return float(
        ((c & 8u | b.y & 4u) >> 2u) |
            ((c & 4u | b.y & 2u) << 1u) |
            ((c & 2u | b.y & 1u) << 4u) //15 ops
    ) / 8. / 8.;
}

float ign(vec2 Pos) {
    float FrameMod = frameCounter % 64;
    Pos += 5.588238f * FrameMod;

    return fract(52.9829189 * fract(0.06711056 * Pos.x + 0.00583715 * Pos.y));
}

vec4 blue_noise(vec2 Pos, const bool Animate) {
    vec4 Noise = texelFetch(blueNoiseTexture, ivec2(Pos) & 255, 0);

    if (Animate)
        Noise = fract(Noise + 1.61803398875 * frameCounter); // Animate

    return Noise;
}

float dither(vec2 Pos) {
    // Interleaved gradient noise
    return ign(Pos);
}
