Shadertoy-Shaders/pathtracing/reverse/buffer-a-glsl

215 lines
6.2 KiB
Plaintext
Raw Normal View History

2024-03-02 13:25:41 +00:00
const int DEPTH = 32;
const int AA = 8;
const vec2 epsilon = vec2(1e-3, 0);
const float planeNear = 1e-3;
const float planeFar = 1e2;
const float fov = 82.0;
const float PI = 3.141592653589793;
const float pdf = 2.0 * PI;
#define UNION(a, b) ((a.x) < (b.x) ? (a) : (b))
#define SDF vec2
#define MATERIAL_WHITE 0
#define MATERIAL_RED 1
#define MATERIAL_GREEN 2
#define MATERIAL_BLUE 3
#define MATERIAL_MIRROR 10
#define MATERIAL_GLASS 20
#define MATERIAL_EMISSION 255
SDF sdfScene(in vec3 pos)
{
vec3 q = abs(pos - vec3(0,0.99,-1)) - vec3(0.5, 0.01, 0.5);
SDF li0 = SDF(length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0), MATERIAL_EMISSION);
SDF sp1 = SDF(length(pos + vec3( 0.5, 0.5, 1)) - 0.5, MATERIAL_WHITE);
SDF sp2 = SDF(abs(length(pos + vec3(-0.5, 0.75, 1)) - 0.25) - epsilon[0], MATERIAL_GLASS);
SDF sp3 = SDF(length(pos + vec3( 0.5,-0.25, 1)) - 0.25, MATERIAL_MIRROR);
SDF pl0 = SDF( pos.y + 1.0, MATERIAL_WHITE);
SDF pl1 = SDF(-pos.y + 1.0, MATERIAL_WHITE);
SDF pl2 = SDF( pos.x + 1.0, MATERIAL_RED);
SDF pl3 = SDF(-pos.x + 1.0, MATERIAL_GREEN);
SDF pl4 = SDF(-pos.z, MATERIAL_BLUE);
SDF pl5 = SDF( pos.z + 4.1, MATERIAL_WHITE);
SDF box = UNION(pl0, pl1);
box = UNION(box, pl2);
box = UNION(box, pl3);
box = UNION(box, pl4);
box = UNION(box, pl5);
box = UNION(box, sp1);
box = UNION(box, sp2);
box = UNION(box, sp3);
return UNION(li0, box);
}
vec3 sceneNormal(in vec3 pos)
{
return normalize(sdfScene(pos)[0] - vec3(
sdfScene(pos + epsilon.xyy)[0],
sdfScene(pos + epsilon.yxy)[0],
sdfScene(pos + epsilon.yyx)[0]
));
}
// 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.0, 1.0))); // 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);
}
// generate a normalized vector within the bounds of the hemisphere with radius of 1.
// Z-Coordinate will be "upwards".
// radius determines the maximum radius the output vector will have.
// NOTE: shrinkin radius will still result in the output to be normalized
vec3 cosine_weighted_hemisphere()
{
float u1 = random();
float u2 = random();
float r = sqrt(u1);
float theta = 2.0 * PI * u2;
float x = r * cos(theta);
float y = r * sin(theta);
return vec3(x, y, sqrt(1.0 - u1));
}
vec3 generate_brdf_ray_direction(in vec3 normal, in vec3 incident) {
vec3 hemisphere = cosine_weighted_hemisphere();
vec3 u, v, w;
construct_orthonormal_basis(normal, u, v, w);
return u * hemisphere.x + v * hemisphere.y + w * hemisphere.z;
}
vec3 tracePath(in vec3 ro, in vec3 rd)
{
vec3 col = vec3(0);
vec3 thr = vec3(1);
for (int k = 0; k < DEPTH; k++)
{
vec3 pos = ro + rd * planeNear;
float t = planeNear;
for (int i = 0; i < 256 && t < planeFar; i++)
{
SDF h = sdfScene(pos);
if (h[0] < epsilon[0])
{
vec3 nor = sceneNormal(pos);
switch(int(h[1]))
{
case MATERIAL_MIRROR:
rd = reflect(rd, nor);
ro = pos - nor * epsilon[0];
break;
case MATERIAL_GLASS:
if (dot(nor, rd) > random())
{
rd = refract(rd, -nor, 0.2);
ro = pos + nor * epsilon[0] * 4.0;
}
else
{
rd = reflect(rd, nor);
ro = pos - nor * epsilon[0];
}
break;
default:
rd = -generate_brdf_ray_direction(nor, rd);
ro = pos - nor * epsilon[0];
break;
}
float cosTheta = abs(dot(nor, -rd)) * pdf / PI * 0.6667;
switch(int(h[1]))
{
case MATERIAL_EMISSION:
col += thr * 10.0;
break;
case MATERIAL_WHITE:
thr *= cosTheta;
break;
case MATERIAL_RED:
thr *= vec3(0.9, 0.1, 0.1) * cosTheta;
break;
case MATERIAL_GREEN:
thr *= vec3(0.1, 0.9, 0.1) * cosTheta;
break;
case MATERIAL_BLUE:
thr *= vec3(0.1, 0.1, 0.9) * cosTheta;
break;
default:
break;
}
break;
}
t += h[0];
pos += rd * h[0];
}
float m = max(thr.x, max(thr.y, thr.z));
if (random() > m)
break;
}
return col;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
SEED = (fragCoord.x * fragCoord.y)/iResolution.y + iTime;
vec3 col = vec3(0);
vec3 ro = vec3(0,0,-3.999);
// take multiple samples per pixel
// offset each ray direction slightly inside pixel
// in order to anti aliase
for (int i = 0; i < AA; i++, SEED++)
{
vec2 off = vec2(random(), random()) * 0.5;
vec2 uv = (fragCoord + off -0.5 * iResolution.xy) / iResolution.y;
vec3 rd = normalize(vec3(uv, 1.0/tan(fov * 0.008726646259971648)));
col += tracePath(ro, rd);
}
col /= float(AA);
vec3 prev = texture(iChannel0, fragCoord.xy/iResolution.xy).rgb;
col = mix(prev, col, 1.0/max(float(iFrame), 1.0));
fragColor = vec4(col, 1.0);
}