Commits

Evan Gates  committed 49b5271

remove plane, add triangle, light intensity by inverse square, multiple
random samples per pixel

  • Participants
  • Parent commits 5247830

Comments (0)

Files changed (1)

File raytrace/raytrace.c

+#include <fcntl.h>
+#include <float.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <linux/fb.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define SWAP(a, b, t) do{ (t) = (a); (a) = (b); (b) = (t); }while(0)
+#define BLACK      (Color){ 0, 0, 0 }
+#define M_PI         3.14159265358979323846
+
+typedef struct {
+    double x, y, z;
+} Point;
+typedef Point Vector;
+typedef Point Color;
+
+typedef struct {
+    Point  origin;
+    Vector direction;
+    double tmin;
+    double tmax;
+} Ray;
+
+typedef struct {
+    Point vertices[3];
+} Triangle;
+
+typedef struct {
+    Point  center;
+    double radius;
+} Sphere;
+
+typedef struct {
+    Point  center;
+    Vector normal;
+    double radius;
+} Disk;
+
+typedef union {
+    Sphere   sphere;
+    Triangle triangle;
+    Disk     disk;
+} Shape;
+
+typedef struct {
+    Shape  shape;
+    Color  color;
+    int    (*intersect)(Shape, Ray* );
+} Object;
+
+typedef struct {
+    Point  position;
+    double intensity;
+} Light;
+
+typedef struct {
+    int xres;
+    int yres;
+    int samples;
+    double fov; // radians
+    double near_plane;
+    double far_plane;
+    Point  position;
+    Vector direction;
+} Camera;
+
+double dot            (Vector a, Vector b) { return a.x * b.x + a.y * b.y + a.z * b.z;                  }
+double magnitude      (Vector a)           { return sqrt(dot(a, a));                                    }
+Vector cross          (Vector a, Vector b) { return (Vector){ a.y * b.z - a.z * b.y,
+                                                              a.z * b.x - a.x * b.z,
+                                                              a.x * b.y - a.y * b.x };                  }
+Vector scalar_multiply(double s, Vector a) { return (Vector){ a.x * s  , a.y * s  , a.z * s   };        }
+Vector add            (Vector a, Vector b) { return (Vector){ a.x + b.x, a.y + b.y, a.z + b.z };        }
+Vector sub            (Vector a, Vector b) { return (Vector){ a.x - b.x, a.y - b.y, a.z - b.z };        }
+Vector normalize      (Vector a) { double m; return (m = magnitude(a)) ? scalar_multiply(1 / m, a) : a; }
+
+double radians(double degrees)
+{
+    return degrees * M_PI / 180;
+}
+
+double clamp(double d)
+{
+    return d < 0 ? 0 : d > 1 ? 1 : d;
+}
+
+void print_color(Color c)
+{
+    printf("%d %d %d ", (int)(clamp(c.x) * 255), (int)(clamp(c.y) * 255), (int)(clamp(c.z) * 255));
+}
+
+int qroots(double a, double b, double c, double *r1, double *r2)
+{
+    double q, d = b * b - 4 * a * c;
+
+    if (d <  0) return 0;
+    if (d == 0) {
+        *r1 = *r2 = -0.5 * b / a;
+        return 1;
+    }
+    q = (b > 0) ? -0.5 * (b + sqrt(d)) : -0.5 * (b - sqrt(d));
+    *r1 = q / a;
+    *r2 = c / q;
+
+    if (*r1 > *r2)
+        SWAP(*r1, *r2, q);
+    return 2;
+}
+
+int intersect_sphere(Shape s, Ray *r)
+{
+    double t0, t1;
+    Vector ro = sub(r->origin, s.sphere.center);
+    double a  = 1; // direction is normalized. if not: a = dot(r->direction, r->direction);
+    double b  = dot(r->direction, ro) * 2;
+    double c  = dot(ro, ro) - s.sphere.radius * s.sphere.radius;
+
+    if (!qroots(a, b, c, &t0, &t1))   return 0;
+    if (t0 < r->tmin) SWAP(t0, t1, a);
+    if (t0 > r->tmax || t0 < r->tmin) return 0;
+    r->tmax = t0;
+    return 1;
+}
+
+int intersect_triangle(Shape s, Ray *r)
+{
+    Vector e1 = sub(s.triangle.vertices[1], s.triangle.vertices[0]);
+    Vector e2 = sub(s.triangle.vertices[2], s.triangle.vertices[0]);
+    Vector tv = sub(r->origin, s.triangle.vertices[0]);
+    Vector pv = cross(r->direction, e2);
+    Vector qv;
+    double d  = dot(e1, pv);
+    double t, u, v;
+
+    if (fabs(d) < DBL_EPSILON) return 0;
+    d  = 1 / d;
+    u  = dot(tv, pv) * d;
+    if (u < 0 || u > 1) return 0;
+    qv = cross(tv, e1);
+    v  = dot(r->direction, qv) * d;
+    if (v < 0 || u + v > 1) return 0;
+    t  = dot(e2, qv) * d;
+    if (t < r->tmin || t > r->tmax) return 0;
+    r->tmax = t;
+    return 1;
+}
+
+int intersect_disk(Shape d, Ray *r)
+{
+    double t = dot(d.disk.normal, r->direction);
+    Vector v = sub(d.disk.center, r->origin);
+
+    if (fabs(t) < DBL_EPSILON) return 0;
+
+    t = dot(v, d.disk.normal) / t;
+    if (t < r->tmin || t > r->tmax) return 0;
+
+    v = sub(add(r->origin, scalar_multiply(t, r->direction)), d.disk.center);
+    if (dot(v, v) > d.disk.radius * d.disk.radius) return 0;
+
+    r->tmax = t;
+    return 1;
+}
+
+Color trace(Ray r, Object *o, Light *l, Color bg)
+{
+    Object *p, *hit = NULL;
+    Light  *i;
+    Color  c = BLACK;
+
+    for (p = o; p->intersect; p++)
+        if (p->intersect(p->shape, &r))
+            hit = p;
+
+    if (!hit) return bg;
+
+    for (i = l; dot(i->position, i->position); i++) {
+        Point  intersect = add(r.origin, scalar_multiply(r.tmax, r.direction));
+        Vector delta     = sub(i->position, intersect);
+        Ray    shadow    = { intersect, normalize(delta), sqrt(DBL_EPSILON), magnitude(delta) };
+
+        for (p = o; p->intersect; p++)
+            if (p->intersect(p->shape, &shadow))
+                break;
+
+        if (p->intersect)
+            continue;
+
+        c = add(c, scalar_multiply(i->intensity / (shadow.tmax * shadow.tmax), hit->color));
+    }
+    return (i - l) ? scalar_multiply(1. / (i - l), c) : c;
+}
+
+Ray primary_ray(Camera c, double x, double y)
+{
+    double aspect_ratio = (double)c.xres / c.yres;
+    double t = tan(c.fov / 2);
+    Vector pixel;
+
+    //pixel.x = (-1 + 2 * (x + 0.5) / c.xres) * t * aspect_ratio;
+    //pixel.y = ( 1 - 2 * (y + 0.5) / c.yres) * t;
+    pixel.x = (-1 + 2 * (x + drand48()) / c.xres) * t * aspect_ratio;
+    pixel.y = ( 1 - 2 * (y + drand48()) / c.yres) * t;
+    pixel.z =  -1;
+    pixel = normalize(pixel);
+
+    //TODO: transform pixel to camera coordinates
+    return (Ray){ c.position, pixel, c.near_plane, c.far_plane };
+}
+
+void render(Camera c, Object *o, Light *l, Color bg)
+{
+    int s, x, y;
+    Color color;
+
+    for (y = 0; y < c.yres; y++) {
+        for (x = 0; x < c.xres; x++) {
+            for (s = 0, color = BLACK; s < c.samples; s++)
+                color = add(color, trace(primary_ray(c, x, y), o, l, bg));
+            print_color(scalar_multiply(1. / c.samples, color));
+        }
+    }
+}
+
+int main(void)
+{
+    Camera c = { 640, 480, 20, radians(80), 1, 100, { 0, 0, 0 }, { 0, 0, 1 } };
+    Object objects[] = {
+        { { .sphere   = { {   0,   0, -10 },                   2                   } }, { 0, 0, 1 }, intersect_sphere   },
+        { { .sphere   = { {   1,   1,  -9 },                   1.5                 } }, { 0, 1, 0 }, intersect_sphere   },
+        { { .disk     = { {   0,   0,   0 },                   {  0,  0,  1 } , 25 } }, { 0, 1, 1 }, intersect_disk     },
+        { { .disk     = { {   0,   0, -20 },                   {  0,  0,  1 } , 25 } }, { 0, 1, 1 }, intersect_disk     },
+        { { .disk     = { { -10,   0,   0 },                   {  1,  0,  0 } , 25 } }, { 1, 0, 0 }, intersect_disk     },
+        { { .disk     = { {  10,   0,   0 },                   { -1,  0,  0 } , 25 } }, { 1, 1, 0 }, intersect_disk     },
+        { { .disk     = { {   0,  10,   0 },                   {  0, -1,  0 } , 25 } }, { 1, 1, 1 }, intersect_disk     },
+        { { .disk     = { {   0, -10,   0 },                   {  0,  1,  0 } , 25 } }, { 1, 1, 1 }, intersect_disk     },
+        { { .disk     = { {  -1,  -1,  -8 }, normalize((Vector){ -1,  0, .5 }), .4 } }, { 1, 0, 1 }, intersect_disk     },
+        { { .triangle = { { { -1, -10, -15 }, { 1, -10, -15 }, { 0, -8, -15 } } }    }, { 1, 0, 0 }, intersect_triangle },
+        { 0 }
+    };
+    Light lights[] = {
+        { { -9,  2, -3 }, 200 },
+        { { -9, -2, -3 }, 100 },
+        { 0 }
+    };
+
+    printf("P3\n%d %d\n%d\n", c.xres, c.yres, 255);
+    render(c, objects, lights, (Color){ 1, 1, 1 });
+
+    return 0;
+}
+