From 6787a9219748575cd63aaadd483f49a4fd83b543 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 9 Jan 2010 20:38:51 -0500 Subject: Implement reflection. --- libdimension/Makefile.am | 1 + libdimension/color.c | 27 ++++++- libdimension/dimension/color.h | 1 + libdimension/dimension/finishes.h | 4 ++ libdimension/dimension/scene.h | 1 + libdimension/dimension/texture.h | 10 ++- libdimension/finish_combination.c | 40 +++++++++-- libdimension/raytrace.c | 144 ++++++++++++++++++++++++++------------ libdimension/reflective.c | 74 ++++++++++++++++++++ libdimension/texture.c | 7 +- 10 files changed, 252 insertions(+), 57 deletions(-) create mode 100644 libdimension/reflective.c (limited to 'libdimension') 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; } } } @@ -422,13 +437,47 @@ dmnsn_raytrace_lighting(const dmnsn_raytrace_state *state, dmnsn_color color) return illum; } +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 * + * * + * 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 * + * . * + *************************************************************************/ + +#include "dimension.h" +#include /* For malloc */ +#include + +/* + * 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; } -- cgit v1.2.3