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);
}