From 9c9fb9d59c2e90fc2bc9066dac514285c9479277 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 Aug 2011 20:17:37 -0600 Subject: Handle reflection of light and transmitted rays. --- libdimension/color.c | 23 +++++++++ libdimension/dimension/color.h | 4 +- libdimension/dimension/light.h | 4 +- libdimension/point_light.c | 4 +- libdimension/raytrace.c | 115 ++++++++++++++++++++++++++++------------- 5 files changed, 108 insertions(+), 42 deletions(-) diff --git a/libdimension/color.c b/libdimension/color.c index 02ecff9..ad9ec0a 100644 --- a/libdimension/color.c +++ b/libdimension/color.c @@ -197,6 +197,29 @@ dmnsn_color_add(dmnsn_color c1, dmnsn_color c2) return ret; } +/* Subtract two colors */ +dmnsn_color +dmnsn_color_sub(dmnsn_color c1, dmnsn_color c2) +{ + dmnsn_color ret = dmnsn_new_color(c1.R - c2.R, c1.G - c2.G, c1.B - c2.B); + + /* Switch into absolute filter and transmittance space */ + double n1 = dmnsn_color_intensity(c1), n2 = dmnsn_color_intensity(c2); + double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans; + double t1 = c1.trans - f1, t2 = c2.trans - f2; + double f = 0.0; + if (n1 - n2 >= dmnsn_epsilon) + f = (n1*f1 - n2*f2)/(n1 - n2); + double t = t1 - t2; + + /* Switch back */ + ret.trans = f + t; + if (ret.trans >= dmnsn_epsilon) + ret.filter = f/ret.trans; + + return ret; +} + /* Multiply a color by a scalar */ dmnsn_color dmnsn_color_mul(double n, dmnsn_color color) diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index b135c70..6d706eb 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -84,7 +84,9 @@ dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color); /** Greyscale color intensity. */ double dmnsn_color_intensity(dmnsn_color color); /** Add two colors together. */ -dmnsn_color dmnsn_color_add(dmnsn_color color1, dmnsn_color color2); +dmnsn_color dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs); +/** Subtract two colors. */ +dmnsn_color dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs); /** Multiply a color's intensity by \p n. */ dmnsn_color dmnsn_color_mul(double n, dmnsn_color color); /** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */ diff --git a/libdimension/dimension/light.h b/libdimension/dimension/light.h index b41a524..36c724a 100644 --- a/libdimension/dimension/light.h +++ b/libdimension/dimension/light.h @@ -34,8 +34,8 @@ typedef struct dmnsn_light dmnsn_light; * @param[in] v The point to illuminate. * @return The direction of light rays pointing from \p v */ -typedef dmnsn_line dmnsn_light_direction_fn(const dmnsn_light *light, - dmnsn_vector v); +typedef dmnsn_vector dmnsn_light_direction_fn(const dmnsn_light *light, + dmnsn_vector v); /** * Light illumination callback. diff --git a/libdimension/point_light.c b/libdimension/point_light.c index 6ecaa24..219ea70 100644 --- a/libdimension/point_light.c +++ b/libdimension/point_light.c @@ -33,11 +33,11 @@ typedef struct dmnsn_point_light_payload { } dmnsn_point_light_payload; /** Point light direction callback. */ -static dmnsn_line +static dmnsn_vector dmnsn_point_light_direction_fn(const dmnsn_light *light, dmnsn_vector v) { dmnsn_point_light_payload *payload = light->ptr; - return dmnsn_new_line(v, dmnsn_vector_sub(payload->origin, v)); + return dmnsn_vector_sub(payload->origin, v); } /** Point light illumination callback. */ diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index ddd6e09..71e5578 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -56,8 +56,8 @@ dmnsn_raytrace_scene_async(dmnsn_scene *scene) { dmnsn_progress *progress = dmnsn_new_progress(); - dmnsn_raytrace_payload *payload - = dmnsn_malloc(sizeof(dmnsn_raytrace_payload)); + dmnsn_raytrace_payload *payload = + dmnsn_malloc(sizeof(dmnsn_raytrace_payload)); payload->progress = progress; payload->scene = scene; @@ -238,7 +238,11 @@ static dmnsn_color dmnsn_raytrace_light_ray(dmnsn_raytrace_state *state, const dmnsn_light *light) { - dmnsn_line shadow_ray = light->direction_fn(light, state->r); + /** @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); @@ -250,39 +254,50 @@ dmnsn_raytrace_light_ray(dmnsn_raytrace_state *state, dmnsn_color color = light->illumination_fn(light, state->r); + /* Test for shadow ray intersections */ unsigned int reclevel = state->reclevel; - while (reclevel > 0 + while (reclevel-- > 0 && dmnsn_color_intensity(color) >= state->scene->adc_bailout) { dmnsn_intersection shadow_caster; - bool shadow_casted = dmnsn_prtree_intersection(state->prtree, shadow_ray, - &shadow_caster, false); + bool shadow_was_cast = dmnsn_prtree_intersection(state->prtree, shadow_ray, + &shadow_caster, false); - if (!shadow_casted || !light->shadow_fn(light, shadow_caster.t)) { - break; + if (!shadow_was_cast || !light->shadow_fn(light, shadow_caster.t)) { + return color; } - dmnsn_raytrace_state shadow_state = *state; - dmnsn_initialize_raytrace_state(&shadow_state, &shadow_caster); - - dmnsn_raytrace_pigment(&shadow_state); - if ((state->scene->quality & DMNSN_RENDER_TRANSPARENCY) - && shadow_state.pigment.trans >= dmnsn_epsilon) - { - color = dmnsn_filter_light(color, shadow_state.pigment); - shadow_ray = light->direction_fn( - light, - dmnsn_line_point(shadow_ray, shadow_caster.t) - ); - shadow_ray = dmnsn_line_add_epsilon(shadow_ray); - } else { - return dmnsn_black; + /* Handle transparency */ + if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) { + dmnsn_raytrace_state shadow_state = *state; + dmnsn_initialize_raytrace_state(&shadow_state, &shadow_caster); + dmnsn_raytrace_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; + } } - --reclevel; + break; } - return color; + return dmnsn_black; } /** Handle light, shadow, and shading. */ @@ -294,8 +309,8 @@ dmnsn_raytrace_lighting(dmnsn_raytrace_state *state) const dmnsn_finish *finish = &state->texture->finish; if (finish->ambient) { - state->diffuse - = finish->ambient->ambient_fn(finish->ambient, state->pigment); + state->diffuse = + finish->ambient->ambient_fn(finish->ambient, state->pigment); } /* Iterate over each light */ @@ -303,6 +318,16 @@ dmnsn_raytrace_lighting(dmnsn_raytrace_state *state) dmnsn_color light_color = dmnsn_raytrace_light_ray(state, *light); if (!dmnsn_color_is_black(light_color)) { if (state->scene->quality & DMNSN_RENDER_FINISH) { + /* 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 color contribution to the object */ dmnsn_color diffuse = dmnsn_black; if (finish->diffuse) { @@ -337,20 +362,23 @@ dmnsn_raytrace_reflection(const dmnsn_raytrace_state *state) { dmnsn_color reflected = dmnsn_black; - const dmnsn_finish *finish = &state->texture->finish; - if (finish->reflection) { + 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_raytrace_state recursive_state = *state; - recursive_state.adc_value = finish->reflection->reflection_fn( - finish->reflection, state->adc_value, state->pigment, state->reflected, + + /* 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_raytrace_shoot(&recursive_state, refl_ray); - reflected = finish->reflection->reflection_fn( - finish->reflection, rec, state->pigment, state->reflected, + reflected = reflection->reflection_fn( + reflection, rec, state->pigment, state->reflected, state->intersection->normal ); reflected.trans = 0.0; @@ -373,6 +401,7 @@ dmnsn_raytrace_transparency(dmnsn_raytrace_state *state) dmnsn_raytrace_state 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; @@ -383,8 +412,8 @@ dmnsn_raytrace_transparency(dmnsn_raytrace_state *state) 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) { @@ -392,7 +421,6 @@ dmnsn_raytrace_transparency(dmnsn_raytrace_state *state) return; } c2 = sqrt(c2); - if (c1 >= 0.0) { trans_ray.n = dmnsn_vector_add( dmnsn_vector_mul(iorr, r), @@ -405,11 +433,24 @@ dmnsn_raytrace_transparency(dmnsn_raytrace_state *state) ); } - recursive_state.adc_value - = dmnsn_filter_light(state->adc_value, state->pigment); + /* Calculate ADC value */ + recursive_state.adc_value = + dmnsn_filter_light(state->adc_value, state->pigment); + /* Shoot the transmitted ray */ dmnsn_color rec = dmnsn_raytrace_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); -- cgit v1.2.3