diff options
author | rodri <rgl@antares-labs.eu> | 2020-06-06 10:27:15 +0000 |
---|---|---|
committer | rodri <rgl@antares-labs.eu> | 2020-06-06 10:27:15 +0000 |
commit | 5eaae194e1d1b7bfea43afcb03d3d40c2a75cad2 (patch) | |
tree | 3b03836ec69d460f3e49c39e9da58a2a25db1d64 | |
download | pcardpt-5eaae194e1d1b7bfea43afcb03d3d40c2a75cad2.tar.gz pcardpt-5eaae194e1d1b7bfea43afcb03d3d40c2a75cad2.tar.bz2 pcardpt-5eaae194e1d1b7bfea43afcb03d3d40c2a75cad2.zip |
initial attempt. getting denormalized number errors.
-rw-r--r-- | mkfile | 8 | ||||
-rw-r--r-- | pcardpt.c | 249 | ||||
-rw-r--r-- | readme.md | 4 |
3 files changed, 261 insertions, 0 deletions
@@ -0,0 +1,8 @@ +</$objtype/mkfile + +BIN=/$objtype/bin +TARG=pcardpt +OFILES=\ + pcardpt.$O\ + +</sys/src/cmd/mkone diff --git a/pcardpt.c b/pcardpt.c new file mode 100644 index 0000000..d74fe39 --- /dev/null +++ b/pcardpt.c @@ -0,0 +1,249 @@ +#include <u.h> +#include <libc.h> + +typedef struct { double x, y, z; } Vec; + +/* when V(n) then all n. when V(x,y) then z = 0 */ +Vec V(double x, double y, double z) { return (Vec){x,y,z}; } +Vec vadd(Vec a, Vec b) { return V(a.x+b.x,a.y+b.y,a.z+b.z); } +Vec vmul(Vec v, double s) { return V(v.x*s,v.y*s,v.z*s); } +Vec vmulv(Vec a, Vec b) { return V(a.x*b.x,a.y*b.y,a.z*b.z); } +double vdot(Vec a, Vec b) { return a.x*b.x + a.y*b.y + a.z*b.z; } +Vec vnorm(Vec v) { return vmul(v, 1/sqrt(vdot(v, v))); } + +double +min(double a, double b) +{ + return a < b? a: b; +} + +double +randomVal(void) +{ + return frand(); +} + +/* + * Rectangle CSG equation. Returns minimum signed distance from + * space carved by + * lowerLeft vertex and opposite rectangle vertex upperRight. + */ +double +BoxTest(Vec p, Vec lowerLeft, Vec upperRight) +{ + lowerLeft = vadd(p, vmul(lowerLeft, -1)); + upperRight = vadd(upperRight, vmul(p, -1)); + return -min( + min( + min(lowerLeft.x, upperRight.x), + min(lowerLeft.y, upperRight.y) + ), + min(lowerLeft.z, upperRight.z) + ); +} + +enum { + HIT_NONE, + HIT_LETTER, + HIT_WALL, + HIT_SUN +}; + +/* Sample the world using Signed Distance Fields. */ +double +QueryDatabase(Vec position, int *hitType) +{ + Vec f; + double distance, roomDist, sun; + int i; + char letters[15*4+1] = /* 15 two points lines */ + "5O5_" "5W9W" "5_9_" /* P (without curve) */ + "AOEO" "COC_" "A_E_" /* I */ + "IOQ_" "I_QO" /* X */ + "UOY_" "Y_]O" "WW[W" /* A */ + "aOa_" "aWeW" "a_e_" "cWiO"; /* R (without curve) */ + + distance = 1e9; + /* flattened position (z = 0) */ + f = position; + f.z = 0; + + for(i = 0; i < sizeof letters; i += 4){ + Vec begin, e, o; + + begin = vmul(V(letters[i] - 79,letters[i + 1] - 79,0), 0.5); + e = vadd(vmul(V(letters[i + 2] - 79,letters[i + 3] - 79,0), 0.5), vmul(begin, -1)); + o = vmul(vadd(f, vadd(begin, vmul(e, min(-min(vdot(vadd(begin, vmul(f, -1)), e) / vdot(e, e), 0), 1)))), -1); + distance = min(distance, vdot(e, e)); + } + + /* actual distance */ + distance = sqrt(distance); + + /* Two curves (for P and R in PixaR) with hard-coded locations. */ + Vec curves[] = { + V(-11,6,0), + V(11,6,0) + }; + for(i = 2; i--;){ + Vec o; + + o = vadd(f, vmul(curves[i], -1)); + distance = min(distance, o.x > 0? abs(sqrt(vdot(o, o)) - 2): (o.y += o.y > 0? -2: 2, sqrt(vdot(o, o)))); + } + distance = pow(pow(distance, 8) + pow(position.z, 8), 0.125) - 0.5; + *hitType = HIT_LETTER; + + /* + * min(A,B) = Union with Constructive solid geometry + * -min carves an empty space + */ + roomDist = min( + -min( /* Lower room */ + BoxTest(position, V(-30,-0.5,-30), V(30,18,30)), + /* Upper room */ + BoxTest(position, V(-25,17,-25), V(25,20,25)) + ), + BoxTest(/* Ceiling "planks" spaced 8 units apart. */ + V(fmod(abs(position.x), 8), + position.y, + position.z), + V(1.5,18.5,-25), + V(6.5,20,25) + ) + ); + + if(roomDist < distance){ + distance = roomDist; + *hitType = HIT_WALL; + } + + sun = 19.9 - position.y ; /* Everything above 19.9 is light source. */ + if(sun < distance){ + distance = sun; + *hitType = HIT_SUN; + } + + return distance; +} + +/* Perform signed sphere marching */ +int +RayMarching(Vec origin, Vec direction, Vec *hitPos, Vec *hitNorm) +{ + int hitType, noHitCount; + double d; /* distance from closest object in world. */ + double total_d; + + hitType = HIT_NONE; + noHitCount = 0; + + /* Signed distance marching */ + for (total_d = 0; total_d < 100; total_d += d){ + *hitPos = vadd(origin, vmul(direction, total_d)); + d = QueryDatabase(*hitPos, &hitType); + if(d < 0.01 || ++noHitCount > 99){ + *hitNorm = vnorm(V(QueryDatabase(vadd(*hitPos, V(0.01,0,0)), &noHitCount) - d, + QueryDatabase(vadd(*hitPos, V(0,0.01,0)), &noHitCount) - d, + QueryDatabase(vadd(*hitPos, V(0,0,0.01)), &noHitCount) - d)); + return hitType; + } + } + return 0; +} + +Vec +Trace(Vec origin, Vec direction) +{ + Vec sampledPosition, normal, color, attenuation, lightDirection; + int bounceCount; + double incidence, p, c, s, g, u, v; + + attenuation = V(1,1,1); + lightDirection = vnorm(V(0.6,0.6,1)); /* Directional light */ + + for(bounceCount = 3; bounceCount--;){ + int hitType; + + hitType = RayMarching(origin, direction, &sampledPosition, &normal); + switch(hitType){ + case HIT_LETTER: /* Specular bounce on a letter. No color acc. */ + direction = vadd(direction, vmul(normal, vdot(normal, direction) * -2)); + origin = vadd(sampledPosition, vmul(direction, 0.1)); + attenuation = vmul(attenuation, 0.2); /* Attenuation via distance traveled. */ + break; + case HIT_WALL: + incidence = vdot(normal, lightDirection); + p = 6.283185 * randomVal(); + c = randomVal(); + s = sqrt(1 - c); + g = normal.z < 0? -1: 1; + u = -1/(g + normal.z); + v = normal.x * normal.y * u; + direction = vadd(vadd(vmul(V(v,g + normal.y * normal.y * u,-normal.y), cos(p)*s),vmul(V(1 + g * normal.x * normal.x * u,g * v,-g * normal.x), sin(p)*s)), vmul(normal, sqrt(c))); + origin = vadd(sampledPosition, vmul(direction, 0.1)); + attenuation = vmul(attenuation, 0.2); + if(incidence > 0 && + RayMarching(vadd(sampledPosition, vmul(normal, 0.1)), + lightDirection, + &sampledPosition, + &normal) == HIT_SUN) + color = vadd(color, vmul(vmulv(attenuation, V(500,400,100)), incidence)); + break; + case HIT_SUN: + color = vadd(color, vmulv(attenuation, V(50,80,100))); + break; + default: + goto Out; + } + } +Out: + return color; +} + +void +usage(void) +{ + fprint(2, "usage: %s [-s nsamples]\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + Vec position, goal, left, up; + int w, h, samplesCount, x, y, p; + + w = 960; + h = 540; + samplesCount = 1; + ARGBEGIN{ + case 's': + samplesCount = strtol(EARGF(usage()), nil, 10); + break; + }ARGEND; + + position = V(-22,5,25); + goal = vnorm(vadd(V(-3,4,0), vmul(position, -1))); + left = vmul(vnorm(V(goal.z,0,-goal.x)), (1.0/w)); + + /* Cross-product to get the up vector */ + up = V(goal.y * left.z - goal.z * left.y, + goal.z * left.x - goal.x * left.z, + goal.x * left.y - goal.y * left.x); + + print("P3 %d %d 255\n", w, h); + for(y = h; y--;) + for(x = w; x--;){ + Vec color, o; + + for (p = samplesCount; p--;) + color = vadd(color, Trace(position, vnorm(vadd(vadd(goal, vmul(left, x - w/2 + randomVal())), vmul(up, y - h/2 + randomVal()))))); + + /* Reinhard tone mapping */ + color = vadd(vmul(color, 1.0/samplesCount), V(14.0/241,14.0/241,14.0/241)); + o = vadd(color, V(1,1,1)); + color = vmul(V(color.x/o.x,color.y/o.y,color.z/o.z), 255); + print("%d %d %d\n", (int)color.x, (int)color.y, (int)color.z); + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..a5fed76 --- /dev/null +++ b/readme.md @@ -0,0 +1,4 @@ +# The Postcard Path Tracer + +This is an adaptation of Andrew "aek" Kensler's original path tracer +to plan 9 C. |