summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2011-08-18 20:17:37 -0600
committerTavian Barnes <tavianator@gmail.com>2011-08-18 20:17:37 -0600
commit9c9fb9d59c2e90fc2bc9066dac514285c9479277 (patch)
treedf0d64d60cb933698054b41312167a790f5787cd
parent6766ce89672897cabe48e48798b29dc3a461f1da (diff)
downloaddimension-9c9fb9d59c2e90fc2bc9066dac514285c9479277.tar.xz
Handle reflection of light and transmitted rays.
-rw-r--r--libdimension/color.c23
-rw-r--r--libdimension/dimension/color.h4
-rw-r--r--libdimension/dimension/light.h4
-rw-r--r--libdimension/point_light.c4
-rw-r--r--libdimension/raytrace.c115
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);