From 141732e737eb7e9a1677fc3b5aa9ebcd66de3aca Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 30 May 2011 23:24:36 -0600 Subject: Make lights more generic to support directional lights. --- libdimension/dimension/light.h | 35 ++++++++++++++++++++++++++++------ libdimension/dimension/scene.h | 7 +++++++ libdimension/light.c | 11 +++++++---- libdimension/point_light.c | 43 +++++++++++++++++++++++++++++++++--------- libdimension/raytrace.c | 28 +++++++++++++-------------- libdimension/scene.c | 7 +++++++ libdimension/tests/render.c | 2 +- 7 files changed, 99 insertions(+), 34 deletions(-) diff --git a/libdimension/dimension/light.h b/libdimension/dimension/light.h index 03de641..b41a524 100644 --- a/libdimension/dimension/light.h +++ b/libdimension/dimension/light.h @@ -23,27 +23,50 @@ * Lights. */ +#include + /* Forward-declar dmnsn_light */ typedef struct dmnsn_light dmnsn_light; /** - * Light callback. + * Light direction callback. + * @param[in] light The light itself. + * @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); + +/** + * Light illumination callback. * @param[in] light The light itself. * @param[in] v The point to illuminate. * @return The color of the light at \p v. */ -typedef dmnsn_color dmnsn_light_fn(const dmnsn_light *light, dmnsn_vector v); +typedef dmnsn_color dmnsn_light_illumination_fn(const dmnsn_light *light, + dmnsn_vector v); + +/** + * Light shadow callback. + * @param[in] light The light itself. + * @param[in] t The line index of the closest shadow ray intersection. + * @return Whether the point is in shadow. + */ +typedef bool dmnsn_light_shadow_fn(const dmnsn_light *light, double t); /** A light. */ struct dmnsn_light { - dmnsn_vector x0; /**< Origin of light rays. */ - /* Callbacks */ - dmnsn_light_fn *light_fn; /**< Light callback. */ - dmnsn_free_fn *free_fn; /**< Desctructor callback. */ + dmnsn_light_direction_fn *direction_fn; /**< Direction callback. */ + dmnsn_light_illumination_fn *illumination_fn; /**< Illumination callback. */ + dmnsn_light_shadow_fn *shadow_fn; /**< Shadow callback. */ + dmnsn_free_fn *free_fn; /**< Desctructor callback. */ /** Generic pointer for light info. */ void *ptr; + + /** @internal Reference count. */ + dmnsn_refcount refcount; }; /** diff --git a/libdimension/dimension/scene.h b/libdimension/dimension/scene.h index b726a77..bb47ee9 100644 --- a/libdimension/dimension/scene.h +++ b/libdimension/dimension/scene.h @@ -99,6 +99,13 @@ void dmnsn_initialize_scene(dmnsn_scene *scene); */ void dmnsn_scene_set_canvas(dmnsn_scene *scene, dmnsn_canvas *canvas); +/** + * Add a light to a scene. + * @param[in,out] scene The scene to which to add the light. + * @param[in] object The object to light. + */ +void dmnsn_scene_add_light(dmnsn_scene *scene, dmnsn_light *light); + /** * Set the camera for a scene. * @param[in,out] scene The scene for which to set the canvas. diff --git a/libdimension/light.c b/libdimension/light.c index e1a060c..1103727 100644 --- a/libdimension/light.c +++ b/libdimension/light.c @@ -31,9 +31,12 @@ dmnsn_light * dmnsn_new_light(void) { dmnsn_light *light = dmnsn_malloc(sizeof(dmnsn_light)); - light->light_fn = NULL; - light->free_fn = NULL; - light->ptr = NULL; + light->direction_fn = NULL; + light->illumination_fn = NULL; + light->shadow_fn = NULL; + light->free_fn = NULL; + light->ptr = NULL; + light->refcount = 0; return light; } @@ -41,7 +44,7 @@ dmnsn_new_light(void) void dmnsn_delete_light(dmnsn_light *light) { - if (light) { + if (light && DMNSN_DECREF(light)) { if (light->free_fn) { light->free_fn(light->ptr); } diff --git a/libdimension/point_light.c b/libdimension/point_light.c index 8617791..6ecaa24 100644 --- a/libdimension/point_light.c +++ b/libdimension/point_light.c @@ -26,11 +26,33 @@ #include "dimension.h" #include -/** Point light callback. */ +/** Point light payload type. */ +typedef struct dmnsn_point_light_payload { + dmnsn_vector origin; + dmnsn_color color; +} dmnsn_point_light_payload; + +/** Point light direction callback. */ +static dmnsn_line +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)); +} + +/** Point light illumination callback. */ static dmnsn_color -dmnsn_point_light_fn(const dmnsn_light *light, dmnsn_vector v) +dmnsn_point_light_illumination_fn(const dmnsn_light *light, dmnsn_vector v) +{ + dmnsn_point_light_payload *payload = light->ptr; + return payload->color; +} + +/** Point light illumination callback. */ +static bool +dmnsn_point_light_shadow_fn(const dmnsn_light *light, double t) { - return *(dmnsn_color *)light->ptr; + return t < 1.0; } dmnsn_light * @@ -38,13 +60,16 @@ dmnsn_new_point_light(dmnsn_vector x0, dmnsn_color color) { dmnsn_light *light = dmnsn_new_light(); - dmnsn_color *ptr = dmnsn_malloc(sizeof(dmnsn_color)); - *ptr = color; + dmnsn_point_light_payload *payload + = dmnsn_malloc(sizeof(dmnsn_point_light_payload)); + payload->origin = x0; + payload->color = color; + light->ptr = payload; - light->x0 = x0; - light->light_fn = dmnsn_point_light_fn; - light->free_fn = dmnsn_free; - light->ptr = ptr; + light->direction_fn = dmnsn_point_light_direction_fn; + light->illumination_fn = dmnsn_point_light_illumination_fn; + light->shadow_fn = dmnsn_point_light_shadow_fn; + light->free_fn = dmnsn_free; return light; } diff --git a/libdimension/raytrace.c b/libdimension/raytrace.c index deb0080..e2fd61e 100644 --- a/libdimension/raytrace.c +++ b/libdimension/raytrace.c @@ -113,6 +113,7 @@ typedef struct dmnsn_raytrace_state { unsigned int reclevel; dmnsn_vector r; + dmnsn_vector light; dmnsn_vector viewer; dmnsn_vector reflected; @@ -240,11 +241,11 @@ dmnsn_raytrace_pigment(dmnsn_raytrace_state *state) /** Get the color of a light ray at an intersection point. */ static dmnsn_color -dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state, +dmnsn_raytrace_light_ray(dmnsn_raytrace_state *state, const dmnsn_light *light) { - dmnsn_line shadow_ray = dmnsn_new_line(state->r, - dmnsn_vector_sub(light->x0, state->r)); + dmnsn_line shadow_ray = 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); @@ -253,7 +254,7 @@ dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state, * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0) return dmnsn_black; - dmnsn_color color = light->light_fn(light, state->r); + dmnsn_color color = light->illumination_fn(light, state->r); unsigned int reclevel = state->reclevel; while (reclevel > 0 @@ -263,7 +264,7 @@ dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state, bool shadow_casted = dmnsn_prtree_intersection(state->prtree, shadow_ray, &shadow_caster, false); - if (!shadow_casted || shadow_caster.t > 1.0) { + if (!shadow_casted || !light->shadow_fn(light, shadow_caster.t)) { break; } @@ -276,8 +277,10 @@ dmnsn_raytrace_light_ray(const dmnsn_raytrace_state *state, && shadow_state.pigment.trans >= dmnsn_epsilon) { color = dmnsn_filter_light(color, shadow_state.pigment); - shadow_ray.x0 = dmnsn_line_point(shadow_ray, shadow_caster.t); - shadow_ray.n = dmnsn_vector_sub(light->x0, shadow_ray.x0); + 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; @@ -310,19 +313,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) { - dmnsn_vector ray = dmnsn_vector_normalized( - dmnsn_vector_sub((*light)->x0, state->r) - ); - /* Get this light's color contribution to the object */ dmnsn_color diffuse = TEXTURE_CALLBACK( state, finish, diffuse_fn, dmnsn_black, - light_color, state->pigment, ray, state->intersection->normal + light_color, state->pigment, state->light, + state->intersection->normal ); dmnsn_color specular = TEXTURE_CALLBACK( state, finish, specular_fn, dmnsn_black, - light_color, state->pigment, ray, state->intersection->normal, - state->viewer + light_color, state->pigment, state->light, + state->intersection->normal, state->viewer ); state->diffuse = dmnsn_color_add(diffuse, state->diffuse); state->additional = dmnsn_color_add(specular, state->additional); diff --git a/libdimension/scene.c b/libdimension/scene.c index ec9fdbd..8462203 100644 --- a/libdimension/scene.c +++ b/libdimension/scene.c @@ -91,6 +91,13 @@ dmnsn_scene_set_canvas(dmnsn_scene *scene, dmnsn_canvas *canvas) scene->canvas = canvas; } +void +dmnsn_scene_add_light(dmnsn_scene *scene, dmnsn_light *light) +{ + DMNSN_INCREF(light); + dmnsn_array_push(scene->lights, &light); +} + void dmnsn_scene_set_camera(dmnsn_scene *scene, dmnsn_camera *camera) { diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c index de5b6a4..85742d0 100644 --- a/libdimension/tests/render.c +++ b/libdimension/tests/render.c @@ -88,7 +88,7 @@ dmnsn_new_test_scene(void) dmnsn_new_vector(-15.0, 20.0, 10.0), dmnsn_white ); - dmnsn_array_push(scene->lights, &light); + dmnsn_scene_add_light(scene, light); /* Now make our objects */ -- cgit v1.2.3