diff --git a/pathtracing/reverse/README.md b/pathtracing/reverse/README.md
new file mode 100644
index 0000000..45a8302
--- /dev/null
+++ b/pathtracing/reverse/README.md
@@ -0,0 +1,7 @@
+# Reverse Path Tracing
+
+
+
+
+
+SDF based ray marching reverse path tracer. Capable of direct and indirect diffuse, specular and refractive ray bounces.
\ No newline at end of file
diff --git a/pathtracing/reverse/buffer-a-glsl b/pathtracing/reverse/buffer-a-glsl
new file mode 100644
index 0000000..b2e8220
--- /dev/null
+++ b/pathtracing/reverse/buffer-a-glsl
@@ -0,0 +1,215 @@
+
+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);
+}
\ No newline at end of file
diff --git a/pathtracing/reverse/common.glsl b/pathtracing/reverse/common.glsl
new file mode 100644
index 0000000..dd1b1ea
--- /dev/null
+++ b/pathtracing/reverse/common.glsl
@@ -0,0 +1,8 @@
+
+float SEED = 0.0;
+
+float random()
+{
+ SEED ++;
+ return fract(cos(SEED) * 2474.0);
+}
\ No newline at end of file
diff --git a/pathtracing/reverse/image.glsl b/pathtracing/reverse/image.glsl
new file mode 100644
index 0000000..e180b4a
--- /dev/null
+++ b/pathtracing/reverse/image.glsl
@@ -0,0 +1,30 @@
+float luminance(vec3 v)
+{
+ return dot(v, vec3(0.2126f, 0.7152f, 0.0722f));
+}
+
+vec3 reinhard_jodie(vec3 v)
+{
+ float l = luminance(v);
+ vec3 tv = v / (1.0f + v);
+ return mix(v / (1.0f + l), tv, tv);
+}
+
+float tentKernel(in vec2 xy, in float r)
+{
+ return (1.0 - abs(xy.x/r)) * (1.0 - abs(xy.y/r)) / (r*r);
+}
+
+float gaussianWeight(in float delta, in float sigma)
+{
+ return exp(-delta*delta/(2.0 * sigma * sigma)) * 2.5 * sigma;
+}
+
+void mainImage(out vec4 fragColor, in vec2 fragCoord)
+{
+ vec2 uv = fragCoord.xy/iResolution.xy;
+
+ vec3 color = texture(iChannel0, uv).rgb;
+
+ fragColor = vec4(reinhard_jodie(color), 1);
+}
\ No newline at end of file
diff --git a/pathtracing/reverse/overview.png b/pathtracing/reverse/overview.png
new file mode 100644
index 0000000..0f5ff72
Binary files /dev/null and b/pathtracing/reverse/overview.png differ
diff --git a/pathtracing/reverse/src/buffer-a-glsl b/pathtracing/reverse/src/buffer-a-glsl
new file mode 100644
index 0000000..b2e8220
--- /dev/null
+++ b/pathtracing/reverse/src/buffer-a-glsl
@@ -0,0 +1,215 @@
+
+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);
+}
\ No newline at end of file
diff --git a/pathtracing/reverse/src/common.glsl b/pathtracing/reverse/src/common.glsl
new file mode 100644
index 0000000..dd1b1ea
--- /dev/null
+++ b/pathtracing/reverse/src/common.glsl
@@ -0,0 +1,8 @@
+
+float SEED = 0.0;
+
+float random()
+{
+ SEED ++;
+ return fract(cos(SEED) * 2474.0);
+}
\ No newline at end of file
diff --git a/pathtracing/reverse/src/image.glsl b/pathtracing/reverse/src/image.glsl
new file mode 100644
index 0000000..e180b4a
--- /dev/null
+++ b/pathtracing/reverse/src/image.glsl
@@ -0,0 +1,30 @@
+float luminance(vec3 v)
+{
+ return dot(v, vec3(0.2126f, 0.7152f, 0.0722f));
+}
+
+vec3 reinhard_jodie(vec3 v)
+{
+ float l = luminance(v);
+ vec3 tv = v / (1.0f + v);
+ return mix(v / (1.0f + l), tv, tv);
+}
+
+float tentKernel(in vec2 xy, in float r)
+{
+ return (1.0 - abs(xy.x/r)) * (1.0 - abs(xy.y/r)) / (r*r);
+}
+
+float gaussianWeight(in float delta, in float sigma)
+{
+ return exp(-delta*delta/(2.0 * sigma * sigma)) * 2.5 * sigma;
+}
+
+void mainImage(out vec4 fragColor, in vec2 fragCoord)
+{
+ vec2 uv = fragCoord.xy/iResolution.xy;
+
+ vec3 color = texture(iChannel0, uv).rgb;
+
+ fragColor = vec4(reinhard_jodie(color), 1);
+}
\ No newline at end of file