#define SAMPLES 8 #define BOUNCES 8 // sphere of size ra centered at point ce vec2 sphIntersect(in vec3 ro, in vec3 rd, in vec3 ce, float ra) { vec3 oc = ro - ce; float b = dot( oc, rd ); vec3 qc = oc - b*rd; float h = ra*ra - dot( qc, qc ); if( h<0.0 ) return vec2(-1.0); // no intersection h = sqrt( h ); return vec2( -b-h, -b+h ); } // disk center c, normal n, radius r float diskIntersect( in vec3 ro, in vec3 rd, vec3 c, vec3 n, float r ) { vec3 o = ro - c; float t = -dot(n,o)/dot(rd,n); vec3 q = o + rd*t; return (dot(q,q) 1e-3) { Hit.t = d; Hit.nor = normalize(ro + rd * d - o); Hit.mat = mat; } } void intersect_disk(in vec3 ro, in vec3 rd, in vec3 o, in vec3 n, in float r, in Mat mat) { float d = diskIntersect(ro, rd, o, n, r); if (d < Hit.t && d > 1e-3) { Hit.t = d; Hit.nor = n; Hit.mat = mat; } } void intersect(in vec3 ro, in vec3 rd) { Hit.t = 1e3; intersect_sp(ro, rd, vec3(0,1,0), 1.0, WHITE); intersect_sp(ro, rd, vec3(2,1,0), 1.0, WHITE); intersect_sp(ro, rd, vec3(2,3,-0.5), 0.5, LIGHT); intersect_sp(ro, rd, vec3(-2,4,+0.5), 0.5, LIGHT2); intersect_sp(ro, rd, vec3(0,3,0), 1.0, WHITE); intersect_disk(ro, rd, vec3(0,0,0), vec3(0,1,0), 4.0, WHITE); } vec3 cosine_hemisphere(in float ra) { float u1 = gold_noise(); float u2 = gold_noise(); float r = sqrt(u1) * ra; float theta = 2.0 * PI * u2; float x = r * cos(theta); float y = r * sin(theta); return vec3(x, y, sqrt(1.0 - u1)); } // project vector b onto vector a vec3 project(in vec3 a, in vec3 b) { return a * (dot(a, b) / dot(a, a)); } // construct a 3D coordinate system with the input up being the "upwards" facing vector // which will be directly stored in w. // Mathematically this function will create two non linear vectors of up and generate an orthonormal // basis using gram-schmidt. // This function assumes "up" being already normalized void construct_orthonormal_basis(in vec3 up, out vec3 u, out vec3 v, out vec3 w) { w = up; vec3 n2 = normalize(cross(w, vec3(0, 1, 1))); // build perpendicular vector from w vec3 n3 = cross(w, n2); // create 2nd vector perpendicular to w and n2 // gram schmidt u = n2 - (project(w, n2)); v = n3 - project(w, n3) - project(u, n3); } vec3 gen_diffuse(in vec3 normal, in vec3 incident) { vec3 hemisphere = cosine_hemisphere(1.0); vec3 u, v, w; construct_orthonormal_basis(normal, u, v, w); return u * hemisphere.x + v * hemisphere.y + w * hemisphere.z; } vec3 gen_reflection(in vec3 normal, in vec3 incident, in float roughness) { vec3 hemisphere = cosine_hemisphere(roughness); vec3 u, v, w; construct_orthonormal_basis(reflect(incident, normal), u, v, w); return u * hemisphere.x + v * hemisphere.y + w * hemisphere.z; } float schlick(in float cosTheta, in float R0) { return R0 + (1.0 - R0) * pow(1.0 - cosTheta, 5.0); } float R0(in float ior) { float a = (ior - 1.0); float b = (ior + 1.0); return (a * a) / (b * b); } vec3 trace_path(in vec3 ro, in vec3 rd) { vec3 rad = vec3(1); vec3 acc = vec3(0); for (int i = 0; i < BOUNCES; i++) { intersect(ro, rd); if (Hit.t < 1e3) { float cosTheta = abs(dot(rd, Hit.nor)); float pdf = cosTheta * INV_PI; acc += rad * Hit.mat.emission * pdf; ro = ro + rd * Hit.t; if (schlick(cosTheta, R0(1.450)) < gold_noise()) { rd = gen_diffuse(Hit.nor, rd); rad *= Hit.mat.albedo; } else { rd = gen_reflection(Hit.nor, rd, 0.05); } } else { acc += rad * pow(texture(iChannel1, rd).rgb, vec3(2.0)) * 8.0; break; } } return acc; } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { init_random_state(fragCoord.xy, iTime); vec2 taa_off = vec2(gold_noise(), gold_noise()) * 1.5 - 0.5; vec2 uv = (fragCoord + taa_off - 0.5 * iResolution.xy) / iResolution.y; vec3 col = vec3(0); float s = sin(iTime); float c = cos(iTime); mat2 r = mat2(c, -s, s, c); vec3 rd = normalize(vec3(uv, 1.0)); vec3 ro = vec3(0,2,-8.0); float t = 0.01; rd.xz *= r; ro.xz *= r; for (int i = 0; i < SAMPLES; i++) { col += trace_path(ro, rd); } col /= float(SAMPLES); vec3 prev = texture(iChannel0, fragCoord/iResolution.xy).rgb; fragColor = vec4(mix(prev, col, 0.3) ,1.0); }