← rauzy-gasket

Rauzy Gasket

Iterates the barycentric algorithm to give an image of the Rauzy Gasket.

Source

image.glsl

// Rauzy gasket, drawn by iterating the fully subtractive algorithm on
// barycentric coordinates. A map fx/fy/fz applies when its coordinate
// dominates the other two (x > y + z etc.); points where some map always
// applies form the gasket and stay black. Points that reach the central hole
// (no map applies) are coloured by their position in the hole, dimmed by the
// number of steps taken to get there. The step budget grows as iTime^2, so
// the picture refines progressively as it runs.

const float r3 = 1.7320508075688772;

// Maps the central hole (all of x, y, z below the sum of the other two) back
// onto the whole triangle, giving coordinates to colour escaped points by.
const mat3 ex = mat3(1.,-1.,1.,1.,1.,-1.,-1.,1.,1.);

// Barycentric coordinates on the equilateral triangle with top vertex at
// (0,1); the top vertex is pure x, the lower left pure v, the lower right w.
vec3 cartToBary(vec2 p) {
    float u = (1.0 + 2.0 * p.y) / 3.0;
    float v = (1.0 - p.y - r3 * p.x) / 3.0;
    float w = (1.0 - p.y + r3 * p.x) / 3.0;
    return vec3(u, v, w);
}

// Barycentric -> Cartesian
vec2 baryToCart(vec3 b) {
    float x = 0.5 * r3 * (b.z - b.y);
    float y = b.x - 0.5 * (b.y + b.z);
    return vec2(x, y);
}

// Fully subtractive maps: subtract the other two coordinates from one, then
// rescale by that coordinate's old value. Valid (all coordinates positive)
// exactly when the chosen coordinate dominates the other two.
vec3 fx(vec3 uv)
{
    vec3 nv = uv;
    nv.x = uv.x-uv.y-uv.z;
    nv = nv/uv.x;
    return nv;
}

vec3 fy(vec3 uv)
{
    vec3 nv = uv;
    nv.y = uv.y-uv.x-uv.z;
    nv = nv/uv.y;
    return nv;
}

vec3 fz(vec3 uv)
{
    vec3 nv = uv;
    nv.z = uv.z-uv.x-uv.y;
    nv = nv/uv.z;
    return nv;
}

float minV(vec3 uv)
{
    return min(min(uv.x,uv.y),uv.z);
}

// Colours for the three vertex directions; cs pushes them past 1 so the
// mix stays bright after the depth dimming.
const float cs = 1.3;

const vec3 coly = cs*vec3(1.,0.,0.);
const vec3 colx = cs*vec3(1.,1.,1.);
const vec3 colz = cs*vec3(0.,0.,1.);

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy-vec2(.5,.5);

    uv.x = uv.x*iResolution.x/iResolution.y;
    uv=uv*1.65;
    uv.y=uv.y+.25;

    vec3 bary = cartToBary(uv);

    vec4 col = vec4(0.,0.,0.,0.);

    if(minV(bary) > 0.)
    {
        // Capped: by 256 iterations the point has converged or escaped at
        // float precision, and an unbounded count grows GPU cost forever.
        int steps = min(int(floor(iTime*iTime))+1, 256);

        for(int i=0; i<steps; i++)
        {
            if(minV(fx(bary)) > 0.) { bary = fx(bary); }
            else if(minV(fy(bary)) > 0.) { bary = fy(bary); }
            else if(minV(fz(bary)) > 0.) { bary = fz(bary); }
            else {
                // Escaped into the central hole: colour by position in the
                // hole, dimmed slowly with the escape depth i.
                bary = ex*bary;
                col = vec4((colx*bary.x+coly*bary.y+colz*bary.z)/(log(log(float(i+1))+1.)+1.), 1.);
                break;
            }
        }
    }

    fragColor = col;
}