Skip to content

Instantly share code, notes, and snippets.

@mwalczyk
Created May 9, 2020 23:04
Show Gist options
  • Save mwalczyk/e1a09bab6633af0d40a8792c99ce40d1 to your computer and use it in GitHub Desktop.
Save mwalczyk/e1a09bab6633af0d40a8792c99ce40d1 to your computer and use it in GitHub Desktop.
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