summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdimension/Makefile.am1
-rw-r--r--libdimension/color.c27
-rw-r--r--libdimension/dimension/color.h1
-rw-r--r--libdimension/dimension/finishes.h4
-rw-r--r--libdimension/dimension/scene.h1
-rw-r--r--libdimension/dimension/texture.h10
-rw-r--r--libdimension/finish_combination.c40
-rw-r--r--libdimension/raytrace.c144
-rw-r--r--libdimension/reflective.c74
-rw-r--r--libdimension/texture.c7
-rw-r--r--tests/libdimension/tests.c16
11 files changed, 261 insertions, 64 deletions
diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am
index 49685c7..0efa05c 100644
--- a/libdimension/Makefile.am
+++ b/libdimension/Makefile.am
@@ -63,6 +63,7 @@ libdimension_la_SOURCES = $(nobase_include_HEADERS) \
point_light.c \
progress.c \
raytrace.c \
+ reflective.c \
scene.c \
solid_pigment.c \
sphere.c \
diff --git a/libdimension/color.c b/libdimension/color.c
index 4421224..8201a8f 100644
--- a/libdimension/color.c
+++ b/libdimension/color.c
@@ -292,7 +292,7 @@ dmnsn_sRGB_from_color(dmnsn_color color)
return sRGB;
}
-/* Add two colors in a perceptually correct manner, using CIE L*a*b*. */
+/* Add two colors */
dmnsn_color
dmnsn_color_add(dmnsn_color c1, dmnsn_color c2)
{
@@ -317,6 +317,31 @@ 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_sRGB sRGB1 = dmnsn_sRGB_from_color(c1);
+ dmnsn_sRGB sRGB2 = dmnsn_sRGB_from_color(c2);
+
+ dmnsn_sRGB sRGB = {
+ .R = sRGB1.R - sRGB2.R,
+ .G = sRGB1.G - sRGB2.G,
+ .B = sRGB1.B - sRGB2.B
+ };
+
+ dmnsn_color ret = dmnsn_color_from_sRGB(sRGB);
+
+ /* Weighted average of transparencies by intensity */
+ dmnsn_CIE_Lab Lab1 = dmnsn_Lab_from_color(ret, dmnsn_whitepoint);
+ dmnsn_CIE_Lab Lab2 = dmnsn_Lab_from_color(ret, dmnsn_whitepoint);
+ if (Lab1.L + Lab2.L) {
+ ret.filter = (Lab1.L*c1.filter - Lab2.L*c2.filter)/(Lab1.L + Lab2.L);
+ ret.trans = (Lab1.L*c1.trans - Lab2.L*c2.trans )/(Lab1.L + Lab2.L);
+ }
+ 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 cd44712..67fa032 100644
--- a/libdimension/dimension/color.h
+++ b/libdimension/dimension/color.h
@@ -83,6 +83,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_sub(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);
diff --git a/libdimension/dimension/finishes.h b/libdimension/dimension/finishes.h
index b836e51..399ff84 100644
--- a/libdimension/dimension/finishes.h
+++ b/libdimension/dimension/finishes.h
@@ -34,4 +34,8 @@ dmnsn_finish *dmnsn_new_diffuse_finish(double diffuse);
/* A phong specular highlight */
dmnsn_finish *dmnsn_new_phong_finish(double specular, double exp);
+/* Specular reflection */
+dmnsn_finish *dmnsn_new_reflective_finish(dmnsn_color min, dmnsn_color max,
+ double falloff);
+
#endif /* DIMENSION_FINISHES_H */
diff --git a/libdimension/dimension/scene.h b/libdimension/dimension/scene.h
index d873a6d..2064792 100644
--- a/libdimension/dimension/scene.h
+++ b/libdimension/dimension/scene.h
@@ -31,6 +31,7 @@ typedef enum {
DMNSN_RENDER_LIGHTS = 1 << 1,
DMNSN_RENDER_FINISH = 1 << 2,
DMNSN_RENDER_TRANSLUCENCY = 1 << 3,
+ DMNSN_RENDER_REFLECTION = 1 << 4,
DMNSN_RENDER_FULL = ~DMNSN_RENDER_NONE
} dmnsn_quality;
diff --git a/libdimension/dimension/texture.h b/libdimension/dimension/texture.h
index c5f4b3f..4b14918 100644
--- a/libdimension/dimension/texture.h
+++ b/libdimension/dimension/texture.h
@@ -63,13 +63,17 @@ typedef dmnsn_color dmnsn_finish_fn(const dmnsn_finish *finish,
dmnsn_vector viewer);
typedef dmnsn_color dmnsn_ambient_fn(const dmnsn_finish *finish,
dmnsn_color pigment);
+typedef dmnsn_color dmnsn_reflection_fn(const dmnsn_finish *finish,
+ dmnsn_color reflect, dmnsn_color color,
+ dmnsn_vector ray, dmnsn_vector normal);
/* dmnsn_finish definition */
struct dmnsn_finish {
/* Callbacks */
- dmnsn_finish_fn *finish_fn;
- dmnsn_ambient_fn *ambient_fn;
- dmnsn_free_fn *free_fn;
+ dmnsn_finish_fn *finish_fn;
+ dmnsn_ambient_fn *ambient_fn;
+ dmnsn_reflection_fn *reflection_fn;
+ dmnsn_free_fn *free_fn;
/* Generic pointer */
void *ptr;
diff --git a/libdimension/finish_combination.c b/libdimension/finish_combination.c
index 045db96..bcfc1c9 100644
--- a/libdimension/finish_combination.c
+++ b/libdimension/finish_combination.c
@@ -34,10 +34,10 @@ dmnsn_finish_combination_fn(const dmnsn_finish *finish,
{
dmnsn_finish **params = finish->ptr;
if (params[0]->finish_fn && params[1]->finish_fn) {
- return dmnsn_color_add((*params[0]->finish_fn)(params[0], light, color, ray,
- normal, viewer),
- (*params[1]->finish_fn)(params[1], light, color, ray,
- normal, viewer));
+ return dmnsn_color_add(
+ (*params[0]->finish_fn)(params[0], light, color, ray, normal, viewer),
+ (*params[1]->finish_fn)(params[1], light, color, ray, normal, viewer)
+ );
} else if (params[0]->finish_fn) {
return (*params[0]->finish_fn)(params[0], light, color, ray,
normal, viewer);
@@ -66,6 +66,26 @@ dmnsn_finish_combination_ambient_fn(const dmnsn_finish *finish,
}
}
+static dmnsn_color
+dmnsn_finish_combination_reflection_fn(const dmnsn_finish *finish,
+ dmnsn_color reflect, dmnsn_color color,
+ dmnsn_vector ray, dmnsn_vector normal)
+{
+ dmnsn_finish **params = finish->ptr;
+ if (params[0]->reflection_fn && params[1]->reflection_fn) {
+ return dmnsn_color_add(
+ (*params[0]->reflection_fn)(params[0], reflect, color, ray, normal),
+ (*params[1]->reflection_fn)(params[1], reflect, color, ray, normal)
+ );
+ } else if (params[0]->reflection_fn) {
+ return (*params[0]->reflection_fn)(params[0], reflect, color, ray, normal);
+ } else if (params[1]->reflection_fn) {
+ return (*params[1]->reflection_fn)(params[1], reflect, color, ray, normal);
+ } else {
+ return dmnsn_black;
+ }
+}
+
static void
dmnsn_finish_combination_free_fn(void *ptr)
{
@@ -93,8 +113,16 @@ dmnsn_new_finish_combination(dmnsn_finish *f1, dmnsn_finish *f2)
params[1] = f2;
finish->ptr = params;
- finish->finish_fn = &dmnsn_finish_combination_fn;
- finish->ambient_fn = &dmnsn_finish_combination_ambient_fn;
+
+ if (f1->finish_fn || f2->finish_fn)
+ finish->finish_fn = &dmnsn_finish_combination_fn;
+
+ if (f1->ambient_fn || f2->ambient_fn)
+ finish->ambient_fn = &dmnsn_finish_combination_ambient_fn;
+
+ if (f1->reflection_fn || f2->reflection_fn)
+ finish->reflection_fn = &dmnsn_finish_combination_reflection_fn;
+
finish->free_fn = &dmnsn_finish_combination_free_fn;
return finish;
diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c
index 2df267f..9cfb904 100644
--- a/libdimension/raytrace.c
+++ b/libdimension/raytrace.c
@@ -220,6 +220,8 @@ typedef struct dmnsn_raytrace_state {
const dmnsn_intersection *intersection;
dmnsn_kD_splay_tree *kD_splay_tree;
unsigned int level;
+
+ dmnsn_color pigment;
} dmnsn_raytrace_state;
/* Main helper for dmnsn_raytrace_scene_impl - shoot a ray */
@@ -283,12 +285,9 @@ dmnsn_line_add_epsilon(dmnsn_line l)
);
}
-static dmnsn_color
-dmnsn_raytrace_pigment(const dmnsn_raytrace_state *state)
+static const dmnsn_pigment *
+dmnsn_raytrace_get_pigment(const dmnsn_raytrace_state *state)
{
- /* Default to black if there's no texture/pigment */
- dmnsn_color color = dmnsn_black;
-
/* Use the default texture if given a NULL texture */
const dmnsn_texture *texture
= state->intersection->texture ? state->intersection->texture
@@ -296,16 +295,42 @@ dmnsn_raytrace_pigment(const dmnsn_raytrace_state *state)
if (texture) {
/* Use the default pigment if given a NULL pigment */
- const dmnsn_pigment *pigment
- = texture->pigment ? texture->pigment
- : state->scene->default_texture->pigment;
-
- if (pigment) {
- color = (*pigment->pigment_fn)(
- pigment,
- dmnsn_line_point(state->intersection->ray, state->intersection->t)
- );
- }
+ return texture->pigment ? texture->pigment
+ : state->scene->default_texture->pigment;
+ } else {
+ return NULL;
+ }
+}
+
+static const dmnsn_finish *
+dmnsn_raytrace_get_finish(const dmnsn_raytrace_state *state)
+{
+ /* Use the default texture if given a NULL texture */
+ const dmnsn_texture *texture
+ = state->intersection->texture ? state->intersection->texture
+ : state->scene->default_texture;
+
+ if (texture) {
+ /* Use the default finish if given a NULL finish */
+ return texture->finish ? texture->finish
+ : state->scene->default_texture->finish;
+ } else {
+ return NULL;
+ }
+}
+
+static dmnsn_color
+dmnsn_raytrace_pigment(const dmnsn_raytrace_state *state)
+{
+ /* Default to black if there's no texture/pigment */
+ dmnsn_color color = dmnsn_black;
+
+ const dmnsn_pigment *pigment = dmnsn_raytrace_get_pigment(state);
+ if (pigment) {
+ color = (*pigment->pigment_fn)(
+ pigment,
+ dmnsn_line_point(state->intersection->ray, state->intersection->t)
+ );
}
return color;
@@ -366,24 +391,14 @@ dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state,
}
static dmnsn_color
-dmnsn_raytrace_lighting(const dmnsn_raytrace_state *state, dmnsn_color color)
+dmnsn_raytrace_lighting(const dmnsn_raytrace_state *state)
{
- /* Use the default texture if given a NULL texture */
- const dmnsn_texture *texture
- = state->intersection->texture ? state->intersection->texture
- : state->scene->default_texture;
-
- const dmnsn_finish *finish = NULL;
- if (texture) {
- /* Use the default finish if given a NULL finish */
- finish = texture->finish ? texture->finish
- : state->scene->default_texture->finish;
- }
+ const dmnsn_finish *finish = dmnsn_raytrace_get_finish(state);
/* The illuminated color */
dmnsn_color illum = dmnsn_black;
if (finish && finish->ambient_fn)
- illum = (*finish->ambient_fn)(finish, color);
+ illum = (*finish->ambient_fn)(finish, state->pigment);
dmnsn_vector x0 = dmnsn_line_point(state->intersection->ray,
state->intersection->t);
@@ -410,11 +425,11 @@ dmnsn_raytrace_lighting(const dmnsn_raytrace_state *state, dmnsn_color color)
/* Get this light's color contribution to the object */
dmnsn_color contrib = (*finish->finish_fn)(finish,
- light_color, color,
+ light_color, state->pigment,
ray, normal, viewer);
illum = dmnsn_color_add(contrib, illum);
} else {
- illum = color;
+ illum = state->pigment;
}
}
}
@@ -423,12 +438,46 @@ dmnsn_raytrace_lighting(const dmnsn_raytrace_state *state, dmnsn_color color)
}
static dmnsn_color
+dmnsn_raytrace_reflection(const dmnsn_raytrace_state *state, dmnsn_color color)
+{
+ const dmnsn_finish *finish = dmnsn_raytrace_get_finish(state);
+ dmnsn_color refl = color;
+
+ if (finish && finish->reflection_fn) {
+ dmnsn_vector normal = state->intersection->normal;
+ dmnsn_vector viewer = dmnsn_vector_normalize(
+ dmnsn_vector_negate(state->intersection->ray.n)
+ );
+ dmnsn_vector ray = dmnsn_vector_sub(
+ dmnsn_vector_mul(2*dmnsn_vector_dot(viewer, normal), normal),
+ viewer
+ );
+
+ dmnsn_line refl_ray = dmnsn_new_line(
+ dmnsn_line_point(state->intersection->ray, state->intersection->t),
+ ray
+ );
+ refl_ray = dmnsn_line_add_epsilon(refl_ray);
+
+ dmnsn_raytrace_state recursive_state = *state;
+ dmnsn_color rec = dmnsn_raytrace_shoot(&recursive_state, refl_ray);
+ refl = dmnsn_color_add(
+ (*finish->reflection_fn)(finish, rec, state->pigment, ray, normal),
+ refl
+ );
+ }
+
+ return refl;
+}
+
+static dmnsn_color
dmnsn_raytrace_translucency(const dmnsn_raytrace_state *state,
- dmnsn_color color, dmnsn_color pigment)
+ dmnsn_color color)
{
dmnsn_color trans = color;
- if (pigment.filter || pigment.trans) {
- trans = dmnsn_color_mul(1.0 - pigment.filter - pigment.trans, color);
+ if (state->pigment.filter || state->pigment.trans) {
+ trans = dmnsn_color_mul(1.0 - state->pigment.filter - state->pigment.trans,
+ color);
dmnsn_line trans_ray = dmnsn_new_line(
dmnsn_line_point(state->intersection->ray, state->intersection->t),
@@ -438,7 +487,7 @@ dmnsn_raytrace_translucency(const dmnsn_raytrace_state *state,
dmnsn_raytrace_state recursive_state = *state;
dmnsn_color rec = dmnsn_raytrace_shoot(&recursive_state, trans_ray);
- dmnsn_color filtered = dmnsn_color_filter(rec, pigment);
+ dmnsn_color filtered = dmnsn_color_filter(rec, state->pigment);
trans = dmnsn_color_add(trans, filtered);
}
return trans;
@@ -459,26 +508,33 @@ dmnsn_raytrace_shoot(dmnsn_raytrace_state *state, dmnsn_line ray)
if (intersection) {
state->intersection = intersection;
- /* Get the pigment of the object */
- dmnsn_color pigment = dmnsn_black;
+ /* Pigment */
+ state->pigment = dmnsn_black;
if (state->scene->quality & DMNSN_RENDER_PIGMENT) {
- pigment = dmnsn_raytrace_pigment(state);
+ state->pigment = dmnsn_raytrace_pigment(state);
}
- color = pigment;
+ color = state->pigment;
- /* Account for finishes and shadows */
- dmnsn_color illum = pigment;
+ /* Finishes and shadows */
+ dmnsn_color illum = color;
if (state->scene->quality & DMNSN_RENDER_LIGHTS) {
- illum = dmnsn_raytrace_lighting(state, illum);
+ illum = dmnsn_raytrace_lighting(state);
}
color = illum;
- /* Account for translucency */
- dmnsn_color trans = illum;
+ /* Reflection */
+ dmnsn_color refl = illum;
+ if (state->scene->quality & DMNSN_RENDER_REFLECTION) {
+ refl = dmnsn_raytrace_reflection(state, refl);
+ }
+ color = refl;
+
+ /* Translucency */
+ dmnsn_color trans = color;
trans.filter = 0.0;
trans.trans = 0.0;
if (state->scene->quality & DMNSN_RENDER_TRANSLUCENCY) {
- trans = dmnsn_raytrace_translucency(state, trans, pigment);
+ trans = dmnsn_raytrace_translucency(state, trans);
}
color = trans;
diff --git a/libdimension/reflective.c b/libdimension/reflective.c
new file mode 100644
index 0000000..f1c7355
--- /dev/null
+++ b/libdimension/reflective.c
@@ -0,0 +1,74 @@
+/*************************************************************************
+ * Copyright (C) 2009 Tavian Barnes <tavianator@gmail.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library is distributed in the hope that it will be *
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty *
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "dimension.h"
+#include <stdlib.h> /* For malloc */
+#include <math.h>
+
+/*
+ * Reflective finish
+ */
+
+typedef struct dmnsn_reflection_params {
+ dmnsn_color min, max;
+ double falloff;
+} dmnsn_reflection_params;
+
+static dmnsn_color
+dmnsn_reflective_finish_fn(const dmnsn_finish *finish,
+ dmnsn_color reflect, dmnsn_color color,
+ dmnsn_vector ray, dmnsn_vector normal)
+{
+ dmnsn_reflection_params *params = finish->ptr;
+ double reflection = pow(dmnsn_vector_dot(ray, normal), params->falloff);
+
+ return dmnsn_color_illuminate(
+ dmnsn_color_add(
+ dmnsn_color_mul(
+ reflection,
+ dmnsn_color_sub(params->max, params->min)
+ ),
+ params->min
+ ),
+ reflect
+ );
+}
+
+dmnsn_finish *
+dmnsn_new_reflective_finish(dmnsn_color min, dmnsn_color max, double falloff)
+{
+ dmnsn_finish *finish = dmnsn_new_finish();
+ if (finish) {
+ dmnsn_reflection_params *params = malloc(sizeof(dmnsn_reflection_params));
+ if (!params) {
+ dmnsn_delete_finish(finish);
+ return NULL;
+ }
+
+ params->min = min;
+ params->max = max;
+ params->falloff = falloff;
+
+ finish->ptr = params;
+ finish->reflection_fn = &dmnsn_reflective_finish_fn;
+ finish->free_fn = &free;
+ }
+ return finish;
+}
diff --git a/libdimension/texture.c b/libdimension/texture.c
index 5fdd708..5878a02 100644
--- a/libdimension/texture.c
+++ b/libdimension/texture.c
@@ -50,9 +50,10 @@ dmnsn_new_finish()
{
dmnsn_finish *finish = malloc(sizeof(dmnsn_finish));
if (finish) {
- finish->finish_fn = NULL;
- finish->ambient_fn = NULL;
- finish->free_fn = NULL;
+ finish->finish_fn = NULL;
+ finish->ambient_fn = NULL;
+ finish->reflection_fn = NULL;
+ finish->free_fn = NULL;
}
return finish;
}
diff --git a/tests/libdimension/tests.c b/tests/libdimension/tests.c
index 54f506a..52a9fda 100644
--- a/tests/libdimension/tests.c
+++ b/tests/libdimension/tests.c
@@ -29,14 +29,16 @@ dmnsn_new_default_scene()
}
/* Default finish */
+ scene->default_texture->finish = dmnsn_new_ambient_finish(
+ dmnsn_color_mul(0.1, dmnsn_white)
+ );
+ scene->default_texture->finish = dmnsn_new_finish_combination(
+ dmnsn_new_diffuse_finish(0.6),
+ scene->default_texture->finish
+ );
scene->default_texture->finish = dmnsn_new_finish_combination(
- dmnsn_new_finish_combination(
- dmnsn_new_ambient_finish(
- dmnsn_color_mul(0.1, dmnsn_white)
- ),
- dmnsn_new_diffuse_finish(0.6)
- ),
- dmnsn_new_phong_finish(0.2, 40.0)
+ dmnsn_new_phong_finish(0.2, 40.0),
+ scene->default_texture->finish
);
if (!scene->default_texture->finish) {
dmnsn_delete_scene(scene);