Created
          May 9, 2020 23:04 
        
      - 
      
- 
        Save mwalczyk/e1a09bab6633af0d40a8792c99ce40d1 to your computer and use it in GitHub Desktop. 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | uniform float u_elapsed_frames; | |
| uniform vec2 u_scene_seed; | |
| uniform vec2 u_camera_shift; | |
| layout(location = 0) out vec4 o_color; | |
| const float pi = 3.1415926535897932384626433832795; | |
| const uint number_of_bounces = 8; | |
| const float epsilon = 0.001; | |
| const float max_distance = 10000.0; | |
| const vec3 x_axis = vec3(1.0, 0.0, 0.0); | |
| const vec3 y_axis = vec3(0.0, 1.0, 0.0); | |
| const vec3 z_axis = vec3(0.0, 0.0, 1.0); | |
| const vec3 origin = vec3(0.0); | |
| const vec3 miss = vec3(-1.0); | |
| const vec3 black = vec3(0.0); | |
| const vec3 white = vec3(1.0); | |
| struct sphere | |
| { | |
| float r; | |
| vec3 c; | |
| vec3 albedo; | |
| }; | |
| struct plane | |
| { | |
| vec3 n; | |
| vec3 c; | |
| }; | |
| float hash(vec2 seed) | |
| { | |
| return fract(sin(dot(seed, vec2(12.9898, 78.233))) * 43758.5453); | |
| } | |
| float rand(vec2 seed) | |
| { | |
| return fract(sin(dot(seed, vec2(12.9898, 78.233) + u_elapsed_frames)) * 43758.5453); | |
| } | |
| vec3 palette(in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) | |
| { | |
| return a + b*cos(2.0 * pi * (c * t + d)); | |
| } | |
| vec3 sphere_color(in float t) | |
| { | |
| return palette(t * 0.5, vec3(0.5, 0.5, 0.5), vec3(0.5, 0.5, 0.5), vec3(1.0, 1.0, 1.0), vec3(0.00, 0.10, 0.20)); | |
| } | |
| const uint number_of_spheres = 8; | |
| const uint number_of_planes = 1; | |
| const float radius = 0.5; | |
| vec3 position_on_circle(int index) | |
| { | |
| float theta = (float(index) / float(number_of_spheres - 2)) * 2.0 * pi; | |
| const float r = 1.45; | |
| float x = cos(theta) * r; | |
| float z = sin(theta) * r; | |
| return vec3(x, -0.5, z); | |
| } | |
| sphere spheres[] = { | |
| // Ground | |
| sphere(100.0, vec3(0.0, -100.9, 0.0), vec3(1.0, 0.8, 0.8)), | |
| sphere(radius, position_on_circle(0), sphere_color(1.0 / float(number_of_spheres))), | |
| sphere(radius, position_on_circle(1), sphere_color(2.0 / float(number_of_spheres))), | |
| sphere(radius, position_on_circle(2), sphere_color(3.0 / float(number_of_spheres))), | |
| sphere(radius, position_on_circle(3), sphere_color(4.0 / float(number_of_spheres))), | |
| sphere(radius, position_on_circle(4), sphere_color(5.0 / float(number_of_spheres))), | |
| sphere(radius, position_on_circle(5), sphere_color(6.0 / float(number_of_spheres))), | |
| // Top | |
| sphere(0.95, vec3(0.0, 1.0, 0.0), vec3(0.8)) | |
| }; | |
| plane planes[] = { | |
| plane(y_axis, origin) | |
| }; | |
| bool intersect_sphere(in sphere s, in vec3 ro, in vec3 rd, out vec3 hit) | |
| { | |
| vec3 v = ro - s.c; | |
| float b = 2.0 * dot(v, rd); | |
| float c = dot(v, v) - s.r * s.r; | |
| float discriminant = b * b - 4.0 * c; | |
| if (discriminant < 0.0) | |
| { | |
| hit = miss; | |
| return false; | |
| } | |
| discriminant = sqrt(discriminant); | |
| float t0 = -b + discriminant; | |
| float t1 = -b - discriminant; | |
| float tf; | |
| if (t1 > epsilon) | |
| { | |
| tf = t1; | |
| } | |
| else if (t0 > epsilon) | |
| { | |
| tf = t0; | |
| } | |
| else | |
| { | |
| hit = miss; | |
| return false; | |
| } | |
| hit = ro + rd * tf * 0.5; | |
| return true; | |
| } | |
| bool intersect_plane(in plane p, in vec3 ro, in vec3 rd, out vec3 hit) | |
| { | |
| float denom = dot(rd, p.n); | |
| if (abs(denom) > epsilon) | |
| { | |
| vec3 temp = p.c - ro; | |
| float t = dot(temp, p.n) / denom; | |
| hit = ro + rd * t; | |
| if (t >= epsilon && hit.y < 1.0) | |
| { | |
| return true; | |
| } | |
| } | |
| else | |
| { | |
| return false; | |
| } | |
| } | |
| vec3 rand_sphere(in vec3 normal, in vec2 seed) | |
| { | |
| float z = rand(seed); | |
| float r = sqrt(1.0 - z * z); | |
| float phi = 2.0 * pi * rand(seed + 100.0); | |
| float x = cos(phi) * r; | |
| float y = sin(phi) * r; | |
| vec3 major_axis; | |
| const float isqrt = 1.0 / sqrt(3.0); | |
| if (abs(normal.x) < isqrt) | |
| { | |
| major_axis = x_axis; | |
| } | |
| else if (abs(normal.y) < isqrt) | |
| { | |
| major_axis = y_axis; | |
| } | |
| else | |
| { | |
| major_axis = z_axis; | |
| } | |
| vec3 u = normalize(cross(major_axis, normal)); | |
| vec3 v = cross(normal, u); | |
| vec3 w = normal; | |
| return normalize(u * x + v * y + w * z); | |
| } | |
| vec3 shade_sky(in vec3 rd) | |
| { | |
| float pct = 0.5 * (rd.y + 1.0); | |
| const vec3 sky = vec3(0.5, 0.7, 1.0); | |
| return mix(white, sky, pct); | |
| } | |
| bool intersect_scene(in vec3 ro, in vec3 rd, out vec3 closest_point) | |
| { | |
| float closest_t = max_distance; | |
| uint closest_index = 0; | |
| bool hit_anything = false; | |
| for(uint j = 0; j < number_of_spheres; ++j) | |
| { | |
| vec3 hit; | |
| if (intersect_sphere(spheres[j], ro, rd, hit)) | |
| { | |
| hit_anything = true; | |
| float t = length(ro - hit); | |
| if (t < closest_t) | |
| { | |
| closest_t = t; | |
| closest_point = hit; | |
| closest_index = j; | |
| } | |
| } | |
| } | |
| for(uint j = 0; j < number_of_planes; ++j) | |
| { | |
| vec3 hit; | |
| if (intersect_plane(planes[j], ro, rd, hit)) | |
| { | |
| hit_anything = true; | |
| float t = length(ro - hit); | |
| if (t < closest_t) | |
| { | |
| closest_t = t; | |
| closest_point = hit; | |
| closest_index = j; | |
| } | |
| } | |
| } | |
| return hit_anything; | |
| } | |
| mat3 lookat(in vec3 t, in vec3 p) | |
| { | |
| vec3 k = normalize(t - p); | |
| vec3 i = cross(k, vec3(0.0, 1.0, 0.0)); | |
| vec3 j = cross(i, k); | |
| return mat3(i, j, k); | |
| } | |
| vec3 trace() | |
| { | |
| // Sample texture that contains random noise | |
| vec2 jitter = texture(sTD2DInputs[0], vUV.st).rg * 2.0 - 1.0; | |
| vec2 uv = vUV.st; | |
| uv = uv * 2.0 - 1.0; | |
| uv += (jitter / 1024.0) * 2.0; | |
| vec3 camera_position = vec3(0.0, -0.25, -4.0); | |
| vec3 ro = camera_position; | |
| ro.xy += u_camera_shift; | |
| vec3 rd = normalize(lookat(origin, ro) * vec3(uv, 1.0)); | |
| vec3 attenuation = white; | |
| bool hit_anything = false; | |
| for (uint i = 0; i < number_of_bounces; ++i) | |
| { | |
| float closest_t = max_distance; | |
| vec3 closest_point; | |
| uint closest_index = 0; | |
| bool valid_intersection = false; | |
| for(uint j = 0; j < number_of_spheres; ++j) | |
| { | |
| vec3 hit; | |
| if (intersect_sphere(spheres[j], ro, rd, hit)) | |
| { | |
| valid_intersection = true; | |
| hit_anything = true; | |
| float t = length(ro - hit); | |
| if (t < closest_t) | |
| { | |
| closest_t = t; | |
| closest_point = hit; | |
| closest_index = j; | |
| } | |
| } | |
| } | |
| if (i == (number_of_bounces - 1)) | |
| { | |
| attenuation *= black; | |
| break; | |
| } | |
| if (valid_intersection) | |
| { | |
| attenuation *= spheres[closest_index].albedo; | |
| vec3 n = normalize(closest_point - spheres[closest_index].c); | |
| vec3 rand_dir = rand_sphere(n, gl_FragCoord.xy + rd.z + i); | |
| float mat = hash(vec2(closest_index)); | |
| if (closest_index == 0) mat = 0.8; | |
| ro = closest_point + n * epsilon; | |
| rd = normalize(mix(reflect(rd, n), rand_dir, mat)); | |
| } | |
| else | |
| { | |
| attenuation *= shade_sky(rd); | |
| break; | |
| } | |
| } | |
| if (!hit_anything) | |
| { | |
| return shade_sky(rd); | |
| } | |
| else | |
| { | |
| return attenuation; | |
| } | |
| } | |
| void main() | |
| { | |
| // Path tracing routine | |
| vec3 trace_color = trace(); | |
| // Apply gamma correction | |
| trace_color = pow(trace_color, vec3(1.0 / 2.2)); | |
| o_color = vec4(trace_color, 1.0); | |
| } | 
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment