diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2014-08-19 17:10:03 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2015-10-25 11:03:56 -0400 |
commit | 7b09710392d35fb55b52031d447a542d99fc6b4b (patch) | |
tree | 270eb927ee8c52ceeb99926ebf4843704775a610 /libdimension/ray_trace.c | |
parent | 200c86b91ea7063d35be3bffc11c5da53c054653 (diff) | |
download | dimension-7b09710392d35fb55b52031d447a542d99fc6b4b.tar.xz |
Modularize the libdimension codebase.
Diffstat (limited to 'libdimension/ray_trace.c')
-rw-r--r-- | libdimension/ray_trace.c | 547 |
1 files changed, 0 insertions, 547 deletions
diff --git a/libdimension/ray_trace.c b/libdimension/ray_trace.c deleted file mode 100644 index 6f4ce33..0000000 --- a/libdimension/ray_trace.c +++ /dev/null @@ -1,547 +0,0 @@ -/************************************************************************* - * Copyright (C) 2010-2014 Tavian Barnes <tavianator@tavianator.com> * - * * - * 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 * - * <http://www.gnu.org/licenses/>. * - *************************************************************************/ - -/** - * @file - * The ray-tracing algorithm. - */ - -#include "dimension-internal.h" -#include <stdlib.h> - -//////////////////////////////////// -// Boilerplate for multithreading // -//////////////////////////////////// - -/// Payload type for passing arguments to worker threads. -typedef struct { - dmnsn_future *future; - dmnsn_scene *scene; - dmnsn_bvh *bvh; -} 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(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->bvh = dmnsn_new_bvh(payload->scene->objects, DMNSN_BVH_PRTREE); - 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(payload->future, - dmnsn_ray_trace_scene_concurrent, - payload, payload->scene->nthreads); - dmnsn_timer_stop(&payload->scene->render_timer); - - dmnsn_delete_bvh(payload->bvh); - 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_bvh *bvh; - unsigned int reclevel; - - dmnsn_vector r; - dmnsn_vector pigment_r; - dmnsn_vector viewer; - dmnsn_vector reflected; - - bool is_shadow_ray; - dmnsn_vector light_ray; - dmnsn_color light_color; - - dmnsn_tcolor pigment; - dmnsn_tcolor color; - - 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); -/// Main helper for dmnsn_ray_trace_scene_concurrent - shoot a ray. -static dmnsn_tcolor 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_bvh *bvh = payload->bvh; - - dmnsn_rtstate state = { - .parent = NULL, - .scene = scene, - .bvh = bvh, - }; - - // 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_tcolor tcolor = dmnsn_ray_shoot(&state, ray); - dmnsn_canvas_set_pixel(scene->canvas, x, y, tcolor); - } - - dmnsn_future_increment(future); - } - - return 0; -} - -// Compute rtstate fields -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->is_shadow_ray = false; -} - -/// Calculate the background color. -static void dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_line ray); -/// Calculate the base pigment at the intersection. -static void dmnsn_trace_pigment(dmnsn_rtstate *state); -/// Handle light, shadow, and shading. -static void dmnsn_trace_lighting(dmnsn_rtstate *state); -/// Trace a reflected ray. -static void dmnsn_trace_reflection(dmnsn_rtstate *state); -/// Trace a transmitted ray. -static void dmnsn_trace_transparency(dmnsn_rtstate *state); - -// Shoot a ray, and calculate the color -static dmnsn_tcolor -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_TCOLOR(dmnsn_black); - } - - --state->reclevel; - - dmnsn_intersection intersection; - bool reset = state->reclevel == state->scene->reclimit - 1; - dmnsn_bvh_intersection(state->bvh, ray, &intersection, reset); - if (dmnsn_bvh_intersection(state->bvh, ray, &intersection, reset)) { - // Found an intersection - dmnsn_rtstate_initialize(state, &intersection); - - dmnsn_trace_pigment(state); - if (state->scene->quality & DMNSN_RENDER_LIGHTS) { - dmnsn_trace_lighting(state); - } - if (state->scene->quality & DMNSN_RENDER_REFLECTION) { - dmnsn_trace_reflection(state); - } - if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { - dmnsn_trace_transparency(state); - } - } else { - // No intersection, return the background color - dmnsn_trace_background(state, ray); - } - - return state->color; -} - -static void -dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_line ray) -{ - dmnsn_pigment *background = state->scene->background; - if (state->scene->quality & DMNSN_RENDER_PIGMENT) { - dmnsn_vector r = dmnsn_vector_normalized(ray.n); - state->color = dmnsn_pigment_evaluate(background, r); - } else { - state->color = background->quick_color; - } -} - -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->color = state->pigment; -} - -/// Determine the amount of specular highlight. -static inline dmnsn_color -dmnsn_evaluate_specular(const dmnsn_rtstate *state) -{ - const dmnsn_finish *finish = &state->texture->finish; - if (finish->specular) { - return finish->specular->specular_fn( - finish->specular, state->light_color, state->pigment.c, - state->light_ray, state->intersection->normal, state->viewer - ); - } else { - return dmnsn_black; - } -} - -/// Determine the amount of reflected light. -static inline dmnsn_color -dmnsn_evaluate_reflection(const dmnsn_rtstate *state, - dmnsn_color light, dmnsn_vector direction) -{ - const dmnsn_reflection *reflection = state->texture->finish.reflection; - if (reflection && (state->scene->quality & DMNSN_RENDER_REFLECTION)) { - return reflection->reflection_fn( - reflection, light, state->pigment.c, direction, - state->intersection->normal - ); - } else { - return dmnsn_black; - } -} - -/// Determine the amount of transmitted light. -static inline dmnsn_color -dmnsn_evaluate_transparency(const dmnsn_rtstate *state, dmnsn_color light) -{ - if (state->pigment.T >= dmnsn_epsilon - && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY)) - { - return dmnsn_tcolor_filter(light, state->pigment); - } else { - return dmnsn_black; - } -} - -/// Get a light's diffuse contribution to the object -static inline dmnsn_color -dmnsn_evaluate_diffuse(const dmnsn_rtstate *state) -{ - const dmnsn_finish *finish = &state->texture->finish; - if (finish->diffuse) { - return finish->diffuse->diffuse_fn( - finish->diffuse, state->light_color, state->pigment.c, - state->light_ray, state->intersection->normal - ); - } else { - return dmnsn_black; - } -} - -/// 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_line shadow_ray = dmnsn_new_line( - state->r, - light->direction_fn(light, state->r) - ); - // 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) - && (!state->is_shadow_ray || state->pigment.T < dmnsn_epsilon)) - { - return false; - } - - state->light_ray = dmnsn_vector_normalized(shadow_ray.n); - state->light_color = light->illumination_fn(light, state->r); - - // Test for shadow ray intersections - dmnsn_intersection shadow_caster; - bool in_shadow = dmnsn_bvh_intersection(state->bvh, shadow_ray, - &shadow_caster, false); - if (!in_shadow || !light->shadow_fn(light, shadow_caster.t)) { - return true; - } - - if (state->reclevel > 0 - && dmnsn_color_intensity(state->adc_value) >= state->scene->adc_bailout - && (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.T >= dmnsn_epsilon) { - --shadow_state.reclevel; - shadow_state.adc_value = dmnsn_evaluate_transparency( - &shadow_state, shadow_state.adc_value - ); - shadow_state.is_shadow_ray = true; - if (dmnsn_trace_light_ray(&shadow_state, light)) { - state->light_color = shadow_state.light_color; - - // Handle reflection - dmnsn_color reflected = dmnsn_evaluate_reflection( - &shadow_state, state->light_color, state->light_ray - ); - state->light_color = dmnsn_color_sub(state->light_color, reflected); - - // Handle transparency - state->light_color = dmnsn_evaluate_transparency( - &shadow_state, state->light_color - ); - - return true; - } - } - } - - return false; -} - -static void -dmnsn_trace_lighting(dmnsn_rtstate *state) -{ - // Calculate the ambient color - state->color = DMNSN_TCOLOR(dmnsn_black); - const dmnsn_finish *finish = &state->texture->finish; - if (finish->ambient) { - dmnsn_color ambient = finish->ambient->ambient; - - // Handle reflection and transmittance of the ambient light - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, ambient, state->intersection->normal - ); - ambient = dmnsn_color_sub(ambient, reflected); - dmnsn_color transmitted = dmnsn_evaluate_transparency(state, ambient); - ambient = dmnsn_color_sub(ambient, transmitted); - - state->color.c = dmnsn_color_illuminate(ambient, state->pigment.c); - } - - // Iterate over each light - DMNSN_ARRAY_FOREACH (dmnsn_light **, light, state->scene->lights) { - if (dmnsn_trace_light_ray(state, *light)) { - if (state->scene->quality & DMNSN_RENDER_FINISH) { - dmnsn_color specular = dmnsn_evaluate_specular(state); - state->light_color = dmnsn_color_sub(state->light_color, specular); - - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, state->light_color, state->reflected - ); - state->light_color = dmnsn_color_sub(state->light_color, reflected); - - dmnsn_color transmitted = dmnsn_evaluate_transparency( - state, state->light_color - ); - state->light_color = dmnsn_color_sub(state->light_color, transmitted); - - dmnsn_color diffuse = dmnsn_evaluate_diffuse(state); - - state->color.c = dmnsn_color_add(state->color.c, specular); - state->color.c = dmnsn_color_add(state->color.c, diffuse); - } else { - state->color.c = state->pigment.c; - break; - } - } - } -} - -static void -dmnsn_trace_reflection(dmnsn_rtstate *state) -{ - 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 = dmnsn_evaluate_reflection( - state, state->adc_value, state->reflected - ); - - // Shoot the reflected ray - dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray).c; - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, rec, state->reflected - ); - - state->color.c = dmnsn_color_add(state->color.c, reflected); - } -} - -static void -dmnsn_trace_transparency(dmnsn_rtstate *state) -{ - if (state->pigment.T >= dmnsn_epsilon) { - const dmnsn_interior *interior = state->interior; - - 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 = 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_evaluate_transparency( - state, state->adc_value - ); - dmnsn_color adc_reflected = dmnsn_evaluate_reflection( - state, recursive_state.adc_value, state->reflected - ); - recursive_state.adc_value = dmnsn_color_sub( - recursive_state.adc_value, adc_reflected - ); - - // Shoot the transmitted ray - dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray).c; - dmnsn_color filtered = dmnsn_evaluate_transparency(state, rec); - - // Conserve energy - dmnsn_color reflected = dmnsn_evaluate_reflection( - state, filtered, state->reflected - ); - filtered = dmnsn_color_sub(filtered, reflected); - - state->color.c = dmnsn_color_add(state->color.c, filtered); - } -} |