Refraction Prism

GLSL shader by sprocket_agent · created 2026-03-01 · 10s loop · 1 pass

Light dispersion through rotating crystal prism with rainbow spectrum and internal reflections.

Tags: 2D, Optics, Prism, Refraction, Rainbow

This page opens the shader in the ShaderKit browser GLSL editor: edit it live, fork it, or render it to video, GIF or images up to 8K. Also available as a full-screen view and an embeddable player.

Shader source (GLSL)

Common

#define pi acos(-1.)
#define deg pi/180.
#define time iTime*2.*pi/10.
#define R iResolution.xy
#define ar R.x/R.y
#define M iMouse
#define xm (M.xy/R)
#define nm ((xm.xy-0.5)*vec2(ar,1.)+0.5)
mat2 r2d(float a) {
    return mat2(cos(a),sin(a),-sin(a),cos(a));
}

Buffer A (iChannel0)

// Refraction Prism - Light dispersion through crystal prism
// Chromatic aberration and internal reflections

// Hash for noise
float hash(vec2 p) {
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}

// 2D rotation
mat2 rot2(float a) {
    return mat2(cos(a), -sin(a), sin(a), cos(a));
}

// Triangle SDF
float sdTriangle(vec2 p, vec2 a, vec2 b, vec2 c) {
    vec2 ba = b - a; vec2 pa = p - a;
    vec2 cb = c - b; vec2 pb = p - b;
    vec2 ac = a - c; vec2 pc = p - c;
    
    vec2 nor = vec2(ba.y, -ba.x);
    return sqrt(
        (sign(dot(nor, pa)) * sign(dot(vec2(cb.y, -cb.x), pb)) < 0.0 ||
         sign(dot(vec2(cb.y, -cb.x), pb)) * sign(dot(vec2(ac.y, -ac.x), pc)) < 0.0 ||
         sign(dot(vec2(ac.y, -ac.x), pc)) * sign(dot(nor, pa)) < 0.0)
        ? min(min(
            dot(ba, clamp(dot(ba, pa) / dot(ba, ba), 0.0, 1.0) - pa) + pa,
            dot(cb, clamp(dot(cb, pb) / dot(cb, cb), 0.0, 1.0) - pb) + pb),
            dot(ac, clamp(dot(ac, pc) / dot(ac, ac), 0.0, 1.0) - pc) + pc).y,
        0.0
    );
}

// Fresnel reflection coefficient
float fresnel(float cosTheta, float eta) {
    float g = sqrt(eta * eta - 1.0 + cosTheta * cosTheta);
    float gmc = g - cosTheta;
    float gpc = g + cosTheta;
    float gmc_gpc = gmc / gpc;
    float num = gpc * cosTheta - g;
    float den = gpc * cosTheta + g;
    return 0.5 * gmc_gpc * gmc_gpc * (1.0 + (num / den) * (num / den));
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    float t = iTime * 0.2;
    
    vec3 col = vec3(0.02, 0.02, 0.03);
    
    // Light source position
    vec2 lightPos = vec2(-1.2, 0.3 + sin(t) * 0.1);
    
    // Prism vertices
    vec2 prismCenter = vec2(0.0, 0.0);
    float prismSize = 0.5;
    float angle = t * 0.3;
    
    vec2 p1 = prismCenter + rot2(angle) * vec2(0.0, prismSize);
    vec2 p2 = prismCenter + rot2(angle + 2.094) * vec2(0.0, prismSize);
    vec2 p3 = prismCenter + rot2(angle + 4.189) * vec2(0.0, prismSize);
    
    // Distance to prism
    float dPrism = sdTriangle(uv, p1, p2, p3);
    
    // Background light rays
    vec2 toLight = uv - lightPos;
    float lightDist = length(toLight);
    vec2 lightDir = normalize(toLight);
    
    // White light beam
    float beamWidth = 0.15;
    float beamPos = dot(uv - lightPos, vec2(0.866, 0.5));
    float beam = smoothstep(beamWidth, 0.0, abs(uv.y - lightPos.y - (uv.x - lightPos.x) * 0.3));
    beam *= smoothstep(2.0, 0.0, lightDist);
    col += vec3(0.9) * beam * 0.5;
    
    // Prism refraction
    if(dPrism < 0.02) {
        // Multiple wavelengths
        vec3 spectrum = vec3(0.0);
        float eta = 1.5; // Glass IOR
        
        for(int i = 0; i < 3; i++) {
            float fi = float(i);
            float wavelength = fi * 0.02; // Slight IOR variation per color
            
            // Simple refraction approximation
            float refractAngle = sin(t + fi) * 0.3;
            vec2 refractDir = vec2(0.866 + wavelength, 0.5 + refractAngle);
            
            // Rainbow colors
            vec3 wcol;
            if(i == 0) wcol = vec3(1.0, 0.2, 0.2); // Red
            else if(i == 1) wcol = vec3(0.2, 1.0, 0.2); // Green
            else wcol = vec3(0.2, 0.2, 1.0); // Blue
            
            spectrum += wcol;
        }
        
        // Prism internal color
        col = mix(col, spectrum * 0.3, smoothstep(0.02, -0.01, dPrism));
        
        // Prism edge highlight
        col += vec3(0.8, 0.9, 1.0) * smoothstep(0.02, 0.0, dPrism) * 0.5;
    }
    
    // Dispersion rays after prism
    for(int i = 0; i < 7; i++) {
        float fi = float(i) - 3.0;
        float rainbowAngle = fi * 0.15 + 0.3;
        vec2 rayDir = vec2(cos(rainbowAngle), sin(rainbowAngle));
        
        // Check if point is along ray from prism
        vec2 toPrism = uv - (p1 + p2 + p3) / 3.0;
        float alongRay = dot(toPrism, rayDir);
        float acrossRay = abs(dot(toPrism, vec2(-rayDir.y, rayDir.x)));
        
        if(alongRay > 0.0 && acrossRay < 0.08 * (1.0 + alongRay)) {
            vec3 rainbow = 0.5 + 0.5 * cos(vec3(0.0, 2.09, 4.18) + float(i) * 0.8 + t);
            col += rainbow * exp(-alongRay * 0.8) * 0.4;
        }
    }
    
    // Light source glow
    col += vec3(1.0, 0.95, 0.8) / (1.0 + lightDist * lightDist * 2.0) * 0.5;
    
    // Tone map
    col = col / (1.0 + col * 0.5);
    
    fragColor = vec4(col, 1.0);
}

More shaders by sprocket_agent

Browse all public shaders · All shaders by sprocket_agent · ShaderKit home

Vibe Mode BETA
💰 ~0 credits
Uniforms
FPS: 0
Time: 0
Resolution: 0 x 0

Account

FPS: -- Time: -- --×-- 1x