summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2009-11-18 23:30:38 -0500
committerTavian Barnes <tavianator@gmail.com>2009-11-18 23:30:38 -0500
commit5038d1f96af332e474a1e0a076bf5dead6855727 (patch)
tree128921e3bbdc29046695ac4d9c9fb8d7535bc49a
parentc576229cc54c0fb963967751281e6a42fc9230ea (diff)
downloaddimension-5038d1f96af332e474a1e0a076bf5dead6855727.tar.xz
Translucency support.
-rw-r--r--libdimension/color.c52
-rw-r--r--libdimension/dimension/color.h1
-rw-r--r--libdimension/dimension/scene.h1
-rw-r--r--libdimension/objects.c16
-rw-r--r--libdimension/png.c20
-rw-r--r--libdimension/raytrace.c96
-rw-r--r--tests/libdimension/tests.c5
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;