Anthony Del Ciotto's Website

Digital Art: Plasma Cube

2019-06-24

Plasma Cube is a retro digital art piece implemented entirely in a GLSL fragment shader. You can run the shader live on this page by hovering over the preview below and clicking the play button.

The shader was written in Visual Studio Code using the glsl-canvas extension. Original source code is shown below:

// Plasma Cube by adelciotto
// References:
// - https://github.com/ajweeks/RaymarchingWorkshop
// - https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
precision mediump float;

uniform vec2 u_resolution;
uniform float u_time;

#define RETRO_MODE 1

const float RETRO_PIXEL_SIZE = 10.0;
const float PI = 3.1415926535;
const float MIN_DIST = 0.0;
const float MAX_DIST = 100.0;
const float EPSILON = 0.001;
const float GAMMA_CORRECTION = 0.4545;
const int MAX_MARCHING_STEPS = 255;

float t = 0.0;

mat2 rotate(float angle) {
    float s = sin(angle);
    float c = cos(angle);
    return mat2(c, s, - s, c);
}

float cubeSDF(vec3 p) {
    p.xz *= rotate(t);
    p.yx *= rotate(t * 0.6);
    p.zx *= rotate(t * 0.4);
    p.y += sin(t * 0.2);
    vec3 d = abs(p) - vec3(1.0);
    float dist = length(max(d, 0.0)) + min(max(d.x, max(d.y, d.z)), 0.0);
    return dist - 0.1;
}

vec3 estimateNormal(vec3 p) {
    return normalize(
        vec3(
            cubeSDF(vec3(p.x + EPSILON, p.yz)) - cubeSDF(vec3(p.x - EPSILON, p.yz)),
            cubeSDF(vec3(p.x, p.y + EPSILON, p.z)) - cubeSDF(vec3(p.x, p.y - EPSILON, p.z)),
            cubeSDF(vec3(p.xy, p.z + EPSILON)) - cubeSDF(vec3(p.xy, p.z - EPSILON))
        )
    );
}

// modified plasma effect from https://www.bidouille.org/prog/plasma
vec3 plasma(vec3 p, float scale) {
    p *= scale;

    float time = t * 0.3;
    float v1 = sin(p.x + time);
    float v2 = sin(p.y + time);
    float v3 = sin(p.z + time);
    float v4 = sin(p.x + p.y + p.z + time);
    float v5 = sin(length(p) + 1.7 * time);
    float v = v1 + v2 + v3 + v4 + v5;

    v *= 2.0;
    vec3 col = vec3(sin(v * PI), sin(v * PI + 2.0 * PI / 3.0), sin(v * PI + 4.0 * PI / 3.0));
    return col * 0.5 + 0.5;
}

void main() {
    vec2 fragCoord = gl_FragCoord.xy;
    #if RETRO_MODE
    fragCoord = ceil(fragCoord / RETRO_PIXEL_SIZE) * RETRO_PIXEL_SIZE;
    #endif
    vec2 uv = vec2(fragCoord - 0.5 * u_resolution.xy);
    uv = 2.0 * uv.xy / u_resolution.y;

    t = u_time + 0.33 * sin(uv.x * 1.76 + uv.y + u_time);

    vec3 camPos = vec3(0, 0, - 8);
    vec3 at = vec3(0, 0, 0);
    vec3 camForward = normalize(at - camPos);
    vec3 camRight = normalize(cross(vec3(0.0, 1.0, 0.0), camForward));
    vec3 camUp = normalize(cross(camForward, camRight));
    vec3 rayDir = normalize(uv.x * camRight + uv.y * camUp + camForward * 2.0);

    float depth = MIN_DIST;
    vec3 col = vec3(0.0);
    for(int i = 0; i < MAX_MARCHING_STEPS; i ++ ) {
        vec3 p = camPos + depth * rayDir;
        float dist = cubeSDF(p);
        if (dist < EPSILON) {
            vec3 light = normalize(vec3(sin(t) * 1.0, cos(t * 0.5) + 0.5, - 0.5));
            vec3 norm = estimateNormal(p);
            vec3 directional = vec3(1.80, 1.27, 0.99) * max(dot(norm, light), 0.0);
            vec3 ambient = vec3(0.02, 0.02, 0.02);
            vec3 diffuse = plasma(p, 1.0) * (directional + ambient);
            col = diffuse;
            break;
        }
        depth += dist;
        if (depth >= MAX_DIST) {
            break;
        }
    }

    col = pow(col, vec3(GAMMA_CORRECTION));
    gl_FragColor = vec4(smoothstep(0.0, 1.0, col), 1.0);
}

The pixelated effect can be disabled by setting RETRO_MODE = 0.

Plasma Cube with RETRO_MODE off

Plasma Cube with RETRO_MODE off

I may update this article soon with a more in-depth explanation of the shader source-code.