radiance 0.7.1

Video art software designed for live performance
Documentation
#property description 3D cube
#property frequency 1

// From maze.wgsl
fn viewMatrix(eye: vec3<f32>, dir: vec3<f32>, up: vec3<f32>) -> mat4x4<f32> {
    // Based on gluLookAt man page
    let f = normalize(dir);
    let s = normalize(cross(f, up));
    let u = cross(s, f);
    return mat4x4<f32>(
        vec4<f32>(s, 0.0),
        vec4<f32>(u, 0.0),
        vec4<f32>(-f, 0.0),
        vec4<f32>(0.0, 0.0, 0.0, 1.0)
    );
}

// Draw a copy of the input filling the given quad
fn draw(bottomLeft: vec2<f32>, bottomRight: vec2<f32>, topLeft: vec2<f32>, topRight: vec2<f32>, uv: vec2<f32>) -> vec4<f32> {
    let ssi = smoothstep(0., 0.2, iIntensity);

    // Four-point projection taken from https://math.stackexchange.com/a/339033
    // Step 1: Compute b in a * e = d
    let a = mat3x3<f32>(vec3<f32>(bottomLeft, 1.),
                        vec3<f32>(bottomRight, 1.),
                        vec3<f32>(topLeft, 1.));
    let d = vec3(topRight, 1.);
    // TODO inverse can be replaced with adjugate
    let e = inverse3(a) * d;

    // Step 2: Scale matrix a by e
    let a2 = mat3x3<f32>(
        a[0] * e.x,
        a[1] * e.y,
        a[2] * e.z,
    );

    // Step 3: Compute the matrix B
    // which is a constant, so just write it down
    let b = mat3x3<f32>(0., 0., -1.,
                        1., 0.,  1.,
                        0., 1.,  1.);

    // Step 4, 5: Get the combined matrix transform c
    // TODO inverse can be replaced with adjugate
    let c = b * inverse3(a2);

    // Step 6: Project the point uv using transform c
    let h = c * vec3((uv - 0.5) * mix(vec2(1.), aspectCorrection, ssi), 1.);

    // Step 7: De-homogenize
    let newUV = h.xy / h.z;

    // Crop the output square if its not
    let squareUV = (newUV - 0.5) / aspectCorrection + 0.5;

    return textureSample(iInputsTex[0], iSampler, mix(newUV, squareUV, ssi)) * box(newUV);
}

fn main(uv: vec2<f32>) -> vec4<f32> {
    let ssi = smoothstep(0., 0.4, iIntensity);

    var cubePoints = array<vec3<f32>, 8>(
        vec3<f32>(-1., -1.,  1.),
        vec3<f32>( 1., -1.,  1.),
        vec3<f32>(-1.,  1.,  1.),
        vec3<f32>( 1.,  1.,  1.),
        vec3<f32>(-1., -1., -1.),
        vec3<f32>( 1., -1., -1.),
        vec3<f32>(-1.,  1., -1.),
        vec3<f32>( 1.,  1., -1.)
    );

    // Indices into cubePoints
    var faces = array<vec4<i32>, 6>(
        vec4<i32>(0, 1, 2, 3), // Front
        vec4<i32>(1, 5, 3, 7), // Right
        vec4<i32>(2, 3, 6, 7), // Top
        vec4<i32>(5, 4, 7, 6), // Back
        vec4<i32>(4, 0, 6, 2), // Left
        vec4<i32>(4, 5, 0, 1) // Bottom
    );

    // Perspective
    // TBH I don't actually know how perspective works
    let p = -0.2 * ssi;

    // Zoom
    let z = mix(0.5, 0.2, sqrt(ssi));

    let xf = mat4x4<f32>(z,  0., 0., 0.,
                         0., z,  0., 0.,
                         0., 0., z,  p,
                         0., 0., 0., 1.);

    let t = iIntensityIntegral * iFrequency + 1.;
    let eye = vec3<f32>(sin(t), sin(0.5 * t), cos(t));
    let eye2 = mix(vec3<f32>(0., 0., 1.), eye, ssi);
    // TODO inverse can be replaced with adjugate
    let xf2 = xf * inverse4(viewMatrix(eye2, -eye2, vec3<f32>(0., 1., 0.)));

    // Use the transformation matrix xf2 to project cubePoints
    for (var i: i32 = 0; i < 8; i++) {
        let h = xf2 * vec4(cubePoints[i], 1.);
        let cubePointXY = h.xy / h.w;
        // Don't project z, we will use that for occlusion checking
        let cubePointZ = h.z;
        cubePoints[i] = vec3<f32>(cubePointXY, cubePointZ);
    }

    // From https://stackoverflow.com/a/749206
    // BUBBLE SORT
    // so that we draw in the right order
    for (var n: i32 = 5; n != 0; n--) {
        for(var i: i32 = 0; i < n; i++) {
            // Compare the center of each cube face
            if cubePoints[faces[i].x].z + cubePoints[faces[i].w].z
            >  cubePoints[faces[i+1].x].z + cubePoints[faces[i+1].w].z {
                let tmp = faces[i];
                faces[i] = faces[i+1];
                faces[i+1] = tmp;
            }
        }
    }

    // Draw the six faces
    var c = vec4<f32>(0.);

    for (var i: i32 = 0; i < 6; i++) {
        let face = faces[i];
        let adj = select(1., smoothstep(0., 0.1, iIntensity), i == 0);
        c = composite(c, draw(cubePoints[face.x].xy, cubePoints[face.y].xy, cubePoints[face.z].xy, cubePoints[face.w].xy, uv) * adj);
    }
    return c;
}