diff options
-rw-r--r-- | main.c | 49 | ||||
-rw-r--r-- | pgm.c | 7 | ||||
-rw-r--r-- | pgm.h | 1 | ||||
-rw-r--r-- | ray.c | 273 | ||||
-rw-r--r-- | ray.h | 2 | ||||
-rw-r--r-- | scene.h | 14 |
6 files changed, 63 insertions, 283 deletions
@@ -9,12 +9,12 @@ #include "scene.h" #include "pgm.h" -#define TESTW 1000 -#define TESTH 1000 +#define TESTW 500 +#define TESTH 500 #define PERCENTSTEP (TESTH / 100) -#define WORKERS 1 +#define WORKERS 4 void *worker_func(void *arg); @@ -34,31 +34,9 @@ pthread_mutex_t percentlock; char container[ CONTAINER_SIZE(OBJECTS, MATERIALS, LIGHTS) ]; container_t *cont = (container_t *) container; -settings_t gfx_high = { - .arealight_samples = 64, - .envlight_samples = 64, - .antialias_samples = 2, - .globallight_samples = 32, - .gl_opt_depth = 1, - .depth = 2, -}; - settings_t gfx_low = { - .arealight_samples = 4, - .envlight_samples = 16, - .antialias_samples = 100, - .globallight_samples = 16, - .gl_opt_depth = 1, - .depth = 16, -}; - -settings_t gfx_very = { - .arealight_samples = 8, - .envlight_samples = 8, - .antialias_samples = 2, - .globallight_samples = 2, - .gl_opt_depth = 0, - .depth = 1, + .samples = 10, + .depth = 15, }; // Implement random @@ -98,7 +76,7 @@ int main() material_t *ml = add_material(cont); vector_set(&ml->color, 1, 1, 1); - ml->emissive = 1; + ml->emissive = 8; material_t *m2 = add_material(cont); vector_set(&m2->color, 1, 1, 1); @@ -172,17 +150,18 @@ int main() vector_set(&o->pl.norm, 1, 0, 0); o->m = mplred; - // Used for the light ball + // light ball o = add_object(cont, TYPE_SPHERE); vector_set(&o->sph.center, 0, 7, 0); o->sph.radius = 0.5; o->m = ml; - light_t *l = add_light(cont, TYPE_L_AREA); - //vector_set(&l->point.pos, 0, 5, 0); - l->area = o; + /* + light_t *l = add_light(cont, TYPE_L_POINT); + vector_set(&l->point.pos, 0, 7, 0); color_set(&l->color, 1, 1, 1); - l->radiance = 0.1; + l->radiance = 5; + */ pgm_write_header(stdout, TESTW, TESTH); @@ -243,14 +222,14 @@ void *worker_func(void *arg) { color_t *c = &office->image[ y * TESTW + x ]; //color_t c; seed = x * y; - ray_trace(&cont->space, TESTW - x, TESTH - y, c, &seed); + path_trace(&cont->space, TESTW - x, TESTH - y, c, &seed); color_clamp(c); } if (y % PERCENTSTEP == 0) { // Unlock the thingy pthread_mutex_lock(&percentlock); - fprintf(stderr, "%d%\n", percent++); + fprintf(stderr, "%d%%\n", percent++); pthread_mutex_unlock(&percentlock); } } @@ -2,6 +2,7 @@ #include <stdio.h> #include <stdlib.h> +#include <math.h> #define COLOR_MAX 255 @@ -39,9 +40,15 @@ void color_clamp(color_t *c) int pgm_write_pixel(FILE *fp, color_t *c) { + //color_gamma_correct(c); return fprintf(fp, "%.0lf %.0lf %.0lf\n", c->r * COLOR_MAX, c->g * COLOR_MAX, c->b * COLOR_MAX); } +void color_gamma_correct(color_t *c) { + c->r = sqrt(c->r); + c->g = sqrt(c->g); + c->b = sqrt(c->b); +} color_t *color_set(color_t *c, COORD_T r, COORD_T g, COORD_T b) { c->r = r; @@ -11,6 +11,7 @@ typedef struct { void color_clamp(color_t *c); +void color_gamma_correct(color_t *c); color_t *color_set(color_t *c, COORD_T r, COORD_T g, COORD_T b); color_t *color_scale(color_t *dest, color_t *a, COORD_T b); color_t *color_add(color_t *dest, color_t *a, color_t *b); @@ -9,6 +9,8 @@ #define PI 3.14159265359 +int path_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T scale, void *seed); + int ray_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T scale, void *seed); // https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection @@ -189,103 +191,10 @@ static void contribution_from_pointlight(space_t *s, color_t *dest, object_t *o, reflected_at(o, dest, &light->color, i, point, &l, V, N); } -// Many of these can maybe be put in a context struct -static void contribution_from_arealight(space_t *s, color_t *dest, object_t *o, light_t *light, vector_t *point, vector_t *V, vector_t *N, void *seed) -{ - // This only works with spheres - assert(light->area->type == TYPE_SPHERE); - - // Color to collect temporary results in - color_t c; - color_set(&c, 0, 0, 0); - - ray_t ray; - ray.start = point; - - // Calculate vector from light to point - vector_t l; - vector_sub(&l, point, &light->area->sph.center); - vector_norm(&l); - - // Initialize the transformation stuff - csystem_t cs; - csystem_init(&cs, &l); - - // Do the same monte carlo as with environment but the starting point is the center of the circle. - // And the result is a point on the circle - for (int i = 0; i < s->gfx->arealight_samples; i++) { - // Do the monte carlo random distribution thing from the article - COORD_T r1 = ray_rand(seed); - - // Random direction on halv sphere pointing towards point - vector_t randpoint; - csystem_hemisphere_random(&cs, r1, ray_rand(seed), &randpoint); - csystem_calc_real(&cs, &randpoint, &randpoint); - - // Shift it up to center of circle - vector_add(&randpoint, &randpoint, &light->area->sph.center); - - // Cast a ray towards it, reuse randpoint as direction - vector_sub(&randpoint, &randpoint, point); - COORD_T dist = vector_len(&randpoint); - - vector_t dir; - vector_scale_inv(&dir, &randpoint, dist); - - ray.direction = &dir; - - object_t *obs = ray_cast(s, &ray, NULL, true, dist - ZERO_APROX); - if (obs) { - // We hit something skip it. - continue; - } - - // Add the light contribution - COORD_T i = light->radiance / ( dist * dist); - reflected_at(o, &c, &light->color, i, point, &randpoint, V, N); - - } - - // Device by pdf - color_scale(&c, &c, ((COORD_T) 1 / s->gfx->arealight_samples) * (2 * PI)); - - color_add(dest, dest, &c); - -} - -static void direct_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *eye, vector_t *point, void *seed) -{ - // And vector towards viewer - vector_t V; - vector_sub(&V, eye, point); - - // Normalice it - vector_norm(&V); - - // Loop lights - light_t *light = s->lights; - while (light) { - // Calculate contribution depending on the light type - switch (light->type) { - case TYPE_L_POINT: - contribution_from_pointlight(s, dest, o, light, point, &V, N); - break; - case TYPE_L_AREA: - contribution_from_arealight(s, dest, o, light, point, &V, N, seed); - break; - } - - 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) { - if (s->gfx->envlight_samples == 0) { - return; - } csystem_t cs; csystem_init(&cs, N); @@ -297,7 +206,7 @@ static void env_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vecto color_t acc; color_set(&acc, 0, 0, 0); - for (unsigned i = 0; i < s->gfx->envlight_samples; i++) { + for (unsigned i = 0; i < 0; i++) { COORD_T r1 = ray_rand(seed); // Calculate the random direction vector @@ -323,7 +232,7 @@ static void env_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vecto } // Devide by number of samples and pdf - color_scale(&acc, &acc, ((COORD_T) 1/ s->gfx->envlight_samples) * (2 * PI)); + color_scale(&acc, &acc, ((COORD_T) 1/ 0) * (2 * PI)); // Add to dest color_add(dest, dest, &acc); @@ -331,11 +240,8 @@ static void env_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vecto } // https://www.scratchapixel.com/lessons/3d-basic-rendering/global-illumination-path-tracing/global-illumination-path-tracing-practical-implementation -static void global_light(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *point, unsigned hop, void *seed) +static void light_in(space_t *s, color_t *dest, object_t *o, vector_t *N, vector_t *eye, vector_t *point, unsigned hop, void *seed) { - if (s->gfx->globallight_samples == 0) { - return; - } // Init hemisphere translation csystem_t cs; @@ -345,55 +251,31 @@ static void global_light(space_t *s, color_t *dest, object_t *o, vector_t *N, ve ray_t r; r.start = point; - // Value for accumilating colors - color_t acc; - color_set(&acc, 0, 0, 0); + // Calculate the random direction vector + vector_t randdir; + csystem_hemisphere_random(&cs, ray_rand(seed), ray_rand(seed), &randdir); - // Samples is lowered for every hop - unsigned samples; - if (hop < s->gfx->gl_opt_depth) { - samples = s->gfx->globallight_samples / (hop + 1); - } else { - samples = s->gfx->globallight_samples / (s->gfx->gl_opt_depth + 1); - } + // Convert to world cordinates using the calculated N vectors. + csystem_calc_real(&cs, &randdir, &randdir); - for (unsigned i = 0; i < samples; i++) { - COORD_T r1 = ray_rand(seed); - - // Calculate the random direction vector - vector_t randdir; - csystem_hemisphere_random(&cs, r1, ray_rand(seed), &randdir); + r.direction = &randdir; + COORD_T cl = 2 * vector_dot(&randdir, N) * o->m->defuse; - // Convert to world cordinates using the calculated N vectors. - csystem_calc_real(&cs, &randdir, &randdir); + vector_t V; + vector_sub(&V, eye, point); - // Check the direction for obstacles - r.direction = &randdir; - COORD_T cl = vector_dot(&randdir, N); - - // Only recurse if neccesary - if (cl > 0.01) { - // Cast ray in direction if we have more hops - color_t tmp; - color_set(&tmp, 0, 0, 0); - if (hop < s->gfx->depth) { - ray_trace_recur(s, &tmp, &r, hop+1, r1, seed); - } - - // Calculate Deffuse light - color_scale(&tmp, &tmp, cl * o->m->defuse); - color_add(&acc, &tmp, &acc); + path_trace_recur(s, dest, &r, hop+1, cl, seed); + + light_t *light = s->lights; + while (light) { + if (light->type) { + contribution_from_pointlight(s, dest, o, light, point, &V, N); + light = light->next; } } - - // Devide by number of samples and pdf - color_scale(&acc, &acc, ((COORD_T) 1/ 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) +int path_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T scale, void *seed) { COORD_T dist; @@ -415,123 +297,40 @@ int ray_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T vector_t N; obj_norm_at(o, &N, r.start, ray->direction); - // Check if emissive + // Check if emissive if (o->m->emissive > ZERO_APROX) { color_set(&c, o->m->emissive, o->m->emissive, o->m->emissive); goto exit; } - // 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, seed); - global_light(s, &c, o, &N, r.start, hop, seed); - } - - // Calculate environmental light - if (s->env_enabled) { - env_light(s, &c, o, &N, r.start, seed); - } - - - // Calculate reflection vector - if (hop < 10 && 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; -} - -int path_trace_recur(space_t *s, color_t *dest, ray_t *ray, unsigned hop, COORD_T scale, void *seed) -{ - COORD_T dist; - - object_t *o = ray_cast(s, ray, &dist, false, 0); - if (!o) { + if (hop >= s->gfx->depth) { return 1; - } - - color_t c; - color_set(&c, 0, 0, 0); - - 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, ray->direction); + } - // Check if emissive - if (o->m->emissive > ZERO_APROX) { - printf("Emisive\n"); - color_set(&c, o->m->emissive, o->m->emissive, o->m->emissive); - goto exit; + // Check if we should calculate light + if (o->m->defuse + o->m->specular > ZERO_APROX) { + light_in(s, &c, o, &N, ray->start, r.start, hop, seed); } if (o->m->reflective > ZERO_APROX) { - printf("reflective\n"); vector_scale(r.direction, &N, 2 * vector_dot(ray->direction, &N)); vector_sub(r.direction, ray->direction, r.direction); path_trace_recur(s, &c, &r, hop+1, o->m->reflective, seed); - goto exit; - } - - printf("random\n"); - // Init hemisphere translation - csystem_t cs; - csystem_init(&cs, &N); - - COORD_T r1 = ray_rand(seed); - // Calculate the random direction vector - vector_t randdir; - csystem_hemisphere_random(&cs, r1, ray_rand(seed), &randdir); - - // Convert to world cordinates using the calculated N vectors. - csystem_calc_real(&cs, &randdir, &randdir); - - // Probability of the raydirection - COORD_T p = 1.0/(PI * 2); - - // Calculate the defuse reflection - r.direction = &randdir; - COORD_T cl = vector_dot(&randdir, &N) / p; - - // Cast ray in direction if we have more hops - if (hop < s->gfx->depth) { - path_trace_recur(s, &c, &r, hop+1, r1, seed); } // Scale by own color color_scale_vector(&c, &c, &o->m->color); - // Calculate Deffuse light - color_scale(&c, &c, cl * o->m->defuse); - exit: - color_scale(&c, &c, scale); color_add(dest, &c, dest); + //printf("Adding color "); pgm_write_pixel(stdout, &c); return 0; } -void ray_trace(space_t *s, unsigned int x, unsigned int y, color_t *c, void *seed) +void path_trace(space_t *s, unsigned int x, unsigned int y, color_t *c, void *seed) { // Init return color. Will be accumilated with all the detected light. color_set(c, 0, 0, 0); @@ -544,8 +343,7 @@ void ray_trace(space_t *s, unsigned int x, unsigned int y, color_t *c, void *see r.direction = vector_set(&dir, 0, 0, 0); // Multiple samples for antialias - // TODO better distribution of antialias probes - for (unsigned i = 0; i < s->gfx->antialias_samples; i++) { + for (unsigned i = 0; i < s->gfx->samples; i++) { color_t ctmp; color_set(&ctmp, 0, 0, 0); @@ -556,23 +354,22 @@ void ray_trace(space_t *s, unsigned int x, unsigned int y, color_t *c, void *see viewpoint_ray(&s->view, r.direction, x + r1, y + r2); // Run the recursive ray trace + //printf("New ray x: %f, y: %f\n", x + r1, y + r2); if (path_trace_recur(s, &ctmp, &r, 0, 1, seed)) { - printf("Hit nothing"); // Hit nothing add back color_add(&ctmp, &ctmp, &s->back); } // 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; - printf("i: %d ", i); - vector_print(c); + //printf("Total r: %f, g: %f, b: %f\n", c->r, c->g, c->b); } // Take the median - if (s->gfx->antialias_samples > 1) { + if (s->gfx->samples > 1) { // Same as deviding by samples - color_scale(c, c, 1.0/ (COORD_T) s->gfx->antialias_samples); + color_scale(c, c, 1.0/ (COORD_T) s->gfx->samples); } // Add ambient @@ -20,6 +20,6 @@ COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray, bool skip_dist); COORD_T ray_intersect_plane(plane_t *p, ray_t *ray, bool skip_dist); object_t *ray_cast(space_t *s, ray_t *r, COORD_T *dist_ret, bool chk, COORD_T chk_dist); -void ray_trace(space_t *s, unsigned int x, unsigned int y, color_t *c, void *seed); +void path_trace(space_t *s, unsigned int x, unsigned int y, color_t *c, void *seed); #endif @@ -14,7 +14,10 @@ #define TYPE_L_POINT 1 #define TYPE_L_AREA 2 -#define CONTAINER_SIZE(objs, mats, ligs) (sizeof(container_t) + objs * sizeof(object_t) + ligs * sizeof(light_t)) +#define CONTAINER_SIZE(objs, mats, ligs) (sizeof(container_t) + \ + objs * sizeof(object_t) + \ + mats * sizeof(material_t) + \ + ligs * sizeof(light_t)) typedef struct { vector_t center; @@ -69,18 +72,11 @@ typedef struct light_s{ struct light_s *next; } light_t; -light_t l; - // Graphics settings // Should actually sit in ray.c typedef struct { - unsigned arealight_samples; - unsigned antialias_samples; - unsigned envlight_samples; - unsigned globallight_samples; - + unsigned samples; unsigned depth; - unsigned gl_opt_depth; } settings_t; typedef struct { |