From 5c287c99a3fa8f137dc279b2253c628e83786afe Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sun, 10 Dec 2017 12:32:19 -0800 Subject: ray: split scene data from render state This introduces ray_render_t, and ray_render.[ch]. The _prepared member of ray_scene_t has been moved to ray_render_t, and the other _prepared members (e.g. objects) will follow. Up until now I've just been sticking the precomputed state under _prepared members of their associated structures, and simply using convention to enforce anything resembling an api boundary. It's been convenient without being inefficient, but I'd like to move the ray code into more of a reusable library and this wart needs to be addressed. The render state is also where any spatial indexes will be built and maintained, another thing I've been experimenting with. Note most of the churn here is just renaming ray_scene.c to ray_render.c. A nearly global s/ray_scene/ray_render/ has occurred, now that ray_scene_t really only serves as glue to bind objects, lights, and scene-global properties into a cohesive unit. --- src/modules/ray/Makefile.am | 2 +- src/modules/ray/ray.c | 19 +++- src/modules/ray/ray_render.c | 242 +++++++++++++++++++++++++++++++++++++++++++ src/modules/ray/ray_render.h | 15 +++ src/modules/ray/ray_scene.c | 219 --------------------------------------- src/modules/ray/ray_scene.h | 20 +--- 6 files changed, 279 insertions(+), 238 deletions(-) create mode 100644 src/modules/ray/ray_render.c create mode 100644 src/modules/ray/ray_render.h delete mode 100644 src/modules/ray/ray_scene.c (limited to 'src/modules/ray') diff --git a/src/modules/ray/Makefile.am b/src/modules/ray/Makefile.am index 872efb6..a0d7cb9 100644 --- a/src/modules/ray/Makefile.am +++ b/src/modules/ray/Makefile.am @@ -1,4 +1,4 @@ noinst_LIBRARIES = libray.a -libray_a_SOURCES = ray_3f.h ray.c ray_camera.c ray_camera.h ray_color.h ray_euler.c ray_euler.h ray_light_emitter.h ray_object.h ray_object_light.h ray_object_plane.h ray_object_point.h ray_object_sphere.h ray_object_type.h ray_ray.h ray_scene.c ray_scene.h ray_surface.h +libray_a_SOURCES = ray_3f.h ray.c ray_camera.c ray_camera.h ray_color.h ray_euler.c ray_euler.h ray_light_emitter.h ray_object.h ray_object_light.h ray_object_plane.h ray_object_point.h ray_object_sphere.h ray_object_type.h ray_ray.h ray_render.c ray_render.h ray_scene.h ray_surface.h libray_a_CFLAGS = @ROTOTILLER_CFLAGS@ -ffast-math libray_a_CPPFLAGS = @ROTOTILLER_CFLAGS@ -I@top_srcdir@/src diff --git a/src/modules/ray/ray.c b/src/modules/ray/ray.c index 5f6326d..bb9c042 100644 --- a/src/modules/ray/ray.c +++ b/src/modules/ray/ray.c @@ -8,6 +8,7 @@ #include "ray_camera.h" #include "ray_object.h" +#include "ray_render.h" #include "ray_scene.h" /* Copyright (C) 2016-2017 Vito Caputo */ @@ -118,6 +119,7 @@ static float r; typedef struct ray_context_t { + ray_render_t *render; } ray_context_t; @@ -142,6 +144,8 @@ static int ray_fragmenter(void *context, const fb_fragment_t *fragment, unsigned /* prepare a frame for concurrent rendering */ static void ray_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter) { + ray_context_t *ctxt = context; + *res_fragmenter = ray_fragmenter; /* TODO: the camera doesn't need the width and height anymore, the fragment has the frame_width/frame_height */ @@ -169,14 +173,24 @@ static void ray_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fra /* tilt camera pitch in time with up and down movements, phase shifted appreciably */ camera.orientation.pitch = -(sinf((M_PI * 1.5f) + r * 1.3f) * .6f + -.35f); #endif - ray_scene_prepare(&scene, &camera); + ctxt->render = ray_render_new(&scene, &camera); } /* ray trace a simple scene into the fragment */ static void ray_render_fragment(void *context, fb_fragment_t *fragment) { - ray_scene_render_fragment(&scene, fragment); + ray_context_t *ctxt = context; + + ray_render_trace_fragment(ctxt->render, fragment); +} + + +static void ray_finish_frame(void *context, fb_fragment_t *fragment) +{ + ray_context_t *ctxt = context; + + ray_render_free(ctxt->render); } @@ -185,6 +199,7 @@ rototiller_module_t ray_module = { .destroy_context = ray_destroy_context, .prepare_frame = ray_prepare_frame, .render_fragment = ray_render_fragment, + .finish_frame = ray_finish_frame, .name = "ray", .description = "Ray tracer (threaded)", .author = "Vito Caputo ", diff --git a/src/modules/ray/ray_render.c b/src/modules/ray/ray_render.c new file mode 100644 index 0000000..bfa5112 --- /dev/null +++ b/src/modules/ray/ray_render.c @@ -0,0 +1,242 @@ +#include +#include + +#include "fb.h" + +#include "ray_camera.h" +#include "ray_color.h" +#include "ray_object.h" +#include "ray_ray.h" +#include "ray_scene.h" + +#define MAX_RECURSION_DEPTH 4 +#define MIN_RELEVANCE 0.05f + +typedef struct ray_render_t { + const ray_scene_t *scene; /* scene being rendered */ + const ray_camera_t *camera; /* camera rendering the scene */ + + ray_color_t ambient_light; + ray_camera_frame_t frame; +} ray_render_t; + +/* Determine if the ray is obstructed by an object within the supplied distance, for shadows */ +static inline int ray_is_obstructed(ray_render_t *render, unsigned depth, ray_ray_t *ray, float distance) +{ + ray_object_t *object; + + for (object = render->scene->objects; object->type; object++) { + float ood; + + if (ray_object_intersects_ray(object, depth, ray, &ood) && + ood < distance) { + return 1; + } + } + + return 0; +} + + +/* shadow test */ +static inline int point_is_shadowed(ray_render_t *render, unsigned depth, ray_3f_t *light_direction, float distance, ray_3f_t *point) +{ + ray_ray_t shadow_ray; + + shadow_ray.direction = *light_direction; + shadow_ray.origin = *point; + + if (ray_is_obstructed(render, depth + 1, &shadow_ray, distance)) + return 1; + + return 0; +} + + +/* a faster powf() that's good enough for our purposes. + * XXX: note there's a faster technique which exploits the IEEE floating point format: + * https://github.com/ekmett/approximate/blob/master/cbits/fast.c#L185 + */ +static inline float approx_powf(float x, float y) +{ + return expf(y * logf(x)); +} + + +/* Determine the color @ distance on ray on object viewed from origin */ +static inline ray_color_t shade_intersection(ray_render_t *render, ray_object_t *object, ray_ray_t *ray, ray_3f_t *intersection, ray_3f_t *normal, unsigned depth, float *res_reflectivity) +{ + ray_surface_t surface = ray_object_surface(object, intersection); + ray_color_t color = ray_3f_mult(&surface.color, &render->ambient_light); + ray_object_t *light; + + /* visit lights for shadows and illumination */ + for (light = render->scene->lights; light->type; light++) { + ray_3f_t lvec = ray_3f_sub(&light->light.emitter.point.center, intersection); + float ldist = ray_3f_length(&lvec); + float lvec_normal_dot; + + lvec = ray_3f_mult_scalar(&lvec, (1.0f / ldist)); /* normalize lvec */ +#if 1 + if (point_is_shadowed(render, depth, &lvec, ldist, intersection)) + continue; +#endif + lvec_normal_dot = ray_3f_dot(normal, &lvec); + + if (lvec_normal_dot > 0) { +#if 1 + float rvec_lvec_dot = ray_3f_dot(&ray->direction, &lvec); + ray_color_t diffuse; + + diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot); + diffuse = ray_3f_mult_scalar(&diffuse, surface.diffuse); + color = ray_3f_add(&color, &diffuse); + + if (rvec_lvec_dot > 0) { + ray_color_t specular; + + /* FIXME: assumes light is a point for its color */ + specular = ray_3f_mult_scalar(&light->light.emitter.point.surface.color, approx_powf(rvec_lvec_dot, surface.highlight_exponent)); + specular = ray_3f_mult_scalar(&specular, surface.specular); + color = ray_3f_add(&color, &specular); + } +#else + ray_color_t diffuse; + + diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot); + color = ray_3f_add(&color, &diffuse); +#endif + } + } + + /* for now just treat specular as the reflectivity */ + *res_reflectivity = surface.specular; + + return color; +} + + +static inline ray_object_t * find_nearest_intersection(ray_render_t *render, ray_object_t *reflector, ray_ray_t *ray, unsigned depth, float *res_distance) +{ + ray_object_t *nearest_object = NULL; + float nearest_object_distance = INFINITY; + ray_object_t *object; + + for (object = render->scene->objects; object->type; object++) { + float distance; + + /* Don't bother checking if a reflected ray intersects the object reflecting it, + * reflector = NULL for primary rays, which will never compare as true here. */ + if (object == reflector) + continue; + + /* Does this ray intersect object? */ + if (ray_object_intersects_ray(object, depth, ray, &distance)) { + /* Is it the nearest intersection? */ + if (distance < nearest_object_distance) { + nearest_object = object; + nearest_object_distance = distance; + } + } + } + + if (nearest_object) + *res_distance = nearest_object_distance; + + return nearest_object; +} + + +static inline ray_color_t trace_ray(ray_render_t *render, ray_ray_t *primary_ray) +{ + ray_color_t color = { .x = 0.0f, .y = 0.0f, .z = 0.0f }; + ray_3f_t intersection, normal; + ray_object_t *reflector = NULL; + float relevance = 1.0f, reflectivity; + unsigned depth = 0; + ray_ray_t reflected_ray, *ray = primary_ray; + + do { + ray_object_t *nearest_object; + float nearest_distance; + + if (reflector) { + float dot = ray_3f_dot(&ray->direction, &normal); + ray_3f_t new_direction = ray_3f_mult_scalar(&normal, dot * 2.0f); + + new_direction = ray_3f_sub(&ray->direction, &new_direction); + + reflected_ray.origin = intersection; + reflected_ray.direction = new_direction; + + ray = &reflected_ray; + } + + nearest_object = find_nearest_intersection(render, reflector, ray, depth, &nearest_distance); + if (nearest_object) { + ray_3f_t more_color; + ray_3f_t rvec; + + rvec = ray_3f_mult_scalar(&ray->direction, nearest_distance); + intersection = ray_3f_add(&ray->origin, &rvec); + normal = ray_object_normal(nearest_object, &intersection); + + more_color = shade_intersection(render, nearest_object, ray, &intersection, &normal, depth, &reflectivity); + more_color = ray_3f_mult_scalar(&more_color, relevance); + color = ray_3f_add(&color, &more_color); + } + + reflector = nearest_object; + } while (reflector && (++depth < MAX_RECURSION_DEPTH) && (relevance *= reflectivity) >= MIN_RELEVANCE); + + return color; +} + + +void ray_render_trace_fragment(ray_render_t *render, fb_fragment_t *fb_fragment) +{ + uint32_t *buf = fb_fragment->buf; + ray_camera_fragment_t fragment; + ray_ray_t ray; + + ray_camera_fragment_begin(&render->frame, fb_fragment, &ray, &fragment); + do { + do { + *buf = ray_color_to_uint32_rgb(trace_ray(render, &ray)); + buf++; + } while (ray_camera_fragment_x_step(&fragment)); + + buf = ((void *)buf) + fb_fragment->stride; + } while (ray_camera_fragment_y_step(&fragment)); +} + + +/* prepare the scene for rendering with camera, must be called whenever anything in the scene+camera pair has been changed. */ +/* this is basically a time for the raytracer to precompute whatever it can which otherwise ends up occurring per-ray */ +/* the camera is included so primary rays which all have a common origin may be optimized for */ +ray_render_t * ray_render_new(ray_scene_t *scene, ray_camera_t *camera) +{ + ray_render_t *render; + ray_object_t *object; + + render = malloc(sizeof(ray_render_t)); + if (!render) + return NULL; + + render->scene = scene; + render->camera = camera; + + render->ambient_light = ray_3f_mult_scalar(&scene->ambient_color, scene->ambient_brightness); + ray_camera_frame_prepare(camera, &render->frame); + + for (object = scene->objects; object->type; object++) + ray_object_prepare(object, camera); + + return render; +} + + +void ray_render_free(ray_render_t *render) +{ + free(render); +} diff --git a/src/modules/ray/ray_render.h b/src/modules/ray/ray_render.h new file mode 100644 index 0000000..a6d6c67 --- /dev/null +++ b/src/modules/ray/ray_render.h @@ -0,0 +1,15 @@ +#ifndef _RAY_RENDER_H +#define _RAY_RENDER_H + +#include "fb.h" + +#include "ray_camera.h" +#include "ray_scene.h" + +typedef struct ray_render_t ray_render_t; + +ray_render_t * ray_render_new(ray_scene_t *scene, ray_camera_t *camera); +void ray_render_free(ray_render_t *render); +void ray_render_trace_fragment(ray_render_t *render, fb_fragment_t *fb_fragment); + +#endif diff --git a/src/modules/ray/ray_scene.c b/src/modules/ray/ray_scene.c deleted file mode 100644 index 21a3bfa..0000000 --- a/src/modules/ray/ray_scene.c +++ /dev/null @@ -1,219 +0,0 @@ -#include -#include - -#include "fb.h" - -#include "ray_camera.h" -#include "ray_color.h" -#include "ray_object.h" -#include "ray_ray.h" -#include "ray_scene.h" - -#define MAX_RECURSION_DEPTH 4 -#define MIN_RELEVANCE 0.05f - - -/* Determine if the ray is obstructed by an object within the supplied distance, for shadows */ -static inline int ray_is_obstructed(ray_scene_t *scene, unsigned depth, ray_ray_t *ray, float distance) -{ - ray_object_t *object; - - for (object = scene->objects; object->type; object++) { - float ood; - - if (ray_object_intersects_ray(object, depth, ray, &ood) && - ood < distance) { - return 1; - } - } - - return 0; -} - - -/* shadow test */ -static inline int point_is_shadowed(ray_scene_t *scene, unsigned depth, ray_3f_t *light_direction, float distance, ray_3f_t *point) -{ - ray_ray_t shadow_ray; - - shadow_ray.direction = *light_direction; - shadow_ray.origin = *point; - - if (ray_is_obstructed(scene, depth + 1, &shadow_ray, distance)) - return 1; - - return 0; -} - - -/* a faster powf() that's good enough for our purposes. - * XXX: note there's a faster technique which exploits the IEEE floating point format: - * https://github.com/ekmett/approximate/blob/master/cbits/fast.c#L185 - */ -static inline float approx_powf(float x, float y) -{ - return expf(y * logf(x)); -} - - -/* Determine the color @ distance on ray on object viewed from origin */ -static inline ray_color_t shade_intersection(ray_scene_t *scene, ray_object_t *object, ray_ray_t *ray, ray_3f_t *intersection, ray_3f_t *normal, unsigned depth, float *res_reflectivity) -{ - ray_surface_t surface = ray_object_surface(object, intersection); - ray_color_t color = ray_3f_mult(&surface.color, &scene->_prepared.ambient_light); - ray_object_t *light; - - /* visit lights for shadows and illumination */ - for (light = scene->lights; light->type; light++) { - ray_3f_t lvec = ray_3f_sub(&light->light.emitter.point.center, intersection); - float ldist = ray_3f_length(&lvec); - float lvec_normal_dot; - - lvec = ray_3f_mult_scalar(&lvec, (1.0f / ldist)); /* normalize lvec */ -#if 1 - if (point_is_shadowed(scene, depth, &lvec, ldist, intersection)) - continue; -#endif - lvec_normal_dot = ray_3f_dot(normal, &lvec); - - if (lvec_normal_dot > 0) { -#if 1 - float rvec_lvec_dot = ray_3f_dot(&ray->direction, &lvec); - ray_color_t diffuse; - - diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot); - diffuse = ray_3f_mult_scalar(&diffuse, surface.diffuse); - color = ray_3f_add(&color, &diffuse); - - if (rvec_lvec_dot > 0) { - ray_color_t specular; - - /* FIXME: assumes light is a point for its color */ - specular = ray_3f_mult_scalar(&light->light.emitter.point.surface.color, approx_powf(rvec_lvec_dot, surface.highlight_exponent)); - specular = ray_3f_mult_scalar(&specular, surface.specular); - color = ray_3f_add(&color, &specular); - } -#else - ray_color_t diffuse; - - diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot); - color = ray_3f_add(&color, &diffuse); -#endif - } - } - - /* for now just treat specular as the reflectivity */ - *res_reflectivity = surface.specular; - - return color; -} - - -static inline ray_object_t * find_nearest_intersection(ray_scene_t *scene, ray_object_t *reflector, ray_ray_t *ray, unsigned depth, float *res_distance) -{ - ray_object_t *nearest_object = NULL; - float nearest_object_distance = INFINITY; - ray_object_t *object; - - for (object = scene->objects; object->type; object++) { - float distance; - - /* Don't bother checking if a reflected ray intersects the object reflecting it, - * reflector = NULL for primary rays, which will never compare as true here. */ - if (object == reflector) - continue; - - /* Does this ray intersect object? */ - if (ray_object_intersects_ray(object, depth, ray, &distance)) { - /* Is it the nearest intersection? */ - if (distance < nearest_object_distance) { - nearest_object = object; - nearest_object_distance = distance; - } - } - } - - if (nearest_object) - *res_distance = nearest_object_distance; - - return nearest_object; -} - - -static inline ray_color_t trace_ray(ray_scene_t *scene, ray_ray_t *primary_ray) -{ - ray_color_t color = { .x = 0.0f, .y = 0.0f, .z = 0.0f }; - ray_3f_t intersection, normal; - ray_object_t *reflector = NULL; - float relevance = 1.0f, reflectivity; - unsigned depth = 0; - ray_ray_t reflected_ray, *ray = primary_ray; - - do { - ray_object_t *nearest_object; - float nearest_distance; - - if (reflector) { - float dot = ray_3f_dot(&ray->direction, &normal); - ray_3f_t new_direction = ray_3f_mult_scalar(&normal, dot * 2.0f); - - new_direction = ray_3f_sub(&ray->direction, &new_direction); - - reflected_ray.origin = intersection; - reflected_ray.direction = new_direction; - - ray = &reflected_ray; - } - - nearest_object = find_nearest_intersection(scene, reflector, ray, depth, &nearest_distance); - if (nearest_object) { - ray_3f_t more_color; - ray_3f_t rvec; - - rvec = ray_3f_mult_scalar(&ray->direction, nearest_distance); - intersection = ray_3f_add(&ray->origin, &rvec); - normal = ray_object_normal(nearest_object, &intersection); - - more_color = shade_intersection(scene, nearest_object, ray, &intersection, &normal, depth, &reflectivity); - more_color = ray_3f_mult_scalar(&more_color, relevance); - color = ray_3f_add(&color, &more_color); - } - - reflector = nearest_object; - } while (reflector && (++depth < MAX_RECURSION_DEPTH) && (relevance *= reflectivity) >= MIN_RELEVANCE); - - return color; -} - - -void ray_scene_render_fragment(ray_scene_t *scene, fb_fragment_t *fb_fragment) -{ - uint32_t *buf = fb_fragment->buf; - ray_camera_fragment_t fragment; - ray_ray_t ray; - - ray_camera_fragment_begin(&scene->_prepared.frame, fb_fragment, &ray, &fragment); - do { - do { - *buf = ray_color_to_uint32_rgb(trace_ray(scene, &ray)); - buf++; - } while (ray_camera_fragment_x_step(&fragment)); - - buf = ((void *)buf) + fb_fragment->stride; - } while (ray_camera_fragment_y_step(&fragment)); -} - - -/* prepare the scene for rendering with camera, must be called whenever anything in the scene+camera pair has been changed. */ -/* this is basically a time for the raytracer to precompute whatever it can which otherwise ends up occurring per-ray */ -/* the camera is included so primary rays which all have a common origin may be optimized for */ -void ray_scene_prepare(ray_scene_t *scene, ray_camera_t *camera) -{ - ray_object_t *object; - - scene->_prepared.ambient_light = ray_3f_mult_scalar(&scene->ambient_color, scene->ambient_brightness); - ray_camera_frame_prepare(camera, &scene->_prepared.frame); - - for (object = scene->objects; object->type; object++) - ray_object_prepare(object, camera); -} diff --git a/src/modules/ray/ray_scene.h b/src/modules/ray/ray_scene.h index 5c7cd5e..ff9c440 100644 --- a/src/modules/ray/ray_scene.h +++ b/src/modules/ray/ray_scene.h @@ -1,28 +1,16 @@ #ifndef _RAY_SCENE_H #define _RAY_SCENE_H -#include "fb.h" - -#include "ray_camera.h" #include "ray_color.h" -#include "ray_ray.h" typedef union ray_object_t ray_object_t; typedef struct ray_scene_t { - ray_object_t *objects; - ray_object_t *lights; - - ray_color_t ambient_color; - float ambient_brightness; + ray_object_t *objects; + ray_object_t *lights; - struct { - ray_color_t ambient_light; - ray_camera_frame_t frame; - } _prepared; + ray_color_t ambient_color; + float ambient_brightness; } ray_scene_t; -void ray_scene_prepare(ray_scene_t *scene, ray_camera_t *camera); -void ray_scene_render_fragment(ray_scene_t *scene, fb_fragment_t *fb_fragment); - #endif -- cgit v1.2.1