From 7641384c1fc9827ac012caa6481ffd35b4369e47 Mon Sep 17 00:00:00 2001 From: Julian T Date: Mon, 23 Mar 2020 18:09:32 +0100 Subject: Runs on arduino, output not testet --- main.c | 128 --------------------- pgm.c | 62 ----------- pgm.ino | 62 +++++++++++ ray.c | 351 ---------------------------------------------------------- ray.ino | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ raytrace.ino | 126 +++++++++++++++++++++ scene.c | 132 ---------------------- scene.ino | 137 +++++++++++++++++++++++ vector.c | 120 -------------------- vector.ino | 120 ++++++++++++++++++++ viewpoint.c | 65 ----------- viewpoint.ino | 58 ++++++++++ 12 files changed, 854 insertions(+), 858 deletions(-) delete mode 100644 main.c delete mode 100644 pgm.c create mode 100644 pgm.ino delete mode 100644 ray.c create mode 100644 ray.ino create mode 100644 raytrace.ino delete mode 100644 scene.c create mode 100644 scene.ino delete mode 100644 vector.c create mode 100644 vector.ino delete mode 100644 viewpoint.c create mode 100644 viewpoint.ino diff --git a/main.c b/main.c deleted file mode 100644 index c27096c..0000000 --- a/main.c +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include - -#include "vector.h" -#include "ray.h" -#include "scene.h" -#include "pgm.h" - -#define TESTW 1000 -#define TESTH 1000 - -char container[ CONTAINER_SIZE(5, 4, 1) ]; - -// Implement random -COORD_T ray_rand(void *seed) -{ - return (COORD_T) rand_r( (int *)seed ) / RAND_MAX; -} - -int main() -{ - - container_t *cont = (container_t *) container; - container_init(cont, 5, 4, 1); - - // Init space_t - space_t *s = container_prepare_space(cont);; - - // Set space options - color_set(&s->ambient, 0.09, 0.09, 0.09); - color_set(&s->back, 0.8, 0.8, 0.8); - color_set(&s->env_color, 0.13, 0.13, 0.13); - s->env_samples = 16; - - // Set viewpoint options - vector_set(&s->view.position, 0, 16, 6); - vector_set(&s->view.target, 0, 0, 6); - s->view.width = TESTW; - s->view.height = TESTH; - - // Create materials - material_t *m = add_material(cont); - vector_set(&m->color, 0.4, 0.4, 1); - m->defuse = 1; - m->specular = 0.0; - m->shine = 80; - m->reflective = 0.0; - - material_t *m3 = add_material(cont); - vector_set(&m3->color, 1, 0.3, 0.1); - m3->defuse = 0.95; - m3->specular = 0.0; - m3->shine = 80; - m3->reflective = 0.05; - - material_t *m2 = add_material(cont); - vector_set(&m2->color, 1, 1, 1); - m2->defuse = 0.0; - m2->specular = 0.5; - m2->shine = 80; - m2->reflective = 1; - - material_t *mpl = add_material(cont); - //vector_set(&mpl.color, 0, 0.396, 0.7019); - vector_set(&mpl->color, 1, 1, 1); - mpl->defuse = 1; - mpl->specular = 0.0; - mpl->shine = 50; - mpl->reflective = 0.0; - - - viewpoint_init(&s->view); - - object_t *o = add_object(cont, TYPE_SPHERE); - vector_set(&o->sph.center, 0, 4, 7); - o->sph.radius = 5; - o->m = m2; - - o = add_object(cont, TYPE_SPHERE); - vector_set(&o->sph.center, 8, 8, 4); - o->sph.radius = 2; - o->m = m3; - - o = add_object(cont, TYPE_SPHERE); - vector_set(&o->sph.center, -10, 9, 5); - o->sph.radius = 3; - o->m = m; - - o = add_object(cont, TYPE_SPHERE); - vector_set(&o->sph.center, -10, -5, 5); - o->sph.radius = 3; - o->m = m; - - o = add_object(cont, TYPE_PLANE); - vector_set(&o->pl.start, 0, 0, 2); - vector_set(&o->pl.norm, 0, 0, 1); - o->m = mpl; - - light_t *l = add_light(cont); - vector_set(&l->pos, 20, 10, 30); - color_set(&l->defuse, 0.3, 0.3, 0.3); - color_set(&l->specular, 0.5, 0.5, 0.5); - - pgm_write_header(stdout, TESTW, TESTH); - - // Height percentage - unsigned percentstep = TESTH / 100; - unsigned percent = 0; - int seed; - - for (int y = TESTH; y; y--) { - for (int x = TESTW; x; x--) { - // Random seed - seed = x * y; - color_t c; - ray_trace(&cont->space, x, y, 2, &c, &seed); - - pgm_write_pixel(stdout, &c); - - } - - if (y % percentstep == 0) { - fprintf(stderr, "%d%\n", percent++); - } - } - -} diff --git a/pgm.c b/pgm.c deleted file mode 100644 index eb0c101..0000000 --- a/pgm.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "pgm.h" - -#include -#include - -#define COLOR_MAX 255 - -int pgm_write_header(FILE *fp, unsigned int w, unsigned int h) -{ - return fprintf(fp, "P3\n%d %d\n%d\n", w, h, COLOR_MAX); -} - -int pgm_write_pixel(FILE *fp, color_t *c) -{ - return fprintf(fp, "%.0lf %.0lf %.0lf\n", c->r * COLOR_MAX, c->g * COLOR_MAX, c->b * COLOR_MAX); -} - -color_t *color_set(color_t *c, COORD_T r, COORD_T g, COORD_T b) -{ - c->r = r; - c->g = g; - c->b = b; - - return c; -} - -color_t *color_add(color_t *dest, color_t *a, color_t *b) -{ - COORD_T tmp = a->r + b->r; - dest->r = tmp > 1 ? 1 : tmp; - - tmp = a->g + b->g; - dest->g = tmp > 1 ? 1 : tmp; - - tmp = a->b + b->b; - dest->b = tmp > 1 ? 1 : tmp; - - return dest; -} - -color_t *color_scale(color_t *dest, color_t *a, COORD_T b) -{ - COORD_T tmp = a->r * b; - dest->r = tmp > 1 ? 1 : tmp; - - tmp = a->g * b; - dest->g = tmp > 1 ? 1 : tmp; - - tmp = a->b * b; - dest->b = tmp > 1 ? 1 : tmp; - - return dest; -} - -color_t *color_scale_vector(color_t *dest, color_t *a, vector_t *v) -{ - dest->r = a->r * v->x; - dest->g = a->g * v->y; - dest->b = a->b * v->z; - - return dest; -} diff --git a/pgm.ino b/pgm.ino new file mode 100644 index 0000000..eb0c101 --- /dev/null +++ b/pgm.ino @@ -0,0 +1,62 @@ +#include "pgm.h" + +#include +#include + +#define COLOR_MAX 255 + +int pgm_write_header(FILE *fp, unsigned int w, unsigned int h) +{ + return fprintf(fp, "P3\n%d %d\n%d\n", w, h, COLOR_MAX); +} + +int pgm_write_pixel(FILE *fp, color_t *c) +{ + return fprintf(fp, "%.0lf %.0lf %.0lf\n", c->r * COLOR_MAX, c->g * COLOR_MAX, c->b * COLOR_MAX); +} + +color_t *color_set(color_t *c, COORD_T r, COORD_T g, COORD_T b) +{ + c->r = r; + c->g = g; + c->b = b; + + return c; +} + +color_t *color_add(color_t *dest, color_t *a, color_t *b) +{ + COORD_T tmp = a->r + b->r; + dest->r = tmp > 1 ? 1 : tmp; + + tmp = a->g + b->g; + dest->g = tmp > 1 ? 1 : tmp; + + tmp = a->b + b->b; + dest->b = tmp > 1 ? 1 : tmp; + + return dest; +} + +color_t *color_scale(color_t *dest, color_t *a, COORD_T b) +{ + COORD_T tmp = a->r * b; + dest->r = tmp > 1 ? 1 : tmp; + + tmp = a->g * b; + dest->g = tmp > 1 ? 1 : tmp; + + tmp = a->b * b; + dest->b = tmp > 1 ? 1 : tmp; + + return dest; +} + +color_t *color_scale_vector(color_t *dest, color_t *a, vector_t *v) +{ + dest->r = a->r * v->x; + dest->g = a->g * v->y; + dest->b = a->b * v->z; + + return dest; +} diff --git a/ray.c b/ray.c deleted file mode 100644 index a41866c..0000000 --- a/ray.c +++ /dev/null @@ -1,351 +0,0 @@ -#include -#include -#include -#include "vector.h" - -#include "ray.h" - -#define PI 3.14159265359 - -// https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection -// http://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/ -// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection -COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray, bool skip_dist) -{ - // Vector between vector start and center of circle - vector_t oc; - vector_sub(&oc, ray->start, &s->center); - - // Solve quadratic function - // TODO Not sure if this step i neccesary because dir is unit - COORD_T a = vector_dot(ray->direction, ray->direction); - COORD_T b = 2 * vector_dot(&oc, ray->direction); - COORD_T c = vector_dot(&oc, &oc) - s->radius * s->radius; - - COORD_T d = b * b - 4 * a * c; - - // no intersection - if (d < 0) { - return -1; - } - if (skip_dist) { - return 1; - } - - // Else take the closest intersection, reuse d - COORD_T q = (b > 0) ? - -0.5 * (b + sqrt(d)) : - -0.5 * (b - sqrt(d)); - - COORD_T x1 = q / a; - COORD_T x0 = c / q; - - // Take the correct result. If one is zero take the other. - if (x0 <= ZERO_APROX) { - if (x1 <= 0) { - return -1; - } - - x0 = x1; - } - - // If point is on sphere it will be zero close to zero - if (x0 < ZERO_APROX) { - return -1; - } - - return x0; -} - -// Requires that vectors are normalized -// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection -COORD_T ray_intersect_plane(plane_t *p, ray_t *ray, bool skip_dist) -{ - // If zero ray is parralel to plane - COORD_T nr = vector_dot(&p->norm, ray->direction); - - // Take care of rounding errors - if (nr < ZERO_APROX && nr > -ZERO_APROX) { - return -1; - } - if (skip_dist) { - return 1; - } - - // Calculate distance - vector_t tmp; - vector_copy(&tmp, &p->start); - vector_sub(&tmp, &tmp, ray->start); - - COORD_T t = vector_dot(&tmp, &p->norm) / nr; - return t; -} - -COORD_T ray_intersect(object_t *o, ray_t *ray, bool skip_dist) -{ - switch (o->type) { - case TYPE_PLANE: - return ray_intersect_plane(&o->pl, ray, skip_dist); - case TYPE_SPHERE: - return ray_intersect_sphere(&o->sph, ray, skip_dist); - default: - printf("Unknown object type %d\n", o->type); - return -1; - } -} - -// If chk is true, will return at first hit less than chk_dist -object_t *ray_cast(space_t *s, ray_t *r, COORD_T *dist_ret, bool chk, COORD_T chk_dist) -{ - object_t *o = s->objects; - - object_t *smallest = NULL; - COORD_T dist = 0; - - while (o) { - COORD_T d = ray_intersect(o, r, false); - - if (d > ZERO_APROX) { - if (chk && ( chk_dist > d || chk_dist == 0)) { - if (dist_ret) { - *dist_ret = d; - } - return o; - } - if (d < dist || smallest == NULL) { - dist = d; - smallest = o; - } - } - - o = o->next; - } - - if (chk) { - return NULL; - } - - if (dist_ret) { - *dist_ret = dist; - } - return smallest; -} - -static void direct_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *eye, vector_t *point) -{ - ray_t r; - r.start = point; - - // And vector towards viewer - vector_t V; - vector_sub(&V, eye, point); - - // Normalice it - vector_scale_inv(&V, &V, vector_len(&V)); - - // Cast light rays - light_t *light = s->lights; - while (light) { - vector_t l; - - // Calculate distance to light - vector_sub(&l, &light->pos, point); - COORD_T d = vector_len(&l); - - // Normalice - vector_scale_inv(&l, &l, vector_len(&l)); - - // Find obstacles - r.direction = &l; - object_t *obs = ray_cast(s, &r, NULL, true, d); - if (obs) { - light = light->next; - continue; - } - - // Calculate Deffuse part - color_t tmp; - COORD_T cl = vector_dot(&l, N); - if (cl > 0) { - color_scale(&tmp, &light->defuse, cl * o->m->defuse); - color_add(dest, &tmp, dest); - } - - // calculate specular part. TODO implement blinn-phong - // Calculate R_m - vector_t R; - vector_scale(&R, N, 2 * vector_dot(&l, N)); - vector_sub(&R, &R, &l); - - // Add it to the light - cl = 1 * vector_dot(&R, &V); - if (cl > 0) { - cl = pow(cl, o->m->shine); - color_scale(&tmp, &light->specular, cl * o->m->specular); - color_add(dest, &tmp, dest); - } - - light = light->next; - } -} - -// Calculates the global illumination. Pretty slow -// https://www.scratchapixel.com/lessons/3d-basic-rendering/global-illumination-path-tracing/global-illumination-path-tracing-practical-implementation -static void env_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *point, void *seed) -{ - // Create new coordinate system where N is up. To do this we need two more vectors for the other axises. - // Create the 2. by setting x or y to 0 - vector_t Nt; - if (N->x > N->y) { - vector_set(&Nt, N->z, 0, -N->x); - } else { - vector_set(&Nt, 0, -N->z, N->y); - } - // Normalice - vector_scale_inv(&Nt, &Nt, vector_len(&Nt)); - - // Create the 3. axis by taking the cross of the other - vector_t Nb; - vector_cross(&Nb, N, &Nt); - - // Prepare ray - ray_t r; - r.start = point; - - // Tmp color for accumilating colors - color_t acc; - color_set(&acc, 0, 0, 0); - - for (unsigned i = 0; i < s->env_samples; i++) { - // Do the monte carlo random distribution thing from the article - COORD_T r1 = ray_rand(seed); - COORD_T r2 = ray_rand(seed); - - COORD_T sinTheta = sqrt(1 - r1 * r1); - COORD_T phi = 2 * PI * r2; - - // Calculate the random direction vector - vector_t randdir; - vector_set(&randdir, sinTheta * cos(phi), r1, sinTheta * sin(phi)); - - // Convert to world cordinates using the calculated N vectors. - vector_set(&randdir, randdir.x * Nb.x + randdir.y * N->x + randdir.z * Nt.x, - randdir.x * Nb.y + randdir.y * N->y + randdir.z * Nt.y, - randdir.x * Nb.z + randdir.y * N->z + randdir.z * Nt.z); - - // Check the direction for obstacles - r.direction = &randdir; - object_t *obs = ray_cast(s, &r, NULL, true, 0); - if (obs) { - // If we hit something don't add the light - continue; - } - - // Add the light together after scaling it - color_t tmp; - color_scale(&tmp, &s->env_color, r1); - - acc.r += tmp.r; acc.g += tmp.g; acc.b += tmp.b; - } - - // Devide by number of samples and pdf - color_scale(&acc, &acc, ((COORD_T) 1/ s->env_samples) * (2 * PI)); - - // Add to dest - color_add(dest, dest, &acc); - -} - -int ray_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T scale, void *seed) -{ - COORD_T dist; - color_t c; - color_set(&c, 0, 0, 0); - - object_t *o = ray_cast(s, ray, &dist, false, 0); - if (!o) { - color_add(&c, &c, &s->back); - goto exit; - } - - vector_t rdir, rstart; - ray_t r = {start: &rstart, direction: &rdir}; - - vector_scale(r.start, ray->direction, dist); - vector_add(r.start, r.start, ray->start); - - // Calculate normal vector - vector_t N; - obj_norm_at(o, &N, r.start); - - // Check if we should calculate light - if (o->m->defuse + o->m->specular > ZERO_APROX) { - // Add all light hitting o at r.start to c - direct_light(s, &c, o, &N, ray->start, r.start); - } - - // Calculate environmental light - if (s->env_samples) { - env_light(s, &c, o, &N, r.start, seed); - } - - // Calculate reflection vector - if (hop < 2 && o->m->reflective > ZERO_APROX) { - vector_scale(r.direction, &N, 2 * vector_dot(ray->direction, &N)); - vector_sub(r.direction, ray->direction, r.direction); - - ray_trace_recur(s, &c, &r, hop+1, o->m->reflective, seed); - } - - - // Scale by the objects own color. - color_scale_vector(&c, &c, &o->m->color); - -exit: - // Add it to the result - color_scale(&c, &c, scale); - color_add(dest, dest, &c); - - return 0; -} - -void ray_trace(space_t *s, unsigned int x, unsigned int y, unsigned samples, color_t *c, void *seed) -{ - // Init return color. Will be accumilated with all the detected light. - color_set(c, 0, 0, 0); - - // Setup primary ray - ray_t r; - r.start = &s->view.position; - - vector_t dir; - r.direction = vector_set(&dir, 0, 0, 0); - - // Multiple samples for antialias - // TODO better distribution of antialias probes - for (int i = 0; i < samples; i++) { - color_t ctmp; - color_set(&ctmp, 0, 0, 0); - //memset(&ctmp, 0, sizeof(color_t)); - - // Multiple samples inside same pixel - COORD_T tmp = (COORD_T) i/ (COORD_T) samples; - viewpoint_ray(&s->view, r.direction, x + tmp, y + tmp); - - // Run the recursive ray trace - ray_trace_recur(s, &ctmp, &r, 0, 1, seed); - - // Color_add will not go above 1. In this case we don't want that. - c->r += ctmp.r; c->g += ctmp.g; c->b += ctmp.b; - - } - - // Take the median - if (samples > 1) { - // Same as deviding by samples - color_scale(c, c, 1.0/ (COORD_T) samples); - } - - // Add ambient - color_add(c, c, &s->ambient); -} diff --git a/ray.ino b/ray.ino new file mode 100644 index 0000000..4ec0799 --- /dev/null +++ b/ray.ino @@ -0,0 +1,351 @@ +#include +#include +#include +#include "vector.h" + +#include "ray.h" + +#define PI 3.14159265359 + +// https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection +// http://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/ +// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection +COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray, bool skip_dist) +{ + // Vector between vector start and center of circle + vector_t oc; + vector_sub(&oc, ray->start, &s->center); + + // Solve quadratic function + // TODO Not sure if this step i neccesary because dir is unit + COORD_T a = vector_dot(ray->direction, ray->direction); + COORD_T b = 2 * vector_dot(&oc, ray->direction); + COORD_T c = vector_dot(&oc, &oc) - s->radius * s->radius; + + COORD_T d = b * b - 4 * a * c; + + // no intersection + if (d < 0) { + return -1; + } + if (skip_dist) { + return 1; + } + + // Else take the closest intersection, reuse d + COORD_T q = (b > 0) ? + -0.5 * (b + sqrt(d)) : + -0.5 * (b - sqrt(d)); + + COORD_T x1 = q / a; + COORD_T x0 = c / q; + + // Take the correct result. If one is zero take the other. + if (x0 <= ZERO_APROX) { + if (x1 <= 0) { + return -1; + } + + x0 = x1; + } + + // If point is on sphere it will be zero close to zero + if (x0 < ZERO_APROX) { + return -1; + } + + return x0; +} + +// Requires that vectors are normalized +// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection +COORD_T ray_intersect_plane(plane_t *p, ray_t *ray, bool skip_dist) +{ + // If zero ray is parralel to plane + COORD_T nr = vector_dot(&p->norm, ray->direction); + + // Take care of rounding errors + if (nr < ZERO_APROX && nr > -ZERO_APROX) { + return -1; + } + if (skip_dist) { + return 1; + } + + // Calculate distance + vector_t tmp; + vector_copy(&tmp, &p->start); + vector_sub(&tmp, &tmp, ray->start); + + COORD_T t = vector_dot(&tmp, &p->norm) / nr; + return t; +} + +COORD_T ray_intersect(object_t *o, ray_t *ray, bool skip_dist) +{ + switch (o->type) { + case TYPE_PLANE: + return ray_intersect_plane(&o->pl, ray, skip_dist); + case TYPE_SPHERE: + return ray_intersect_sphere(&o->sph, ray, skip_dist); + default: + //printf("Unknown object type %d\n", o->type); + return -1; + } +} + +// If chk is true, will return at first hit less than chk_dist +object_t *ray_cast(space_t *s, ray_t *r, COORD_T *dist_ret, bool chk, COORD_T chk_dist) +{ + object_t *o = s->objects; + + object_t *smallest = NULL; + COORD_T dist = 0; + + while (o) { + COORD_T d = ray_intersect(o, r, false); + + if (d > ZERO_APROX) { + if (chk && ( chk_dist > d || chk_dist == 0)) { + if (dist_ret) { + *dist_ret = d; + } + return o; + } + if (d < dist || smallest == NULL) { + dist = d; + smallest = o; + } + } + + o = o->next; + } + + if (chk) { + return NULL; + } + + if (dist_ret) { + *dist_ret = dist; + } + return smallest; +} + +static void direct_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *eye, vector_t *point) +{ + ray_t r; + r.start = point; + + // And vector towards viewer + vector_t V; + vector_sub(&V, eye, point); + + // Normalice it + vector_scale_inv(&V, &V, vector_len(&V)); + + // Cast light rays + light_t *light = s->lights; + while (light) { + vector_t l; + + // Calculate distance to light + vector_sub(&l, &light->pos, point); + COORD_T d = vector_len(&l); + + // Normalice + vector_scale_inv(&l, &l, vector_len(&l)); + + // Find obstacles + r.direction = &l; + object_t *obs = ray_cast(s, &r, NULL, true, d); + if (obs) { + light = light->next; + continue; + } + + // Calculate Deffuse part + color_t tmp; + COORD_T cl = vector_dot(&l, N); + if (cl > 0) { + color_scale(&tmp, &light->defuse, cl * o->m->defuse); + color_add(dest, &tmp, dest); + } + + // calculate specular part. TODO implement blinn-phong + // Calculate R_m + vector_t R; + vector_scale(&R, N, 2 * vector_dot(&l, N)); + vector_sub(&R, &R, &l); + + // Add it to the light + cl = 1 * vector_dot(&R, &V); + if (cl > 0) { + cl = pow(cl, o->m->shine); + color_scale(&tmp, &light->specular, cl * o->m->specular); + color_add(dest, &tmp, dest); + } + + light = light->next; + } +} + +// Calculates the global illumination. Pretty slow +// https://www.scratchapixel.com/lessons/3d-basic-rendering/global-illumination-path-tracing/global-illumination-path-tracing-practical-implementation +static void env_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *point, void *seed) +{ + // Create new coordinate system where N is up. To do this we need two more vectors for the other axises. + // Create the 2. by setting x or y to 0 + vector_t Nt; + if (N->x > N->y) { + vector_set(&Nt, N->z, 0, -N->x); + } else { + vector_set(&Nt, 0, -N->z, N->y); + } + // Normalice + vector_scale_inv(&Nt, &Nt, vector_len(&Nt)); + + // Create the 3. axis by taking the cross of the other + vector_t Nb; + vector_cross(&Nb, N, &Nt); + + // Prepare ray + ray_t r; + r.start = point; + + // Tmp color for accumilating colors + color_t acc; + color_set(&acc, 0, 0, 0); + + for (unsigned i = 0; i < s->env_samples; i++) { + // Do the monte carlo random distribution thing from the article + COORD_T r1 = ray_rand(seed); + COORD_T r2 = ray_rand(seed); + + COORD_T sinTheta = sqrt(1 - r1 * r1); + COORD_T phi = 2 * PI * r2; + + // Calculate the random direction vector + vector_t randdir; + vector_set(&randdir, sinTheta * cos(phi), r1, sinTheta * sin(phi)); + + // Convert to world cordinates using the calculated N vectors. + vector_set(&randdir, randdir.x * Nb.x + randdir.y * N->x + randdir.z * Nt.x, + randdir.x * Nb.y + randdir.y * N->y + randdir.z * Nt.y, + randdir.x * Nb.z + randdir.y * N->z + randdir.z * Nt.z); + + // Check the direction for obstacles + r.direction = &randdir; + object_t *obs = ray_cast(s, &r, NULL, true, 0); + if (obs) { + // If we hit something don't add the light + continue; + } + + // Add the light together after scaling it + color_t tmp; + color_scale(&tmp, &s->env_color, r1); + + acc.r += tmp.r; acc.g += tmp.g; acc.b += tmp.b; + } + + // Devide by number of samples and pdf + color_scale(&acc, &acc, ((COORD_T) 1/ s->env_samples) * (2 * PI)); + + // Add to dest + color_add(dest, dest, &acc); + +} + +int ray_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T scale, void *seed) +{ + COORD_T dist; + color_t c; + color_set(&c, 0, 0, 0); + + vector_t rdir, rstart; + ray_t r = {start: &rstart, direction: &rdir}; + + object_t *o = ray_cast(s, ray, &dist, false, 0); + if (!o) { + color_add(&c, &c, &s->back); + goto exit; + } + + vector_scale(r.start, ray->direction, dist); + vector_add(r.start, r.start, ray->start); + + // Calculate normal vector + vector_t N; + obj_norm_at(o, &N, r.start); + + // Check if we should calculate light + if (o->m->defuse + o->m->specular > ZERO_APROX) { + // Add all light hitting o at r.start to c + direct_light(s, &c, o, &N, ray->start, r.start); + } + + // Calculate environmental light + if (s->env_samples) { + env_light(s, &c, o, &N, r.start, seed); + } + + // Calculate reflection vector + if (hop < 2 && o->m->reflective > ZERO_APROX) { + vector_scale(r.direction, &N, 2 * vector_dot(ray->direction, &N)); + vector_sub(r.direction, ray->direction, r.direction); + + ray_trace_recur(s, &c, &r, hop+1, o->m->reflective, seed); + } + + + // Scale by the objects own color. + color_scale_vector(&c, &c, &o->m->color); + +exit: + // Add it to the result + color_scale(&c, &c, scale); + color_add(dest, dest, &c); + + return 0; +} + +void ray_trace(space_t *s, unsigned int x, unsigned int y, unsigned samples, color_t *c, void *seed) +{ + // Init return color. Will be accumilated with all the detected light. + color_set(c, 0, 0, 0); + + // Setup primary ray + ray_t r; + r.start = &s->view.position; + + vector_t dir; + r.direction = vector_set(&dir, 0, 0, 0); + + // Multiple samples for antialias + // TODO better distribution of antialias probes + for (unsigned i = 0; i < samples; i++) { + color_t ctmp; + color_set(&ctmp, 0, 0, 0); + //memset(&ctmp, 0, sizeof(color_t)); + + // Multiple samples inside same pixel + COORD_T tmp = (COORD_T) i/ (COORD_T) samples; + viewpoint_ray(&s->view, r.direction, x + tmp, y + tmp); + + // Run the recursive ray trace + ray_trace_recur(s, &ctmp, &r, 0, 1, seed); + + // Color_add will not go above 1. In this case we don't want that. + c->r += ctmp.r; c->g += ctmp.g; c->b += ctmp.b; + + } + + // Take the median + if (samples > 1) { + // Same as deviding by samples + color_scale(c, c, 1.0/ (COORD_T) samples); + } + + // Add ambient + color_add(c, c, &s->ambient); +} diff --git a/raytrace.ino b/raytrace.ino new file mode 100644 index 0000000..8301758 --- /dev/null +++ b/raytrace.ino @@ -0,0 +1,126 @@ +#include +#include +#include + +#include "vector.h" +#include "ray.h" +#include "scene.h" +#include "pgm.h" + +#define TESTW 320 +#define TESTH 240 + +char container[ CONTAINER_SIZE(5, 4, 1) ]; + +// Implement random +COORD_T ray_rand(void *seed) +{ + return (COORD_T) random(10000) / 10000; +} + +void setup() +{ + Serial.begin(115200); + Serial.println("Starting"); + + container_t *cont = (container_t *) container; + container_init(cont, 5, 4, 1); + + // Init space_t + space_t *s = container_prepare_space(cont); + Serial.println("Starting"); + + // Set space options + color_set(&s->ambient, 0.09, 0.09, 0.09); + color_set(&s->back, 0.8, 0.8, 0.8); + color_set(&s->env_color, 0.13, 0.13, 0.13); + s->env_samples = 256; + + // Set viewpoint options + vector_set(&s->view.position, 0, 16, 6); + vector_set(&s->view.target, 0, 0, 6); + s->view.width = TESTW; + s->view.height = TESTH; + Serial.println("Starting"); + + // Create materials + material_t *m = add_material(cont); + vector_set(&m->color, 0.4, 0.4, 1); + m->defuse = 1; + m->specular = 0.0; + m->shine = 80; + m->reflective = 0.0; + + material_t *m3 = add_material(cont); + vector_set(&m3->color, 1, 0.3, 0.1); + m3->defuse = 0.95; + m3->specular = 0.0; + m3->shine = 80; + m3->reflective = 0.05; + Serial.println("Starting"); + + material_t *m2 = add_material(cont); + vector_set(&m2->color, 1, 1, 1); + m2->defuse = 0.0; + m2->specular = 0.5; + m2->shine = 80; + m2->reflective = 1; + Serial.println("Starting"); + + material_t *mpl = add_material(cont); + //vector_set(&mpl.color, 0, 0.396, 0.7019); + vector_set(&mpl->color, 1, 1, 1); + mpl->defuse = 1; + mpl->specular = 0.0; + mpl->shine = 50; + mpl->reflective = 0.0; + + + viewpoint_init(&s->view); + + object_t *o = add_object(cont, TYPE_SPHERE); + vector_set(&o->sph.center, 0, 4, 7); + o->sph.radius = 5; + o->m = m2; + + o = add_object(cont, TYPE_SPHERE); + vector_set(&o->sph.center, 8, 8, 4); + o->sph.radius = 2; + o->m = m3; + Serial.println("Starting"); + + o = add_object(cont, TYPE_SPHERE); + vector_set(&o->sph.center, -10, 9, 5); + o->sph.radius = 3; + o->m = m; + + o = add_object(cont, TYPE_SPHERE); + vector_set(&o->sph.center, -10, -5, 5); + o->sph.radius = 3; + o->m = m; + + o = add_object(cont, TYPE_PLANE); + vector_set(&o->pl.start, 0, 0, 2); + vector_set(&o->pl.norm, 0, 0, 1); + o->m = mpl; + + light_t *l = add_light(cont); + vector_set(&l->pos, 20, 10, 30); + color_set(&l->defuse, 0.3, 0.3, 0.3); + color_set(&l->specular, 0.5, 0.5, 0.5); + + pgm_write_header(stdout, TESTW, TESTH); + + for (int y = TESTH; y; y--) { + for (int x = TESTW; x; x--) { + // Random seed + color_t c; + ray_trace(&cont->space, x, y, 2, &c, NULL); + } + Serial.print("row "); Serial.println(y); + } + +} + +void loop() { +} diff --git a/scene.c b/scene.c deleted file mode 100644 index 6221fe8..0000000 --- a/scene.c +++ /dev/null @@ -1,132 +0,0 @@ -#include "scene.h" - -#include -#include - -static inline void link_object(space_t *s, object_t *o) -{ - if (s) { - o->next = s->objects; - s->objects = o; - } else { - o->next = 0; - } -} - -container_t *container_init(container_t *c, unsigned objs, unsigned mats, unsigned ligs) -{ - c->obj_index = c->mat_index = c->lig_index = 0; - - c->obj_cap = objs; - c->mat_cap = mats; - c->lig_cap = ligs; - - return c; -} - -space_t *container_prepare_space(container_t *c) -{ - memset(&c->space, 0, sizeof(space_t)); -} - -// Finds the next empty object_t space -// Return NULL if full -static inline object_t *container_obj_space(container_t *cont) -{ - if (cont->obj_index >= cont->obj_cap) { - fprintf(stderr, "Could not create object, because container is full\n"); - return NULL; - } - - // Find a nice spot in the content blob - return &((object_t *) cont->content)[cont->obj_index++]; -} - -static inline material_t *container_mat_space(container_t *cont) -{ - if (cont->mat_index >= cont->mat_cap) { - fprintf(stderr, "Could not create material, because container is full\n"); - return NULL; - } - - // Find a nice spot in the content blob. - // Remember to jump over objects - return &((material_t *) ( - cont->content + - sizeof(object_t) * cont->obj_cap - ))[cont->mat_index++]; -} - -static inline light_t *container_lig_space(container_t *cont) -{ - if (cont->lig_index >= cont->lig_cap) { - fprintf(stderr, "Could not create light, because container is full\n"); - return NULL; - } - - // Find a nice spot in the content blob. - // Remember to jump over objects and materials - return &((light_t *) ( - cont->content + - sizeof(object_t) * cont->obj_cap + - sizeof(material_t) * cont->mat_cap - ))[cont->lig_index++]; -} - -object_t *add_object(container_t *cont, unsigned type) -{ - - object_t *o = container_obj_space(cont); - if (!o) { - return NULL; - } - - // Fill out the data - o->type = type; - - // Link to the linked list - link_object(&cont->space, o); - - return o; -} - -light_t *add_light(container_t *cont) -{ - light_t *o = container_lig_space(cont); - if (!o) { - return NULL; - } - - space_t *s = &cont->space; - if (s) { - o->next = s->lights; - s->lights = o; - } else { - o->next = NULL; - } - - return o; -} - -// It's alot easier to set the stuff outside of this function -material_t *add_material(container_t *cont) -{ - material_t *m = container_mat_space(cont); - - return m; -} - -void obj_norm_at(object_t *o, vector_t *dest, vector_t *point) -{ - switch(o->type) { - case TYPE_SPHERE: - vector_sub(dest, point, &o->sph.center); - vector_scale_inv(dest, dest, vector_len(dest)); - break; - case TYPE_PLANE: - vector_copy(dest, &o->pl.norm); - break; - } - - return; -} diff --git a/scene.ino b/scene.ino new file mode 100644 index 0000000..77dbdc1 --- /dev/null +++ b/scene.ino @@ -0,0 +1,137 @@ +#include "scene.h" + +#include +#include + +static inline void link_object(space_t *s, object_t *o) +{ + if (s) { + o->next = s->objects; + s->objects = o; + } else { + o->next = 0; + } +} + +container_t *container_init(container_t *c, unsigned objs, unsigned mats, unsigned ligs) +{ + c->obj_index = c->mat_index = c->lig_index = 0; + + c->obj_cap = objs; + c->mat_cap = mats; + c->lig_cap = ligs; + + return c; +} + +space_t *container_prepare_space(container_t *c) +{ + //memset(&c->space, 0, sizeof(space_t)); + for (int i = 0; i < sizeof(space_t); i++) { + *(uint8_t *)&c->space = 0; + } + + return &c->space; +} + +// Finds the next empty object_t space +// Return NULL if full +static inline object_t *container_obj_space(container_t *cont) +{ + if (cont->obj_index >= cont->obj_cap) { + //fprintf(stderr, "Could not create object, because container is full\n"); + return NULL; + } + + // Find a nice spot in the content blob + return &((object_t *) cont->content)[cont->obj_index++]; +} + +static inline material_t *container_mat_space(container_t *cont) +{ + if (cont->mat_index >= cont->mat_cap) { + //fprintf(stderr, "Could not create material, because container is full\n"); + return NULL; + } + + // Find a nice spot in the content blob. + // Remember to jump over objects + return &((material_t *) ( + cont->content + + sizeof(object_t) * cont->obj_cap + ))[cont->mat_index++]; +} + +static inline light_t *container_lig_space(container_t *cont) +{ + if (cont->lig_index >= cont->lig_cap) { + //fprintf(stderr, "Could not create light, because container is full\n"); + return NULL; + } + + // Find a nice spot in the content blob. + // Remember to jump over objects and materials + return &((light_t *) ( + cont->content + + sizeof(object_t) * cont->obj_cap + + sizeof(material_t) * cont->mat_cap + ))[cont->lig_index++]; +} + +object_t *add_object(container_t *cont, unsigned type) +{ + + object_t *o = container_obj_space(cont); + if (!o) { + return NULL; + } + + // Fill out the data + o->type = type; + + // Link to the linked list + link_object(&cont->space, o); + + return o; +} + +light_t *add_light(container_t *cont) +{ + light_t *o = container_lig_space(cont); + if (!o) { + return NULL; + } + + space_t *s = &cont->space; + if (s) { + o->next = s->lights; + s->lights = o; + } else { + o->next = NULL; + } + + return o; +} + +// It's alot easier to set the stuff outside of this function +material_t *add_material(container_t *cont) +{ + material_t *m = container_mat_space(cont); + + return m; +} + +void obj_norm_at(object_t *o, vector_t *dest, vector_t *point) +{ + switch(o->type) { + case TYPE_SPHERE: + vector_sub(dest, point, &o->sph.center); + vector_scale_inv(dest, dest, vector_len(dest)); + break; + case TYPE_PLANE: + vector_copy(dest, &o->pl.norm); + break; + } + + return; +} diff --git a/vector.c b/vector.c deleted file mode 100644 index 0bcd1dc..0000000 --- a/vector.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "vector.h" - -#include -#include -#include -#include -#include - -static inline vector_t *vector_exists(vector_t *v) -{ - assert(v); - - return v; -} - -// Overwrites stuff in p. If p is null a new vector is created -vector_t *vector_set(vector_t *p, COORD_T x, COORD_T y, COORD_T z) -{ - p = vector_exists(p); - - p->x = x; - p->y = y; - p->z = z; - - return p; -} - -// Can also be used to create a vector by leaving v NULL -vector_t *vector_copy(vector_t *dest, vector_t *src) -{ - dest = vector_exists(dest); - - // If v exist copy it over - if (src) { - memcpy(dest, src, sizeof(vector_t)); - } - - return dest; -} - -COORD_T vector_len(vector_t *v) -{ - return sqrt( v->x * v->x + v->y * v->y + v->z * v->z ); -} - -vector_t *vector_add(vector_t *dest, vector_t *a, vector_t *b) -{ - dest = vector_exists(dest); - - dest->x = a->x + b->x; - dest->y = a->y + b->y; - dest->z = a->z + b->z; - - return dest; -} - -vector_t *vector_sub(vector_t *dest, vector_t *a, vector_t *b) -{ - dest = vector_exists(dest); - - dest->x = a->x - b->x; - dest->y = a->y - b->y; - dest->z = a->z - b->z; - - return dest; -} - -vector_t *vector_mult(vector_t *dest, vector_t *a, vector_t *b) -{ - dest = vector_exists(dest); - - dest->x = a->x * b->x; - dest->y = a->y * b->y; - dest->z = a->z * b->z; - - return dest; -} - -vector_t *vector_scale(vector_t *dest, vector_t *a, COORD_T b) -{ - dest = vector_exists(dest); - - dest->x = a->x * b; - dest->y = a->y * b; - dest->z = a->z * b; - - return dest; -} - -vector_t *vector_scale_inv(vector_t *dest, vector_t *a, COORD_T b) -{ - dest = vector_exists(dest); - - dest->x = a->x / b; - dest->y = a->y / b; - dest->z = a->z / b; - - return dest; -} - -COORD_T vector_dot(vector_t *a, vector_t *b) -{ - return a->x * b->x + a->y * b->y + a->z * b->z; -} - -vector_t *vector_cross(vector_t *dest, vector_t *a, vector_t *b) -{ - dest = vector_exists(dest); - - dest->x = a->y * b->z - a->z * b->y; - dest->y = a->z * b->x - a->x * b->z; - dest->z = a->x * b->y - a->y * b->x; - - return dest; -} - -void vector_print(vector_t *v) -{ - printf("[ %f, %f, %f ]\n", v->x, v->y, v->z); -} diff --git a/vector.ino b/vector.ino new file mode 100644 index 0000000..d56cf85 --- /dev/null +++ b/vector.ino @@ -0,0 +1,120 @@ +#include "vector.h" + +#include +#include +#include +#include +#include + +static inline vector_t *vector_exists(vector_t *v) +{ + assert(v); + + return v; +} + +// Overwrites stuff in p. If p is null a new vector is created +vector_t *vector_set(vector_t *p, COORD_T x, COORD_T y, COORD_T z) +{ + p = vector_exists(p); + + p->x = x; + p->y = y; + p->z = z; + + return p; +} + +// Can also be used to create a vector by leaving v NULL +vector_t *vector_copy(vector_t *dest, vector_t *src) +{ + dest = vector_exists(dest); + + // If v exist copy it over + if (src) { + memcpy(dest, src, sizeof(vector_t)); + } + + return dest; +} + +COORD_T vector_len(vector_t *v) +{ + return sqrt( v->x * v->x + v->y * v->y + v->z * v->z ); +} + +vector_t *vector_add(vector_t *dest, vector_t *a, vector_t *b) +{ + dest = vector_exists(dest); + + dest->x = a->x + b->x; + dest->y = a->y + b->y; + dest->z = a->z + b->z; + + return dest; +} + +vector_t *vector_sub(vector_t *dest, vector_t *a, vector_t *b) +{ + dest = vector_exists(dest); + + dest->x = a->x - b->x; + dest->y = a->y - b->y; + dest->z = a->z - b->z; + + return dest; +} + +vector_t *vector_mult(vector_t *dest, vector_t *a, vector_t *b) +{ + dest = vector_exists(dest); + + dest->x = a->x * b->x; + dest->y = a->y * b->y; + dest->z = a->z * b->z; + + return dest; +} + +vector_t *vector_scale(vector_t *dest, vector_t *a, COORD_T b) +{ + dest = vector_exists(dest); + + dest->x = a->x * b; + dest->y = a->y * b; + dest->z = a->z * b; + + return dest; +} + +vector_t *vector_scale_inv(vector_t *dest, vector_t *a, COORD_T b) +{ + dest = vector_exists(dest); + + dest->x = a->x / b; + dest->y = a->y / b; + dest->z = a->z / b; + + return dest; +} + +COORD_T vector_dot(vector_t *a, vector_t *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + +vector_t *vector_cross(vector_t *dest, vector_t *a, vector_t *b) +{ + dest = vector_exists(dest); + + dest->x = a->y * b->z - a->z * b->y; + dest->y = a->z * b->x - a->x * b->z; + dest->z = a->x * b->y - a->y * b->x; + + return dest; +} + +void vector_print(vector_t *v) +{ + Serial.println("Not implemented"); +} diff --git a/viewpoint.c b/viewpoint.c deleted file mode 100644 index e8d2a1e..0000000 --- a/viewpoint.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "viewpoint.h" - -#include -#include -#include - -static const vector_t up = {0, 0, 1}; - -void viewpoint_init(viewpoint_t *view) -{ - // Vector from E to T - vector_t t; - vector_sub(&t, &view->target, &view->position); - - // Calculate orthogonal vectors to E - vector_t b, v; - vector_cross(&b, (vector_t *)&up, &t); - - // Convert to unit-vectors - vector_scale_inv(&t, &t, vector_len(&t)); - vector_scale_inv(&b, &b, vector_len(&b)); - - vector_cross(&v, &t, &b); - - // Calculate size of viewplane - COORD_T gx = tanf( FOV / 2 ); - COORD_T gy = gx * ((COORD_T)view->height / view->width); - - // Calculate viewplane vectors - vector_scale(&view->qx, &b, ( 2 * gx ) / (view->width - 1)); - vector_scale(&view->qy, &v, ( 2 * gy ) / (view->height - 1)); - - // Viewplane starting point - vector_copy(&view->blc, &t); - // This will thrash b and v - vector_sub(&view->blc, &view->blc, vector_scale(&b, &b, gx)); - vector_sub(&view->blc, &view->blc, vector_scale(&v, &v, gy)); - - // Debug print - /* - printf("Calculated the following viewpoint stuff\n"); - printf("qx"); vector_print(&view->qx); - printf("qy"); vector_print(&view->qy); - printf("blc"); vector_print(&view->blc); - */ -} - -// Calculate ray for viewport w, h -// https://en.wikipedia.org/wiki/Ray_tracing_(graphics) -void viewpoint_ray(viewpoint_t *view, vector_t *r, COORD_T x, COORD_T y) -{ - // Calculate ray vector - vector_copy(r, &view->blc); - - vector_t t; - vector_scale(&t, &view->qx, x); - vector_add(r, r, &t); - - vector_scale(&t, &view->qy, y); - vector_add(r, r, &t); - - // Calculate unit vector - vector_scale_inv(r, r, vector_len(r)); -} - diff --git a/viewpoint.ino b/viewpoint.ino new file mode 100644 index 0000000..62ff08a --- /dev/null +++ b/viewpoint.ino @@ -0,0 +1,58 @@ +#include "viewpoint.h" + +#include +#include +#include + +static const vector_t up = {0, 0, 1}; + +void viewpoint_init(viewpoint_t *view) +{ + // Vector from E to T + vector_t t; + vector_sub(&t, &view->target, &view->position); + + // Calculate orthogonal vectors to E + vector_t b, v; + vector_cross(&b, (vector_t *)&up, &t); + + // Convert to unit-vectors + vector_scale_inv(&t, &t, vector_len(&t)); + vector_scale_inv(&b, &b, vector_len(&b)); + + vector_cross(&v, &t, &b); + + // Calculate size of viewplane + COORD_T gx = tanf( FOV / 2 ); + COORD_T gy = gx * ((COORD_T)view->height / view->width); + + // Calculate viewplane vectors + vector_scale(&view->qx, &b, ( 2 * gx ) / (view->width - 1)); + vector_scale(&view->qy, &v, ( 2 * gy ) / (view->height - 1)); + + // Viewplane starting point + vector_copy(&view->blc, &t); + // This will thrash b and v + vector_sub(&view->blc, &view->blc, vector_scale(&b, &b, gx)); + vector_sub(&view->blc, &view->blc, vector_scale(&v, &v, gy)); + +} + +// Calculate ray for viewport w, h +// https://en.wikipedia.org/wiki/Ray_tracing_(graphics) +void viewpoint_ray(viewpoint_t *view, vector_t *r, COORD_T x, COORD_T y) +{ + // Calculate ray vector + vector_copy(r, &view->blc); + + vector_t t; + vector_scale(&t, &view->qx, x); + vector_add(r, r, &t); + + vector_scale(&t, &view->qy, y); + vector_add(r, r, &t); + + // Calculate unit vector + vector_scale_inv(r, r, vector_len(r)); +} + -- cgit v1.2.3