From 0f6df6bc1eb9ded4a9a1a9e2c7ad50646cfb5aac Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 5 Dec 2011 19:40:30 -0500 Subject: Rename raytrace.{c,h} to ray_trace.{c,h}. --- HACKING | 2 +- libdimension/Makefile.am | 4 +- libdimension/dimension.h | 2 +- libdimension/dimension/ray_trace.h | 37 +++ libdimension/dimension/raytrace.h | 37 --- libdimension/ray_trace.c | 503 +++++++++++++++++++++++++++++++++++++ libdimension/raytrace.c | 503 ------------------------------------- 7 files changed, 544 insertions(+), 544 deletions(-) create mode 100644 libdimension/dimension/ray_trace.h delete mode 100644 libdimension/dimension/raytrace.h create mode 100644 libdimension/ray_trace.c delete mode 100644 libdimension/raytrace.c diff --git a/HACKING b/HACKING index 58ca3f2..df8321c 100644 --- a/HACKING +++ b/HACKING @@ -6,7 +6,7 @@ Library (LGPLv3): API: libdimension/dimension.h, libdimension/dimension/*.h (Implementations for libdimension/dimension/$file.h are generally at libdimension/$file.c) -Ray tracing algorithm: libdimension/raytrace.c +Ray tracing algorithm: libdimension/ray_trace.c Bounding hierarchy: libdimension/prtree.{c,h} Library internals: libdimension/dimension-internal.h and its #includes Tests: libdimension/tests/*.{c,cpp} diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am index c75241e..e99fe3c 100644 --- a/libdimension/Makefile.am +++ b/libdimension/Makefile.am @@ -54,7 +54,7 @@ nobase_include_HEADERS = dimension.h \ dimension/pigments.h \ dimension/png.h \ dimension/polynomial.h \ - dimension/raytrace.h \ + dimension/ray_trace.h \ dimension/refcount.h \ dimension/scene.h \ dimension/texture.h \ @@ -102,7 +102,7 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \ polynomial.c \ prtree.c \ prtree.h \ - raytrace.c \ + ray_trace.c \ refcount-internal.h \ reflection.c \ rgba16.c \ diff --git a/libdimension/dimension.h b/libdimension/dimension.h index 375c2ec..e427b17 100644 --- a/libdimension/dimension.h +++ b/libdimension/dimension.h @@ -105,7 +105,7 @@ typedef void dmnsn_free_fn(void *ptr); #include #include #include -#include +#include #ifdef __cplusplus } diff --git a/libdimension/dimension/ray_trace.h b/libdimension/dimension/ray_trace.h new file mode 100644 index 0000000..82fb759 --- /dev/null +++ b/libdimension/dimension/ray_trace.h @@ -0,0 +1,37 @@ +/************************************************************************* + * Copyright (C) 2009-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * Ray-trace a scene. + */ + +/** + * Render a scene by ray tracing. + * @param[in,out] scene The scene to render. + */ +void dmnsn_ray_trace(dmnsn_scene *scene); + +/** + * Render a scene in the background. + * @param[in,out] scene The scene to render. + * @return A \p dmnsn_future object. + */ +dmnsn_future *dmnsn_ray_trace_async(dmnsn_scene *scene); diff --git a/libdimension/dimension/raytrace.h b/libdimension/dimension/raytrace.h deleted file mode 100644 index 82fb759..0000000 --- a/libdimension/dimension/raytrace.h +++ /dev/null @@ -1,37 +0,0 @@ -/************************************************************************* - * Copyright (C) 2009-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * Ray-trace a scene. - */ - -/** - * Render a scene by ray tracing. - * @param[in,out] scene The scene to render. - */ -void dmnsn_ray_trace(dmnsn_scene *scene); - -/** - * Render a scene in the background. - * @param[in,out] scene The scene to render. - * @return A \p dmnsn_future object. - */ -dmnsn_future *dmnsn_ray_trace_async(dmnsn_scene *scene); diff --git a/libdimension/ray_trace.c b/libdimension/ray_trace.c new file mode 100644 index 0000000..dd84a26 --- /dev/null +++ b/libdimension/ray_trace.c @@ -0,0 +1,503 @@ +/************************************************************************* + * Copyright (C) 2010-2011 Tavian Barnes * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * . * + *************************************************************************/ + +/** + * @file + * The ray-tracing algorithm. + */ + +#include "dimension-internal.h" +#include + +/* + * Boilerplate for multithreading + */ + +/** Payload type for passing arguments to worker threads. */ +typedef struct { + dmnsn_future *future; + dmnsn_scene *scene; + dmnsn_prtree *prtree; +} dmnsn_ray_trace_payload; + +/* Ray-trace a scene */ +void +dmnsn_ray_trace(dmnsn_scene *scene) +{ + dmnsn_future *future = dmnsn_ray_trace_async(scene); + if (dmnsn_future_join(future) != 0) { + dmnsn_error("Error occured while ray-tracing."); + } +} + +/** Background thread callback. */ +static int dmnsn_ray_trace_scene_thread(void *ptr); + +/* Ray-trace a scene in the background */ +dmnsn_future * +dmnsn_ray_trace_async(dmnsn_scene *scene) +{ + dmnsn_future *future = dmnsn_new_future(); + + dmnsn_ray_trace_payload *payload = + dmnsn_malloc(sizeof(dmnsn_ray_trace_payload)); + payload->future = future; + payload->scene = scene; + + dmnsn_new_thread(future, dmnsn_ray_trace_scene_thread, payload); + + return future; +} + +/** Worker thread callback. */ +static int dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, + unsigned int nthreads); + +/* Thread callback -- set up the multithreaded engine */ +static int +dmnsn_ray_trace_scene_thread(void *ptr) +{ + dmnsn_ray_trace_payload *payload = ptr; + + /* Pre-calculate bounding box transformations, etc. */ + dmnsn_scene_initialize(payload->scene); + + /* Time the bounding tree construction */ + dmnsn_timer_start(&payload->scene->bounding_timer); + payload->prtree = dmnsn_new_prtree(payload->scene->objects); + dmnsn_timer_stop(&payload->scene->bounding_timer); + + /* Set up the future object */ + dmnsn_future_set_total(payload->future, payload->scene->canvas->height); + + /* Time the render itself */ + dmnsn_timer_start(&payload->scene->render_timer); + int ret = dmnsn_execute_concurrently(dmnsn_ray_trace_scene_concurrent, + payload, payload->scene->nthreads); + dmnsn_timer_stop(&payload->scene->render_timer); + + dmnsn_delete_prtree(payload->prtree); + dmnsn_free(payload); + + return ret; +} + +/* + * Ray-tracing algorithm + */ + +/** The current state of the ray-tracing engine. */ +typedef struct dmnsn_rtstate { + const struct dmnsn_rtstate *parent; + + const dmnsn_scene *scene; + const dmnsn_intersection *intersection; + const dmnsn_texture *texture; + const dmnsn_interior *interior; + const dmnsn_prtree *prtree; + unsigned int reclevel; + + dmnsn_vector r; + dmnsn_vector pigment_r; + dmnsn_vector viewer; + dmnsn_vector reflected; + dmnsn_vector light; + + dmnsn_color pigment; + dmnsn_color diffuse; + dmnsn_color additional; + + double ior; + + dmnsn_color adc_value; +} dmnsn_rtstate; + +/** Compute a ray-tracing state from an intersection. */ +static inline void +dmnsn_rtstate_initialize(dmnsn_rtstate *state, + const dmnsn_intersection *intersection) +{ + state->intersection = intersection; + state->texture = intersection->object->texture; + state->interior = intersection->object->interior; + + state->r = dmnsn_line_point(intersection->ray, intersection->t); + state->pigment_r = dmnsn_transform_point( + intersection->object->pigment_trans, + state->r + ); + state->viewer = dmnsn_vector_normalized( + dmnsn_vector_negate(intersection->ray.n) + ); + state->reflected = dmnsn_vector_sub( + dmnsn_vector_mul( + 2*dmnsn_vector_dot(state->viewer, intersection->normal), + intersection->normal), + state->viewer + ); + + state->pigment = dmnsn_black; + state->diffuse = dmnsn_black; + state->additional = dmnsn_black; +} + +/** Main helper for dmnsn_ray_trace_scene_impl - shoot a ray. */ +static dmnsn_color dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray); + +/* Actually ray-trace a scene */ +static int +dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, + unsigned int nthreads) +{ + const dmnsn_ray_trace_payload *payload = ptr; + dmnsn_future *future = payload->future; + dmnsn_scene *scene = payload->scene; + dmnsn_prtree *prtree = payload->prtree; + + dmnsn_rtstate state = { + .parent = NULL, + .scene = scene, + .prtree = prtree, + }; + + /* Iterate through each pixel */ + for (size_t y = thread; y < scene->canvas->height; y += nthreads) { + for (size_t x = 0; x < scene->canvas->width; ++x) { + /* Get the ray corresponding to the (x,y)'th pixel */ + dmnsn_line ray = dmnsn_camera_ray( + scene->camera, + ((double)(x + scene->region_x))/(scene->outer_width - 1), + ((double)(y + scene->region_y))/(scene->outer_height - 1) + ); + + /* Shoot a ray */ + state.reclevel = scene->reclimit; + state.ior = 1.0; + state.adc_value = dmnsn_white; + dmnsn_color color = dmnsn_ray_shoot(&state, ray); + + dmnsn_canvas_set_pixel(scene->canvas, x, y, color); + } + + dmnsn_future_increment(future); + } + + return 0; +} + +/** Calculate the background color. */ +static dmnsn_color +dmnsn_trace_background(const dmnsn_rtstate *state, dmnsn_line ray) +{ + dmnsn_pigment *background = state->scene->background; + if (state->scene->quality & DMNSN_RENDER_PIGMENT) { + return dmnsn_pigment_evaluate(background, dmnsn_vector_normalized(ray.n)); + } else { + return background->quick_color; + } +} + +/** Calculate the base pigment at the intersection. */ +static void +dmnsn_trace_pigment(dmnsn_rtstate *state) +{ + dmnsn_pigment *pigment = state->texture->pigment; + if (state->scene->quality & DMNSN_RENDER_PIGMENT) { + state->pigment = dmnsn_pigment_evaluate(pigment, state->pigment_r); + } else { + state->pigment = pigment->quick_color; + } + + state->diffuse = state->pigment; +} + +/** Get the color of a light ray at an intersection point. */ +static bool +dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light, + dmnsn_color *color) +{ + /** @todo: Start at the light source */ + dmnsn_line shadow_ray = dmnsn_new_line( + state->r, + light->direction_fn(light, state->r) + ); + state->light = dmnsn_vector_normalized(shadow_ray.n); + /* Add epsilon to avoid hitting ourselves with the shadow ray */ + shadow_ray = dmnsn_line_add_epsilon(shadow_ray); + + /* Check if we're casting a shadow on ourself */ + if (dmnsn_vector_dot(shadow_ray.n, state->intersection->normal) + * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0) + { + return false; + } + + *color = light->illumination_fn(light, state->r); + + /* Test for shadow ray intersections */ + unsigned int reclevel = state->reclevel; + while (reclevel-- > 0 + && dmnsn_color_intensity(*color) >= state->scene->adc_bailout) + { + dmnsn_intersection shadow_caster; + bool shadow_was_cast = dmnsn_prtree_intersection(state->prtree, shadow_ray, + &shadow_caster, false); + + if (!shadow_was_cast || !light->shadow_fn(light, shadow_caster.t)) { + return true; + } + + /* Handle transparency */ + if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { + dmnsn_rtstate shadow_state = *state; + dmnsn_rtstate_initialize(&shadow_state, &shadow_caster); + dmnsn_trace_pigment(&shadow_state); + + if (shadow_state.pigment.trans >= dmnsn_epsilon) { + /* Reflect the light */ + const dmnsn_reflection *reflection = + shadow_state.texture->finish.reflection; + if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) { + dmnsn_color reflected = reflection->reflection_fn( + reflection, *color, shadow_state.pigment, shadow_state.reflected, + shadow_state.intersection->normal + ); + *color = dmnsn_color_sub(*color, reflected); + } + + /* Filter the light */ + *color = dmnsn_filter_light(*color, shadow_state.pigment); + shadow_ray.x0 = dmnsn_line_point(shadow_ray, shadow_caster.t); + shadow_ray.n = light->direction_fn(light, shadow_ray.x0); + shadow_ray = dmnsn_line_add_epsilon(shadow_ray); + continue; + } + } + + break; + } + + return false; +} + +/** Handle light, shadow, and shading. */ +static void +dmnsn_trace_lighting(dmnsn_rtstate *state) +{ + /* The ambient color */ + state->diffuse = dmnsn_black; + + const dmnsn_finish *finish = &state->texture->finish; + if (finish->ambient) { + state->diffuse = + finish->ambient->ambient_fn(finish->ambient, state->pigment); + } + + /* Iterate over each light */ + DMNSN_ARRAY_FOREACH (dmnsn_light **, light, state->scene->lights) { + dmnsn_color light_color; + if (dmnsn_trace_light_ray(state, *light, &light_color)) { + if (state->scene->quality & DMNSN_RENDER_FINISH) { + /* Get this light's specular contribution to the object */ + dmnsn_color specular = dmnsn_black; + if (finish->specular) { + specular = finish->specular->specular_fn( + finish->specular, light_color, state->pigment, state->light, + state->intersection->normal, state->viewer + ); + light_color = dmnsn_color_sub(light_color, specular); + } + + /* Reflect the light */ + const dmnsn_reflection *reflection = state->texture->finish.reflection; + if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) { + dmnsn_color reflected = reflection->reflection_fn( + reflection, light_color, state->pigment, state->reflected, + state->intersection->normal + ); + light_color = dmnsn_color_sub(light_color, reflected); + } + + /* Get this light's diffuse contribution to the object */ + dmnsn_color diffuse = dmnsn_black; + if (finish->diffuse) { + diffuse = finish->diffuse->diffuse_fn( + finish->diffuse, light_color, state->pigment, state->light, + state->intersection->normal + ); + } + + state->diffuse = dmnsn_color_add(diffuse, state->diffuse); + state->additional = dmnsn_color_add(specular, state->additional); + } else { + state->diffuse = state->pigment; + state->diffuse.trans = 0.0; + state->diffuse.filter = 0.0; + } + } + } +} + +/** Trace a reflected ray. */ +static dmnsn_color +dmnsn_trace_reflection(const dmnsn_rtstate *state) +{ + dmnsn_color reflected = dmnsn_black; + + const dmnsn_reflection *reflection = state->texture->finish.reflection; + if (reflection) { + dmnsn_line refl_ray = dmnsn_new_line(state->r, state->reflected); + refl_ray = dmnsn_line_add_epsilon(refl_ray); + + dmnsn_rtstate recursive_state = *state; + + /* Calculate ADC value */ + recursive_state.adc_value = reflection->reflection_fn( + reflection, state->adc_value, state->pigment, state->reflected, + state->intersection->normal + ); + + /* Shoot the reflected ray */ + dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray); + reflected = reflection->reflection_fn( + reflection, rec, state->pigment, state->reflected, + state->intersection->normal + ); + reflected.trans = 0.0; + reflected.filter = 0.0; + } + + return reflected; +} + +/** Handle transparency - must be called last to work correctly. */ +static void +dmnsn_trace_transparency(dmnsn_rtstate *state) +{ + if (state->pigment.trans >= dmnsn_epsilon) { + dmnsn_line trans_ray = dmnsn_new_line(state->r, state->intersection->ray.n); + trans_ray = dmnsn_line_add_epsilon(trans_ray); + + dmnsn_vector r = dmnsn_vector_normalized(trans_ray.n); + dmnsn_vector n = state->intersection->normal; + + dmnsn_rtstate recursive_state = *state; + + /* Calculate new refractive index */ + if (dmnsn_vector_dot(r, n) < 0.0) { + /* We are entering an object */ + recursive_state.ior = state->interior->ior; + recursive_state.parent = state; + } else { + /* We are leaving an object */ + recursive_state.ior = state->parent ? state->parent->ior : 1.0; + recursive_state.parent = state->parent ? state->parent->parent : NULL; + } + + /* Calculate transmitted ray direction */ + double iorr = state->ior/recursive_state.ior; /* ior ratio */ + double c1 = -dmnsn_vector_dot(r, n); + double c2 = 1.0 - iorr*iorr*(1.0 - c1*c1); + if (c2 <= 0.0) { + /* Total internal reflection */ + return; + } + c2 = sqrt(c2); + if (c1 >= 0.0) { + trans_ray.n = dmnsn_vector_add( + dmnsn_vector_mul(iorr, r), + dmnsn_vector_mul(iorr*c1 - c2, n) + ); + } else { + trans_ray.n = dmnsn_vector_add( + dmnsn_vector_mul(iorr, r), + dmnsn_vector_mul(iorr*c1 + c2, n) + ); + } + + /* Calculate ADC value */ + recursive_state.adc_value = + dmnsn_filter_light(state->adc_value, state->pigment); + + /* Shoot the transmitted ray */ + dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray); + dmnsn_color filtered = dmnsn_filter_light(rec, state->pigment); + + /* Conserve energy */ + const dmnsn_reflection *reflection = state->texture->finish.reflection; + if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) { + dmnsn_color reflected = reflection->reflection_fn( + reflection, filtered, state->pigment, state->reflected, + state->intersection->normal + ); + filtered = dmnsn_color_sub(filtered, reflected); + } + + state->diffuse.filter = state->pigment.filter; + state->diffuse.trans = state->pigment.trans; + state->diffuse = dmnsn_apply_transparency(filtered, state->diffuse); + } +} + +/* Shoot a ray, and calculate the color */ +static dmnsn_color +dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray) +{ + if (state->reclevel == 0 + || dmnsn_color_intensity(state->adc_value) < state->scene->adc_bailout) + { + return dmnsn_black; + } + + --state->reclevel; + + dmnsn_intersection intersection; + bool reset = state->reclevel == state->scene->reclimit - 1; + if (dmnsn_prtree_intersection(state->prtree, ray, &intersection, reset)) { + /* Found an intersection */ + dmnsn_rtstate_initialize(state, &intersection); + + /* Pigment */ + dmnsn_trace_pigment(state); + + /* Finishes and shadows */ + if (state->scene->quality & DMNSN_RENDER_LIGHTS) { + dmnsn_trace_lighting(state); + } + + /* Reflection */ + if (state->scene->quality & DMNSN_RENDER_REFLECTION) { + state->additional = dmnsn_color_add( + dmnsn_trace_reflection(state), + state->additional + ); + } + + /* Transparency */ + if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { + dmnsn_trace_transparency(state); + } + + return dmnsn_color_add(state->diffuse, state->additional); + } else { + /* No intersection, return the background color */ + return dmnsn_trace_background(state, ray); + } +} diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c deleted file mode 100644 index dd84a26..0000000 --- a/libdimension/raytrace.c +++ /dev/null @@ -1,503 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2011 Tavian Barnes * - * * - * This file is part of The Dimension Library. * - * * - * The Dimension Library is free software; you can redistribute it and/ * - * or modify it under the terms of the GNU Lesser General Public License * - * as published by the Free Software Foundation; either version 3 of the * - * License, or (at your option) any later version. * - * * - * The Dimension Library is distributed in the hope that it will be * - * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * Lesser General Public License for more details. * - * * - * You should have received a copy of the GNU Lesser General Public * - * License along with this program. If not, see * - * . * - *************************************************************************/ - -/** - * @file - * The ray-tracing algorithm. - */ - -#include "dimension-internal.h" -#include - -/* - * Boilerplate for multithreading - */ - -/** Payload type for passing arguments to worker threads. */ -typedef struct { - dmnsn_future *future; - dmnsn_scene *scene; - dmnsn_prtree *prtree; -} dmnsn_ray_trace_payload; - -/* Ray-trace a scene */ -void -dmnsn_ray_trace(dmnsn_scene *scene) -{ - dmnsn_future *future = dmnsn_ray_trace_async(scene); - if (dmnsn_future_join(future) != 0) { - dmnsn_error("Error occured while ray-tracing."); - } -} - -/** Background thread callback. */ -static int dmnsn_ray_trace_scene_thread(void *ptr); - -/* Ray-trace a scene in the background */ -dmnsn_future * -dmnsn_ray_trace_async(dmnsn_scene *scene) -{ - dmnsn_future *future = dmnsn_new_future(); - - dmnsn_ray_trace_payload *payload = - dmnsn_malloc(sizeof(dmnsn_ray_trace_payload)); - payload->future = future; - payload->scene = scene; - - dmnsn_new_thread(future, dmnsn_ray_trace_scene_thread, payload); - - return future; -} - -/** Worker thread callback. */ -static int dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, - unsigned int nthreads); - -/* Thread callback -- set up the multithreaded engine */ -static int -dmnsn_ray_trace_scene_thread(void *ptr) -{ - dmnsn_ray_trace_payload *payload = ptr; - - /* Pre-calculate bounding box transformations, etc. */ - dmnsn_scene_initialize(payload->scene); - - /* Time the bounding tree construction */ - dmnsn_timer_start(&payload->scene->bounding_timer); - payload->prtree = dmnsn_new_prtree(payload->scene->objects); - dmnsn_timer_stop(&payload->scene->bounding_timer); - - /* Set up the future object */ - dmnsn_future_set_total(payload->future, payload->scene->canvas->height); - - /* Time the render itself */ - dmnsn_timer_start(&payload->scene->render_timer); - int ret = dmnsn_execute_concurrently(dmnsn_ray_trace_scene_concurrent, - payload, payload->scene->nthreads); - dmnsn_timer_stop(&payload->scene->render_timer); - - dmnsn_delete_prtree(payload->prtree); - dmnsn_free(payload); - - return ret; -} - -/* - * Ray-tracing algorithm - */ - -/** The current state of the ray-tracing engine. */ -typedef struct dmnsn_rtstate { - const struct dmnsn_rtstate *parent; - - const dmnsn_scene *scene; - const dmnsn_intersection *intersection; - const dmnsn_texture *texture; - const dmnsn_interior *interior; - const dmnsn_prtree *prtree; - unsigned int reclevel; - - dmnsn_vector r; - dmnsn_vector pigment_r; - dmnsn_vector viewer; - dmnsn_vector reflected; - dmnsn_vector light; - - dmnsn_color pigment; - dmnsn_color diffuse; - dmnsn_color additional; - - double ior; - - dmnsn_color adc_value; -} dmnsn_rtstate; - -/** Compute a ray-tracing state from an intersection. */ -static inline void -dmnsn_rtstate_initialize(dmnsn_rtstate *state, - const dmnsn_intersection *intersection) -{ - state->intersection = intersection; - state->texture = intersection->object->texture; - state->interior = intersection->object->interior; - - state->r = dmnsn_line_point(intersection->ray, intersection->t); - state->pigment_r = dmnsn_transform_point( - intersection->object->pigment_trans, - state->r - ); - state->viewer = dmnsn_vector_normalized( - dmnsn_vector_negate(intersection->ray.n) - ); - state->reflected = dmnsn_vector_sub( - dmnsn_vector_mul( - 2*dmnsn_vector_dot(state->viewer, intersection->normal), - intersection->normal), - state->viewer - ); - - state->pigment = dmnsn_black; - state->diffuse = dmnsn_black; - state->additional = dmnsn_black; -} - -/** Main helper for dmnsn_ray_trace_scene_impl - shoot a ray. */ -static dmnsn_color dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray); - -/* Actually ray-trace a scene */ -static int -dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, - unsigned int nthreads) -{ - const dmnsn_ray_trace_payload *payload = ptr; - dmnsn_future *future = payload->future; - dmnsn_scene *scene = payload->scene; - dmnsn_prtree *prtree = payload->prtree; - - dmnsn_rtstate state = { - .parent = NULL, - .scene = scene, - .prtree = prtree, - }; - - /* Iterate through each pixel */ - for (size_t y = thread; y < scene->canvas->height; y += nthreads) { - for (size_t x = 0; x < scene->canvas->width; ++x) { - /* Get the ray corresponding to the (x,y)'th pixel */ - dmnsn_line ray = dmnsn_camera_ray( - scene->camera, - ((double)(x + scene->region_x))/(scene->outer_width - 1), - ((double)(y + scene->region_y))/(scene->outer_height - 1) - ); - - /* Shoot a ray */ - state.reclevel = scene->reclimit; - state.ior = 1.0; - state.adc_value = dmnsn_white; - dmnsn_color color = dmnsn_ray_shoot(&state, ray); - - dmnsn_canvas_set_pixel(scene->canvas, x, y, color); - } - - dmnsn_future_increment(future); - } - - return 0; -} - -/** Calculate the background color. */ -static dmnsn_color -dmnsn_trace_background(const dmnsn_rtstate *state, dmnsn_line ray) -{ - dmnsn_pigment *background = state->scene->background; - if (state->scene->quality & DMNSN_RENDER_PIGMENT) { - return dmnsn_pigment_evaluate(background, dmnsn_vector_normalized(ray.n)); - } else { - return background->quick_color; - } -} - -/** Calculate the base pigment at the intersection. */ -static void -dmnsn_trace_pigment(dmnsn_rtstate *state) -{ - dmnsn_pigment *pigment = state->texture->pigment; - if (state->scene->quality & DMNSN_RENDER_PIGMENT) { - state->pigment = dmnsn_pigment_evaluate(pigment, state->pigment_r); - } else { - state->pigment = pigment->quick_color; - } - - state->diffuse = state->pigment; -} - -/** Get the color of a light ray at an intersection point. */ -static bool -dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light, - dmnsn_color *color) -{ - /** @todo: Start at the light source */ - dmnsn_line shadow_ray = dmnsn_new_line( - state->r, - light->direction_fn(light, state->r) - ); - state->light = dmnsn_vector_normalized(shadow_ray.n); - /* Add epsilon to avoid hitting ourselves with the shadow ray */ - shadow_ray = dmnsn_line_add_epsilon(shadow_ray); - - /* Check if we're casting a shadow on ourself */ - if (dmnsn_vector_dot(shadow_ray.n, state->intersection->normal) - * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0) - { - return false; - } - - *color = light->illumination_fn(light, state->r); - - /* Test for shadow ray intersections */ - unsigned int reclevel = state->reclevel; - while (reclevel-- > 0 - && dmnsn_color_intensity(*color) >= state->scene->adc_bailout) - { - dmnsn_intersection shadow_caster; - bool shadow_was_cast = dmnsn_prtree_intersection(state->prtree, shadow_ray, - &shadow_caster, false); - - if (!shadow_was_cast || !light->shadow_fn(light, shadow_caster.t)) { - return true; - } - - /* Handle transparency */ - if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { - dmnsn_rtstate shadow_state = *state; - dmnsn_rtstate_initialize(&shadow_state, &shadow_caster); - dmnsn_trace_pigment(&shadow_state); - - if (shadow_state.pigment.trans >= dmnsn_epsilon) { - /* Reflect the light */ - const dmnsn_reflection *reflection = - shadow_state.texture->finish.reflection; - if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) { - dmnsn_color reflected = reflection->reflection_fn( - reflection, *color, shadow_state.pigment, shadow_state.reflected, - shadow_state.intersection->normal - ); - *color = dmnsn_color_sub(*color, reflected); - } - - /* Filter the light */ - *color = dmnsn_filter_light(*color, shadow_state.pigment); - shadow_ray.x0 = dmnsn_line_point(shadow_ray, shadow_caster.t); - shadow_ray.n = light->direction_fn(light, shadow_ray.x0); - shadow_ray = dmnsn_line_add_epsilon(shadow_ray); - continue; - } - } - - break; - } - - return false; -} - -/** Handle light, shadow, and shading. */ -static void -dmnsn_trace_lighting(dmnsn_rtstate *state) -{ - /* The ambient color */ - state->diffuse = dmnsn_black; - - const dmnsn_finish *finish = &state->texture->finish; - if (finish->ambient) { - state->diffuse = - finish->ambient->ambient_fn(finish->ambient, state->pigment); - } - - /* Iterate over each light */ - DMNSN_ARRAY_FOREACH (dmnsn_light **, light, state->scene->lights) { - dmnsn_color light_color; - if (dmnsn_trace_light_ray(state, *light, &light_color)) { - if (state->scene->quality & DMNSN_RENDER_FINISH) { - /* Get this light's specular contribution to the object */ - dmnsn_color specular = dmnsn_black; - if (finish->specular) { - specular = finish->specular->specular_fn( - finish->specular, light_color, state->pigment, state->light, - state->intersection->normal, state->viewer - ); - light_color = dmnsn_color_sub(light_color, specular); - } - - /* Reflect the light */ - const dmnsn_reflection *reflection = state->texture->finish.reflection; - if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) { - dmnsn_color reflected = reflection->reflection_fn( - reflection, light_color, state->pigment, state->reflected, - state->intersection->normal - ); - light_color = dmnsn_color_sub(light_color, reflected); - } - - /* Get this light's diffuse contribution to the object */ - dmnsn_color diffuse = dmnsn_black; - if (finish->diffuse) { - diffuse = finish->diffuse->diffuse_fn( - finish->diffuse, light_color, state->pigment, state->light, - state->intersection->normal - ); - } - - state->diffuse = dmnsn_color_add(diffuse, state->diffuse); - state->additional = dmnsn_color_add(specular, state->additional); - } else { - state->diffuse = state->pigment; - state->diffuse.trans = 0.0; - state->diffuse.filter = 0.0; - } - } - } -} - -/** Trace a reflected ray. */ -static dmnsn_color -dmnsn_trace_reflection(const dmnsn_rtstate *state) -{ - dmnsn_color reflected = dmnsn_black; - - const dmnsn_reflection *reflection = state->texture->finish.reflection; - if (reflection) { - dmnsn_line refl_ray = dmnsn_new_line(state->r, state->reflected); - refl_ray = dmnsn_line_add_epsilon(refl_ray); - - dmnsn_rtstate recursive_state = *state; - - /* Calculate ADC value */ - recursive_state.adc_value = reflection->reflection_fn( - reflection, state->adc_value, state->pigment, state->reflected, - state->intersection->normal - ); - - /* Shoot the reflected ray */ - dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray); - reflected = reflection->reflection_fn( - reflection, rec, state->pigment, state->reflected, - state->intersection->normal - ); - reflected.trans = 0.0; - reflected.filter = 0.0; - } - - return reflected; -} - -/** Handle transparency - must be called last to work correctly. */ -static void -dmnsn_trace_transparency(dmnsn_rtstate *state) -{ - if (state->pigment.trans >= dmnsn_epsilon) { - dmnsn_line trans_ray = dmnsn_new_line(state->r, state->intersection->ray.n); - trans_ray = dmnsn_line_add_epsilon(trans_ray); - - dmnsn_vector r = dmnsn_vector_normalized(trans_ray.n); - dmnsn_vector n = state->intersection->normal; - - dmnsn_rtstate recursive_state = *state; - - /* Calculate new refractive index */ - if (dmnsn_vector_dot(r, n) < 0.0) { - /* We are entering an object */ - recursive_state.ior = state->interior->ior; - recursive_state.parent = state; - } else { - /* We are leaving an object */ - recursive_state.ior = state->parent ? state->parent->ior : 1.0; - recursive_state.parent = state->parent ? state->parent->parent : NULL; - } - - /* Calculate transmitted ray direction */ - double iorr = state->ior/recursive_state.ior; /* ior ratio */ - double c1 = -dmnsn_vector_dot(r, n); - double c2 = 1.0 - iorr*iorr*(1.0 - c1*c1); - if (c2 <= 0.0) { - /* Total internal reflection */ - return; - } - c2 = sqrt(c2); - if (c1 >= 0.0) { - trans_ray.n = dmnsn_vector_add( - dmnsn_vector_mul(iorr, r), - dmnsn_vector_mul(iorr*c1 - c2, n) - ); - } else { - trans_ray.n = dmnsn_vector_add( - dmnsn_vector_mul(iorr, r), - dmnsn_vector_mul(iorr*c1 + c2, n) - ); - } - - /* Calculate ADC value */ - recursive_state.adc_value = - dmnsn_filter_light(state->adc_value, state->pigment); - - /* Shoot the transmitted ray */ - dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray); - dmnsn_color filtered = dmnsn_filter_light(rec, state->pigment); - - /* Conserve energy */ - const dmnsn_reflection *reflection = state->texture->finish.reflection; - if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) { - dmnsn_color reflected = reflection->reflection_fn( - reflection, filtered, state->pigment, state->reflected, - state->intersection->normal - ); - filtered = dmnsn_color_sub(filtered, reflected); - } - - state->diffuse.filter = state->pigment.filter; - state->diffuse.trans = state->pigment.trans; - state->diffuse = dmnsn_apply_transparency(filtered, state->diffuse); - } -} - -/* Shoot a ray, and calculate the color */ -static dmnsn_color -dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray) -{ - if (state->reclevel == 0 - || dmnsn_color_intensity(state->adc_value) < state->scene->adc_bailout) - { - return dmnsn_black; - } - - --state->reclevel; - - dmnsn_intersection intersection; - bool reset = state->reclevel == state->scene->reclimit - 1; - if (dmnsn_prtree_intersection(state->prtree, ray, &intersection, reset)) { - /* Found an intersection */ - dmnsn_rtstate_initialize(state, &intersection); - - /* Pigment */ - dmnsn_trace_pigment(state); - - /* Finishes and shadows */ - if (state->scene->quality & DMNSN_RENDER_LIGHTS) { - dmnsn_trace_lighting(state); - } - - /* Reflection */ - if (state->scene->quality & DMNSN_RENDER_REFLECTION) { - state->additional = dmnsn_color_add( - dmnsn_trace_reflection(state), - state->additional - ); - } - - /* Transparency */ - if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { - dmnsn_trace_transparency(state); - } - - return dmnsn_color_add(state->diffuse, state->additional); - } else { - /* No intersection, return the background color */ - return dmnsn_trace_background(state, ray); - } -} -- cgit v1.2.3