Real-time path traced cubes.
Log in to post a comment.
precision highp float; uniform float iTime; uniform vec2 iResolution; #define PATH_LENGTH 4 #define SAMPLES_PER_PIXEL 16 #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 gridScale;// value=7, min=3, max=16, step=1 uniform float gridFill;// value=0.6, min=0.1, max=0.6, step=0.01 uniform float lightFill;// value=0.9, min=0.75, max=1, step=0.01 uniform float matType;// value=1, min=0, max=1, step=1 (Lambertian, Metal) uniform float matRoughness;// value=0.5, min=0.5, max=1, step=0.01 uniform float colorScale;// value=0.25, min=0, max=1, step=0.01 uniform float colorOffset;// value=4., min=0, max=6.4, step=0.01 uniform float floorBrightness;// value=0.03, min=0.025, max=0.05, step=0.01 uniform float floorRoughness;// value=0.2, min=0.02, max=0.2, step=0.001 uniform float floorType;// value=2, min=0, max=2, step=1 (Water, Solid, Checkerboard) float seed; float mat_hash; // Hash without Sine // MIT License... // Copyright (c)2014 David Hoskins. // https://www.shadertoy.com/view/4djSRW float hash1(float p) { p = fract(p * .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); } mat2 rot(float a) { return mat2(cos(a), sin(a), -sin(a), cos(a)); } // Plane float iPlane(in vec3 ro, in vec3 rd, in vec2 distBound, inout vec3 normal, in vec3 planeNormal, in float planeDist) { float a = dot(rd, planeNormal); float d = -(dot(ro, planeNormal)+planeDist)/a; if (a > 0. || d < distBound.x || d > distBound.y) { return MAX_DIST; } else { normal = planeNormal; return d; } } float grid(in vec3 ro, in vec3 rd, in vec2 distBound, inout vec3 normal) { float cubeGridScale = gridScale; const int steps = 20; vec3 ros = ro * cubeGridScale; vec3 pos = floor(ros + (all(lessThan(abs(ro), vec3(1.)))?0.:distBound.x-EPSILON) * cubeGridScale * rd); 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(2,2,3)); grid_seed += floor(iTime * .1 + grid_seed * (1./gridScale)); float grid_hash = hash1(grid_seed); if (all(lessThan(abs(pos+.5), vec3(cubeGridScale))) && grid_hash > gridFill) { hit = true; break; } else if (max(abs(pos.x), max(abs(pos.y), abs(pos.z))) > cubeGridScale + 1.) 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)) / cubeGridScale; mat_hash = hash1(-grid_seed); normal = -mm*rs; // matCol *= .5; return t; } else { return MAX_DIST; } } // Box: https://www.shadertoy.com/view/ld23DV float iBox(in vec3 ro, in vec3 rd, in vec2 distBound, inout vec3 normal, in vec3 boxSize) { mat3 rf = mat3(0.8164965, -0.5773504, -0.0000000, 0.4082484, 0.5773502, -0.7071068, 0.4082484, 0.5773502, 0.7071068); ro.xz*=rot(0.1*iTime); ro *= rf; rd.xz*=rot(0.1*iTime); rd *= rf; vec3 m = sign(rd)/max(abs(rd), 1e-8); vec3 n = m*ro; vec3 k = abs(m)*boxSize; vec3 t1 = -n - k; vec3 t2 = -n + k; float tN = max(max(t1.x, t1.y), t1.z); float tF = min(min(t2.x, t2.y), t2.z); if (tN > tF || tF <= 0.) { return MAX_DIST; } else { if (tN <= distBound.y) { // normal = -sign(rd)*step(t1.yzx,t1.xyz)*step(t1.zxy,t1.xyz); tN = grid(ro, rd, vec2(tN < 0. ? 0. : tN, tF), normal); normal = rf * normal; normal.xz*=rot(-0.1*iTime); return tN >= distBound.x ? tN : MAX_DIST; } else { return MAX_DIST; } } } // // Ray tracer helper functions // float FresnelSchlickRoughness(float cosTheta, float F0, float roughness) { return F0 + (max((1. - roughness), F0) - F0) * pow(abs(1. - cosTheta), 5.0); } 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 // vec2 opU(vec2 d, float iResult) { return (iResult < d.y) ? vec2(d.x, iResult) : d; } vec2 worldhit(in vec3 ro, in vec3 rd, in vec2 dist, out vec3 normal) { vec2 d = dist; d = opU(d, iBox (ro-vec3(0, .4, 0.), rd, d.xy, normal, vec3(1.))); d = opU(d, iPlane (ro, rd, d.xy, normal, vec3(0, 1, 0), 1.35)); return d; } float checkerBoard(vec2 p) { return mod(floor(p.x) + floor(p.y), 2.); } 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; for (int i=0; i<PATH_LENGTH; i++) { vec2 res = worldhit(ro, rd, vec2(.0001, MAX_DIST), normal); if (res.y < MAX_DIST) { ro += rd * res.y; if (ro.y < -1.34) { // floor albedo = vec3(floorBrightness / (1. + 2. * length(ro.xz))); if (floorType < 1.5) roughness = floorRoughness; else roughness = floorRoughness + (4. * floorRoughness * checkerBoard((ro.xz * rot(PI*.25)))); if (floorType < .5) { float ws = .15*sin(length(ro.xz) * 20. - iTime * 4.); float wc = .15*sin(length(ro.xz) * 20. - iTime * 4.); normal = normalize(vec3(ws, 10.-ws-wc, wc)); } } else { // 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; } if (hash1(mat_hash) > lightFill) { emit = (.5 + .4 * cos((12.5663706144 * colorScale) + colorOffset + vec3(0., 0.6, 1.2))) * 50.; } } emitted += i == 0 ? emit : col * emit; col = i == 0 ? albedo : col * albedo; if (matType < LAMBERTIAN+.5 && ro.y > -1.34) { rd = cosWeightedRandomHemisphereDirection(normal, seed); } else { rd = modifyDirectionWithRoughness(normal, reflect(rd, normal), roughness, seed); } } else { return emitted + col * getSkyColor(rd); } // if(dot(col,col) < 0.0001) return emitted + col * getSkyColor(rd); } return emitted; } void main() { float fpd = 5.; vec3 col = vec3(0); for (int i=0; i<SAMPLES_PER_PIXEL; i++) { seed = iTime + 100.*(float(i)*2.399+hash1(gl_FragCoord.x)+hash1(-gl_FragCoord.y)); // AA vec2 p = (2. * gl_FragCoord.xy - iResolution) / iResolution.y; p += 2.*hash2(seed)/iResolution.y; vec3 ro = vec3(0., .25, 6.); vec3 rd = normalize(vec3(p.x, p.y, -2.5)); 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.2; gl_FragColor = vec4(col, 1); }