From 5038d1f96af332e474a1e0a076bf5dead6855727 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 18 Nov 2009 23:30:38 -0500 Subject: Translucency support. --- libdimension/color.c | 52 ++++++++++++++++++----- libdimension/dimension/color.h | 1 + libdimension/dimension/scene.h | 1 + libdimension/objects.c | 16 ++++--- libdimension/png.c | 20 +++++++-- libdimension/raytrace.c | 96 +++++++++++++++++++++++++++++------------- tests/libdimension/tests.c | 5 ++- 7 files changed, 141 insertions(+), 50 deletions(-) diff --git a/libdimension/color.c b/libdimension/color.c index 0ec9e0b..f309d09 100644 --- a/libdimension/color.c +++ b/libdimension/color.c @@ -27,41 +27,61 @@ const dmnsn_CIE_XYZ dmnsn_whitepoint = { .X = 0.9504060171449392, .Z = 1.089062231497274 }; /* Standard colors */ -const dmnsn_color dmnsn_black = { .X = 0.0, .Y = 0.0, .Z = 0.0 }; +const dmnsn_color dmnsn_black = { + .X = 0.0, + .Y = 0.0, + .Z = 0.0, + .filter = 0.0, + .trans = 0.0 +}; const dmnsn_color dmnsn_white = { .X = 0.9504060171449392, .Y = 0.9999085943425312, - .Z = 1.089062231497274 + .Z = 1.089062231497274, + .filter = 0.0, + .trans = 0.0 }; const dmnsn_color dmnsn_red = { .X = 0.4123808838268995, .Y = 0.2126198631048975, - .Z = 0.0193434956789248 + .Z = 0.0193434956789248, + .filter = 0.0, + .trans = 0.0 }; const dmnsn_color dmnsn_green = { .X = 0.3575728355732478, .Y = 0.7151387878413206, - .Z = 0.1192121694056356 + .Z = 0.1192121694056356, + .filter = 0.0, + .trans = 0.0 }; const dmnsn_color dmnsn_blue = { .X = 0.1804522977447919, .Y = 0.0721499433963131, - .Z = 0.950506566412713 + .Z = 0.950506566412713, + .filter = 0.0, + .trans = 0.0 }; const dmnsn_color dmnsn_magenta = { .X = 0.5928331815716914, .Y = 0.2847698065012106, - .Z = 0.9698500620916378 + .Z = 0.9698500620916378, + .filter = 0.0, + .trans = 0.0 }; const dmnsn_color dmnsn_yellow = { .X = 0.7699537194001473, .Y = 0.9277586509462181, - .Z = 0.1385556650845604 + .Z = 0.1385556650845604, + .filter = 0.0, + .trans = 0.0 }; const dmnsn_color dmnsn_cyan = { .X = 0.5380251333180397, .Y = 0.7872887312376337, - .Z = 1.069718735818349 + .Z = 1.069718735818349, + .filter = 0.0, + .trans = 0.0 }; /* Convert a CIE XYZ color to a dmnsn_color (actually a no-op) */ @@ -335,8 +355,10 @@ dmnsn_color_add(dmnsn_color color1, dmnsn_color color2) dmnsn_color ret = dmnsn_color_from_Lab(Lab, dmnsn_whitepoint); /* Weighted average of transparencies by intensity */ - ret.filter = (Lab1.L*color1.filter + Lab2.L*color2.filter)/Lab.L; - ret.trans = (Lab1.L*color1.trans + Lab2.L*color2.trans)/Lab.L; + if (Lab.L) { + ret.filter = (Lab1.L*color1.filter + Lab2.L*color2.filter)/Lab.L; + ret.trans = (Lab1.L*color1.trans + Lab2.L*color2.trans)/Lab.L; + } return ret; } @@ -357,6 +379,16 @@ dmnsn_color_mul(double n, dmnsn_color color) return ret; } +/* Filters `color' through `filter' */ +dmnsn_color +dmnsn_color_filter(dmnsn_color color, dmnsn_color filter) +{ + dmnsn_color transmitted = dmnsn_color_mul(filter.trans, color); + dmnsn_color filtered = dmnsn_color_mul(filter.filter, + dmnsn_color_illuminate(filter, color)); + return dmnsn_color_add(transmitted, filtered); +} + /* Illuminates `color' with `light' */ dmnsn_color dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color) diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h index ad50334..10fa1a7 100644 --- a/libdimension/dimension/color.h +++ b/libdimension/dimension/color.h @@ -90,6 +90,7 @@ dmnsn_sRGB dmnsn_sRGB_from_color(dmnsn_color color); /* Perceptual color manipulation */ dmnsn_color dmnsn_color_add(dmnsn_color color1, dmnsn_color color2); dmnsn_color dmnsn_color_mul(double n, dmnsn_color color); +dmnsn_color dmnsn_color_filter(dmnsn_color color, dmnsn_color filter); dmnsn_color dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color); double dmnsn_color_difference(dmnsn_color color1, dmnsn_color color2); diff --git a/libdimension/dimension/scene.h b/libdimension/dimension/scene.h index 8108b2b..25e52fe 100644 --- a/libdimension/dimension/scene.h +++ b/libdimension/dimension/scene.h @@ -31,6 +31,7 @@ typedef enum { DMNSN_RENDER_PIGMENT, DMNSN_RENDER_LIGHTS, DMNSN_RENDER_FINISH, + DMNSN_RENDER_TRANSLUCENCY, DMNSN_RENDER_FULL } dmnsn_quality; diff --git a/libdimension/objects.c b/libdimension/objects.c index b5e4a32..845edfa 100644 --- a/libdimension/objects.c +++ b/libdimension/objects.c @@ -73,6 +73,10 @@ dmnsn_sphere_intersection_fn(const dmnsn_object *sphere, dmnsn_line line) intersection->t = t; intersection->normal = dmnsn_line_point(line, t); intersection->texture = sphere->texture; + + /* Flip the normal if we're inside the sphere */ + if (dmnsn_vector_dot(line.n, intersection->normal) > 0.0) + intersection->normal = dmnsn_vector_negate(intersection->normal); } } @@ -128,7 +132,7 @@ dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line) && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) { t = t_temp; - normal = dmnsn_vector_construct(-1.0, 0.0, 0.0); + normal = dmnsn_vector_construct(-copysign(line.n.x, 1.0), 0.0, 0.0); } /* x = 1.0 */ @@ -138,7 +142,7 @@ dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line) && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) { t = t_temp; - normal = dmnsn_vector_construct(1.0, 0.0, 0.0); + normal = dmnsn_vector_construct(-copysign(line.n.x, 1.0), 0.0, 0.0); } } @@ -150,7 +154,7 @@ dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line) && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) { t = t_temp; - normal = dmnsn_vector_construct(0.0, -1.0, 0.0); + normal = dmnsn_vector_construct(0.0, -copysign(line.n.y, 1.0), 0.0); } /* y = 1.0 */ @@ -160,7 +164,7 @@ dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line) && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) { t = t_temp; - normal = dmnsn_vector_construct(0.0, 1.0, 0.0); + normal = dmnsn_vector_construct(0.0, -copysign(line.n.y, 1.0), 0.0); } } @@ -172,7 +176,7 @@ dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line) && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) { t = t_temp; - normal = dmnsn_vector_construct(0.0, 0.0, -1.0); + normal = dmnsn_vector_construct(0.0, 0.0, -copysign(line.n.z, 1.0)); } /* z = 1.0 */ @@ -182,7 +186,7 @@ dmnsn_cube_intersection_fn(const dmnsn_object *cube, dmnsn_line line) && t_temp >= 0.0 && (t < 0.0 || t_temp < t)) { t = t_temp; - normal = dmnsn_vector_construct(0.0, 0.0, 1.0); + normal = dmnsn_vector_construct(0.0, 0.0, -copysign(line.n.z, 1.0)); } } diff --git a/libdimension/png.c b/libdimension/png.c index 20190d8..1ad8a1a 100644 --- a/libdimension/png.c +++ b/libdimension/png.c @@ -96,8 +96,14 @@ dmnsn_png_optimizer_fn(dmnsn_canvas *canvas, dmnsn_canvas_optimizer optimizer, pixel[2] = sRGB.B*UINT16_MAX; } - /* color.filter + color.trans is in [0.0, 1.0] by definition */ - pixel[3] = (color.filter + color.trans)*UINT16_MAX; + double alpha = color.filter + color.trans; + if (alpha <= 0.0) { + pixel[3] = 0; + } else if (alpha >= 1.0) { + pixel[3] = UINT16_MAX; + } else { + pixel[3] = alpha*UINT16_MAX; + } } /* Payload to store function arguments for thread callbacks */ @@ -359,8 +365,14 @@ dmnsn_png_write_canvas_impl(dmnsn_progress *progress, row[4*x + 2] = sRGB.B*UINT16_MAX; } - /* color.filter + color.trans is in [0.0, 1.0] by definition */ - row[4*x + 3] = (color.filter + color.trans)*UINT16_MAX; + double alpha = color.filter + color.trans; + if (alpha <= 0.0) { + row[4*x + 3] = 0; + } else if (alpha >= 1.0) { + row[4*x + 3] = UINT16_MAX; + } else { + row[4*x + 3] = alpha*UINT16_MAX; + } } /* Write the row */ diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index 7608d85..1024a7e 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -316,28 +316,31 @@ dmnsn_raytrace_lighting(dmnsn_intersection *intersection, dmnsn_scene *scene, ); /* Search for an object in the way of the light source */ - dmnsn_intersection *shadow_caster - = dmnsn_kD_splay_search(kD_splay_tree, shadow_ray); - bool lit = !shadow_caster || shadow_caster->t > 1.0; - dmnsn_delete_intersection(shadow_caster); - - if (scene->quality >= DMNSN_RENDER_FINISH && finish) { - dmnsn_color light_color = dmnsn_black; - if (lit) - light_color = (*light->light_fn)(light, x0); - - dmnsn_vector ray = dmnsn_vector_normalize(shadow_ray.n); - dmnsn_vector normal = intersection->normal; - dmnsn_vector viewer - = dmnsn_vector_normalize(dmnsn_vector_negate(intersection->ray.n)); - - /* Get this light's color contribution to the object */ - dmnsn_color contrib = (*finish->finish_fn)(finish, - light_color, color, - ray, normal, viewer); - illum = dmnsn_color_add(contrib, illum); - } else if (lit) { - illum = color; + bool lit = dmnsn_vector_dot(shadow_ray.n, intersection->normal) > 0.0; + if (lit) { + dmnsn_intersection *shadow_caster + = dmnsn_kD_splay_search(kD_splay_tree, shadow_ray); + lit = (!shadow_caster || shadow_caster->t > 1.0); + dmnsn_delete_intersection(shadow_caster); + } + + if (lit) { + if (scene->quality >= DMNSN_RENDER_FINISH && finish) { + dmnsn_color light_color = (*light->light_fn)(light, x0); + + dmnsn_vector ray = dmnsn_vector_normalize(shadow_ray.n); + dmnsn_vector normal = intersection->normal; + dmnsn_vector viewer + = dmnsn_vector_normalize(dmnsn_vector_negate(intersection->ray.n)); + + /* Get this light's color contribution to the object */ + dmnsn_color contrib = (*finish->finish_fn)(finish, + light_color, color, + ray, normal, viewer); + illum = dmnsn_color_add(contrib, illum); + } else { + illum = color; + } } } @@ -347,24 +350,59 @@ dmnsn_raytrace_lighting(dmnsn_intersection *intersection, dmnsn_scene *scene, /* Shoot a ray, and calculate the color, using `color' as the background */ static dmnsn_color dmnsn_raytrace_shoot(dmnsn_line ray, dmnsn_scene *scene, - dmnsn_kD_splay_tree *kD_splay_tree, dmnsn_color color) + dmnsn_kD_splay_tree *kD_splay_tree, dmnsn_color background) { dmnsn_intersection *intersection = dmnsn_kD_splay_search(kD_splay_tree, ray); + dmnsn_color color = background; if (intersection) { - /* Default to black if we aren't rendering pigments */ - color = dmnsn_black; - + /* Get the pigment of the object */ + dmnsn_color pigment = dmnsn_black; if (scene->quality >= DMNSN_RENDER_PIGMENT) { - color = dmnsn_raytrace_pigment(intersection, scene); + pigment = dmnsn_raytrace_pigment(intersection, scene); } + color = pigment; + color.filter = 0.0; + color.trans = 0.0; + /* Account for finishes and shadows */ + dmnsn_color illum = pigment; if (scene->quality >= DMNSN_RENDER_LIGHTS) { - color = dmnsn_raytrace_lighting(intersection, + illum = dmnsn_raytrace_lighting(intersection, scene, kD_splay_tree, - color); + pigment); + } + color = illum; + color.filter = 0.0; + color.trans = 0.0; + + /* Account for translucency */ + dmnsn_color trans = illum; + if (scene->quality >= DMNSN_RENDER_TRANSLUCENCY + && (pigment.filter || pigment.trans)) + { + trans = dmnsn_color_mul(1.0 - pigment.filter - pigment.trans, illum); + trans.filter = 0.0; + trans.trans = 0.0; + + dmnsn_line trans_ray = dmnsn_line_construct( + dmnsn_vector_add( + dmnsn_line_point(ray, intersection->t), + dmnsn_vector_mul(1.0e-9, ray.n) + ), + ray.n + ); + trans_ray.n = ray.n; + + dmnsn_color rec = dmnsn_raytrace_shoot(trans_ray, + scene, + kD_splay_tree, + background); + dmnsn_color filtered = dmnsn_color_filter(rec, pigment); + trans = dmnsn_color_add(trans, filtered); } + color = trans; dmnsn_delete_intersection(intersection); } diff --git a/tests/libdimension/tests.c b/tests/libdimension/tests.c index b2670f8..c0bd11f 100644 --- a/tests/libdimension/tests.c +++ b/tests/libdimension/tests.c @@ -109,7 +109,10 @@ dmnsn_new_default_scene() return NULL; } - cube->texture->pigment = dmnsn_new_solid_pigment(dmnsn_magenta); + dmnsn_color cube_color = dmnsn_magenta; + cube_color.filter = 0.25; + cube_color.trans = 0.25; + cube->texture->pigment = dmnsn_new_solid_pigment(cube_color); if (!cube->texture->pigment) { dmnsn_delete_scene(scene); return NULL; -- cgit v1.2.3