summaryrefslogtreecommitdiff
path: root/src/modules/ray/ray_render.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/ray/ray_render.c')
-rw-r--r--src/modules/ray/ray_render.c242
1 files changed, 242 insertions, 0 deletions
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 <stdlib.h>
+#include <math.h>
+
+#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);
+}
© All Rights Reserved