From bfbe9e43e108f6816c17b9b7764b75284ac78313 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 14 Dec 2011 19:27:22 -0500 Subject: Re-think colors. Color is a property of light, and thus doesn't include information about transparency. But canvas pixels and object pigments represent a color and a degree of transparency. The new type dmnsn_tcolor/ TColor encapsulates that information. Also, fix the transparent shadow implementation. --- libdimension/ray_trace.c | 458 ++++++++++++++++++++++++++--------------------- 1 file changed, 251 insertions(+), 207 deletions(-) (limited to 'libdimension/ray_trace.c') diff --git a/libdimension/ray_trace.c b/libdimension/ray_trace.c index dd84a26..127eb1e 100644 --- a/libdimension/ray_trace.c +++ b/libdimension/ray_trace.c @@ -118,11 +118,13 @@ typedef struct dmnsn_rtstate { dmnsn_vector pigment_r; dmnsn_vector viewer; dmnsn_vector reflected; - dmnsn_vector light; - dmnsn_color pigment; - dmnsn_color diffuse; - dmnsn_color additional; + bool is_shadow_ray; + dmnsn_vector light_ray; + dmnsn_color light_color; + + dmnsn_tcolor pigment; + dmnsn_tcolor color; double ior; @@ -132,34 +134,9 @@ typedef struct 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); + 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 @@ -191,9 +168,8 @@ dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, 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_tcolor tcolor = dmnsn_ray_shoot(&state, ray); + dmnsn_canvas_set_pixel(scene->canvas, x, y, tcolor); } dmnsn_future_increment(future); @@ -202,19 +178,93 @@ dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread, 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 dmnsn_color -dmnsn_trace_background(const dmnsn_rtstate *state, dmnsn_line ray) +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_prtree_intersection(state->prtree, ray, &intersection, reset); + if (dmnsn_prtree_intersection(state->prtree, 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) { - return dmnsn_pigment_evaluate(background, dmnsn_vector_normalized(ray.n)); + dmnsn_vector r = dmnsn_vector_normalized(ray.n); + state->color = dmnsn_pigment_evaluate(background, r); } else { - return background->quick_color; + state->color = background->quick_color; } } -/** Calculate the base pigment at the intersection. */ static void dmnsn_trace_pigment(dmnsn_rtstate *state) { @@ -224,143 +274,185 @@ dmnsn_trace_pigment(dmnsn_rtstate *state) } 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; + } +} - state->diffuse = state->pigment; +/** 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_color *color) +dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light) { - /** @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) + 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; } - *color = light->illumination_fn(light, state->r); + state->light_ray = dmnsn_vector_normalized(shadow_ray.n); + state->light_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; - } + dmnsn_intersection shadow_caster; + bool in_shadow = dmnsn_prtree_intersection(state->prtree, shadow_ray, + &shadow_caster, false); + if (!in_shadow || !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; + 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; } } - - break; } return false; } -/** Handle light, shadow, and shading. */ static void dmnsn_trace_lighting(dmnsn_rtstate *state) { - /* The ambient color */ - state->diffuse = dmnsn_black; - + /* Calculate the ambient color */ + state->color = DMNSN_TCOLOR(dmnsn_black); const dmnsn_finish *finish = &state->texture->finish; if (finish->ambient) { - state->diffuse = - finish->ambient->ambient_fn(finish->ambient, state->pigment); + 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) { - dmnsn_color light_color; - if (dmnsn_trace_light_ray(state, *light, &light_color)) { + if (dmnsn_trace_light_ray(state, *light)) { 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); + 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->diffuse = state->pigment; - state->diffuse.trans = 0.0; - state->diffuse.filter = 0.0; + state->color.c = state->pigment.c; + break; } } } } -/** Trace a reflected ray. */ -static dmnsn_color -dmnsn_trace_reflection(const dmnsn_rtstate *state) +static void +dmnsn_trace_reflection(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); @@ -369,29 +461,26 @@ dmnsn_trace_reflection(const dmnsn_rtstate *state) 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 + 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); - reflected = reflection->reflection_fn( - reflection, rec, state->pigment, state->reflected, - state->intersection->normal + dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray).c; + dmnsn_color reflected = dmnsn_evaluate_reflection( + state, rec, state->reflected ); - reflected.trans = 0.0; - reflected.filter = 0.0; - } - return reflected; + state->color.c = dmnsn_color_add(state->color.c, reflected); + } } -/** Handle transparency - must be called last to work correctly. */ static void dmnsn_trace_transparency(dmnsn_rtstate *state) { - if (state->pigment.trans >= dmnsn_epsilon) { + 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); @@ -403,7 +492,7 @@ dmnsn_trace_transparency(dmnsn_rtstate *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.ior = interior->ior; recursive_state.parent = state; } else { /* We are leaving an object */ @@ -433,71 +522,26 @@ dmnsn_trace_transparency(dmnsn_rtstate *state) } /* Calculate ADC value */ - recursive_state.adc_value = - dmnsn_filter_light(state->adc_value, state->pigment); + 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); - dmnsn_color filtered = dmnsn_filter_light(rec, state->pigment); + dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray).c; + dmnsn_color filtered = dmnsn_evaluate_transparency(state, rec); /* 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); - } + dmnsn_color reflected = dmnsn_evaluate_reflection( + state, filtered, state->reflected + ); + filtered = dmnsn_color_sub(filtered, reflected); - return dmnsn_color_add(state->diffuse, state->additional); - } else { - /* No intersection, return the background color */ - return dmnsn_trace_background(state, ray); + state->color.c = dmnsn_color_add(state->color.c, filtered); } } -- cgit v1.2.3