0.00
60.0 fps

Voxel tunnel

Real-time path traced voxel tunnel.

Log in to post a comment.

precision highp float;

// Forked from "Fractal cube" by reinder
// https://oneshader.net/shader/e94495f6e9


uniform float iTime;
uniform vec2  iResolution;

#define PATH_LENGTH 3
#define SAMPLES_PER_PIXEL 8

#define MAX_DIST 1e10
#define PI 3.14159265359
#define MAX_FLOAT 1e5
#define EPSILON 0.001

#define LAMBERTIAN 0.
#define METAL 1.
#define DIFFUSE_LIGHT 2.

uniform float speed; // value=10, min=5, max=50, step=0.1
  
  
uniform float gridFill;// value=1.6, min=1., max=4., step=0.01
uniform float lightFill;// value=0.9, min=0.75, max=0.98, step=0.01

uniform float matType;// value=1, min=0, max=1, step=1 (Lambertian, Metal)
uniform float matRoughness;// value=0.15, min=0.05, max=1, step=0.01

uniform float colorScale;// value=0.3, min=0, max=1, step=0.01
uniform float colorOffset;// value=5.6, min=0, max=6.4, step=0.01

float seed;
float mat_hash;

vec3  bgCol = (.6 + .4 * cos(12.5663706144 * colorScale + colorOffset +  vec3(0., 0.6, 1.2)));

// Hash without Sine
// MIT License...
// Copyright (c)2014 David Hoskins.
// https://www.shadertoy.com/view/4djSRW

float hash1(inout float p) {
    p = fract((p+=0.1) * .1031);
    p *= p + 33.33;
    p *= p + p;
    return fract(p);
}

vec2 hash2(inout float p) {
    vec3 p3 = fract(vec3(p+=1.) * vec3(.1031, .1030, .0973));
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.xx+p3.yz)*p3.zy);
}

vec3 hash3(inout float p) {
    vec3 p3 = fract(vec3(p+=1.) * vec3(.1031, .1030, .0973));
    p3 += dot(p3, p3.yzx+33.33);
    return fract((p3.xxy+p3.yzz)*p3.zyx);
}

float gpuIndepentHash(float p) {
    p = fract(p * .1031);
    p *= p + 19.19;
    p *= p + p;
    return fract(p);
}

float grid(in vec3 ro, in vec3 rd, in vec2 distBound, inout vec3 normal) {
    const int steps = 32;

    vec3 ros = ro;
    vec3 pos = floor(ros + rd * distBound.x);
    vec3 ri = 1.0/rd;
    vec3 rs = sign(rd);
    vec3 dis = (pos-ros + 0.5 + rs*0.5) * ri;

    float res = 0.0, grid_seed;
    vec3 mm = vec3(0.0);
    bool hit = false;

    for (int i=0; i<steps; i++) {
        mm = step(dis.xyz, dis.yxy) * step(dis.xyz, dis.zzx);
        dis += mm * rs * ri;
        pos += mm * rs;

        vec3 hash_pos = pos + .5;
        hash_pos = abs(hash_pos);

        grid_seed = dot(hash_pos, vec3(sqrt(5.), sqrt(2.), sqrt(3.)));
        // grid_seed = dot(hash_pos, vec3(1));
        // grid_seed = dot(hash_pos, vec3(1,1,1));
        grid_seed += floor(grid_seed);
        float grid_hash = hash1(grid_seed);
        if (length(pos.xy + .5) > 2.5 + grid_hash * gridFill) {
            hit = true;
            break;
        }
    }

    if (hit) {
        // intersect the cube
        vec3 mini = (pos-ros + 0.5 - 0.5*vec3(rs))*ri;
        float t = max (mini.x, max (mini.y, mini.z));

        mat_hash = hash1(grid_seed);

        normal = -mm*rs;
        //  matCol *= .5;
        return t;
    } else {
        return MAX_DIST;
    }
}

//
// Ray tracer helper functions
//


vec3 cosWeightedRandomHemisphereDirection(const vec3 n, inout float seed) {
    vec2 r = hash2(seed);
    vec3  uu = normalize(cross(n, abs(n.y) > .5 ? vec3(1., 0., 0.) : vec3(0., 1., 0.)));
    vec3  vv = cross(uu, n);
    float ra = sqrt(r.y);
    float rx = ra*cos(6.28318530718*r.x);
    float ry = ra*sin(6.28318530718*r.x);
    float rz = sqrt(1.-r.y);
    vec3  rr = vec3(rx*uu + ry*vv + rz*n);
    return normalize(rr);
}

vec3 modifyDirectionWithRoughness(const vec3 normal, const vec3 n, const float roughness, inout float seed) {
    vec2 r = hash2(seed);

    vec3  uu = normalize(cross(n, abs(n.y) > .5 ? vec3(1., 0., 0.) : vec3(0., 1., 0.)));
    vec3  vv = cross(uu, n);

    float a = roughness*roughness;

    float rz = sqrt(abs((1.0-r.y) / clamp(1.+(a - 1.)*r.y, .00001, 1.)));
    float ra = sqrt(abs(1.-rz*rz));
    float rx = ra*cos(6.28318530718*r.x);
    float ry = ra*sin(6.28318530718*r.x);
    vec3  rr = vec3(rx*uu + ry*vv + rz*n);

    vec3 ret = normalize(rr);
    return dot(ret, normal) > 0. ? ret : normalize(ret + 2. * dot(ret, normal) * n);
}

vec2 randomInUnitDisk(inout float seed) {
    vec2 h = hash2(seed) * vec2(1, 6.28318530718);
    float phi = h.y;
    float r = sqrt(h.x);
    return r*vec2(sin(phi), cos(phi));
}

//
// Scene description
//

float worldhit(in vec3 ro, in vec3 rd, in vec2 dist, out vec3 normal) {
    return grid(ro, rd, dist, normal);
}

vec3 getSkyColor(vec3 rd) {
    float amb = 6. - 2. * rd.y;
    float sun = clamp(dot(normalize(vec3(.4, .7, 1.2)), rd), 0., 1.);

    sun = (pow(sun, 4.) + 20.*pow(sun, 32.));

    return (.6 + .4 * cos(12.5663706144 * colorScale + colorOffset +  vec3(0., 0.6, 1.2))) * sun + amb;
}

//
// Simple ray tracer
//

vec3 render(in vec3 ro, in vec3 rd) {
    vec3 col = vec3(0), normal, albedo, emit;
    vec3 emitted = vec3(0);
    float type, roughness;

    // bounding box
    
    float d = min(abs(1.99/rd.y), abs(1.99/rd.x));

    for (int i=0; i<PATH_LENGTH; i++) {
        float res = worldhit(ro, rd, vec2(i == 0 ? d : .0001, MAX_DIST), normal);

        if (res < MAX_DIST) {
            ro +=  rd * res;

            // cube
            const float ga = 0.39996322972865332;
            if (matType < .5) {
                albedo = .5 + .4 * cos(mat_hash * (12.5663706144 * colorScale) + colorOffset +   vec3(0., 0.6, 1.2));
                albedo *= albedo;
            } else {
                type = 1.;
                albedo = .4 + .3 * cos((12.5663706144 * colorScale) + colorOffset +  vec3(0., 0.6, 1.2));
                albedo *= albedo;
                roughness = mat_hash * mat_hash * matRoughness;
            }
            
            float l = smoothstep(lightFill, 1., abs(fract(gpuIndepentHash(mat_hash) + iTime * 0.1) * 2. - 1.));
            
            if (l > 0.) {
                emit = bgCol * (75. * l);
            }

            emit += (1. - exp(-0.0015*res)) * bgCol;

            emitted += i == 0 ? emit : col * emit;
            col = i == 0 ? albedo : col * albedo;

            if (matType < LAMBERTIAN+.5) {
                rd = cosWeightedRandomHemisphereDirection(normal, seed);
            } else {
                rd = modifyDirectionWithRoughness(normal, reflect(rd, normal), roughness, seed);
            }
        } else {
            return emitted + (.5 * bgCol);// + col * getSkyColor(rd);
        }
        //      if(dot(col,col) < 0.0001) return emitted + col * getSkyColor(rd);
    }
    return emitted;
}

void main() {

    float fpd = 20.;
    vec3  col = vec3(0);
    vec2  fc = gl_FragCoord.xy;
    
    for (int i=0; i<SAMPLES_PER_PIXEL; i++) {
        seed = iTime + 100.*(float(i)*2.399+hash1(fc.x)+hash1(fc.y));
        // AA
        vec2 p = (2. * gl_FragCoord.xy - iResolution) / iResolution.y;
        p += 2.*hash2(seed)/iResolution.y;
        
        p *= 1. + .25 *  dot(p,p);

        vec3 ro = vec3(0., 0., 6. - speed * (iTime + 0.05 * gpuIndepentHash(seed)));
        vec3 rd = normalize(vec3(p.x, p.y, -2.15));
        vec3 normal;

        // DOF
        vec3 fp = ro + rd * fpd;
        ro = ro + vec3(randomInUnitDisk(seed), 0.)*.05;
        rd = normalize(fp - ro);

        vec3 outcol = render(ro, rd);

        outcol = max(vec3(0), outcol - 0.004);
        outcol = (outcol*(6.2*outcol + .5)) / (outcol*(6.2*outcol+1.7) + 0.06);

        col += outcol;
    }

    col *= 1./float(SAMPLES_PER_PIXEL);

    col += vec3(hash1(seed)) * 0.02;

    gl_FragColor = vec4(col, 1);
}