diff options
| author | Vito Caputo <vcaputo@gnugeneration.com> | 2017-01-18 19:12:41 -0800 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-01-18 19:12:41 -0800 | 
| commit | 467137113c8b3d6bcb73ecff8c76f23793f25cb7 (patch) | |
| tree | ecf3064d6587ec875d5c021d46d44855dc814212 /src/modules/ray | |
| parent | ee2073d4e411555aba878277131b56f7eb562c84 (diff) | |
| parent | 404a356b2b22a134aea151145d1baabf253ee491 (diff) | |
Merge build system cleanups
- Move source to src/ subdir
- Use $(top_srcdir)/src instead of ../../
Diffstat (limited to 'src/modules/ray')
| -rw-r--r-- | src/modules/ray/Makefile.am | 4 | ||||
| -rw-r--r-- | src/modules/ray/ray.c | 161 | ||||
| -rw-r--r-- | src/modules/ray/ray.h | 8 | ||||
| -rw-r--r-- | src/modules/ray/ray_3f.h | 161 | ||||
| -rw-r--r-- | src/modules/ray/ray_camera.c | 85 | ||||
| -rw-r--r-- | src/modules/ray/ray_camera.h | 77 | ||||
| -rw-r--r-- | src/modules/ray/ray_color.h | 29 | ||||
| -rw-r--r-- | src/modules/ray/ray_euler.h | 45 | ||||
| -rw-r--r-- | src/modules/ray/ray_light_emitter.h | 18 | ||||
| -rw-r--r-- | src/modules/ray/ray_object.c | 74 | ||||
| -rw-r--r-- | src/modules/ray/ray_object.h | 24 | ||||
| -rw-r--r-- | src/modules/ray/ray_object_light.h | 67 | ||||
| -rw-r--r-- | src/modules/ray/ray_object_plane.h | 46 | ||||
| -rw-r--r-- | src/modules/ray/ray_object_point.h | 37 | ||||
| -rw-r--r-- | src/modules/ray/ray_object_sphere.h | 65 | ||||
| -rw-r--r-- | src/modules/ray/ray_object_type.h | 11 | ||||
| -rw-r--r-- | src/modules/ray/ray_ray.h | 11 | ||||
| -rw-r--r-- | src/modules/ray/ray_scene.c | 188 | ||||
| -rw-r--r-- | src/modules/ray/ray_scene.h | 27 | ||||
| -rw-r--r-- | src/modules/ray/ray_surface.h | 14 | ||||
| -rw-r--r-- | src/modules/ray/ray_threads.c | 111 | ||||
| -rw-r--r-- | src/modules/ray/ray_threads.h | 30 | 
22 files changed, 1293 insertions, 0 deletions
diff --git a/src/modules/ray/Makefile.am b/src/modules/ray/Makefile.am new file mode 100644 index 0000000..6c9a50a --- /dev/null +++ b/src/modules/ray/Makefile.am @@ -0,0 +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.h ray.h ray_light_emitter.h ray_object.c 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 ray_threads.c ray_threads.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 new file mode 100644 index 0000000..60d08cf --- /dev/null +++ b/src/modules/ray/ray.c @@ -0,0 +1,161 @@ +#include <stdint.h> +#include <inttypes.h> +#include <math.h> + +#include "fb.h" +#include "rototiller.h" +#include "util.h" + +#include "ray_camera.h" +#include "ray_object.h" +#include "ray_scene.h" +#include "ray_threads.h" + +/* Copyright (C) 2016 Vito Caputo <vcaputo@pengaru.com> */ + +/* ray trace a simple scene into the fragment */ +static void ray(fb_fragment_t *fragment) +{ +	static ray_object_t	objects[] = { +		{ +			.plane = { +				.type = RAY_OBJECT_TYPE_PLANE, +				.surface = { +					.color = { .x = 0.4, .y = 0.2, .z = 0.5 }, +					.diffuse = 1.0f, +					.specular = 0.2f, +				}, +				.normal = { .x = 0.0, .y = 1.0, .z = 0.0 }, +				.distance = -3.2f, +			} +		}, { +			.sphere = { +				.type = RAY_OBJECT_TYPE_SPHERE, +				.surface = { +					.color = { .x = 1.0, .y = 0.0, .z = 0.0 }, +					.diffuse = 1.0f, +					.specular = 0.05f, +				}, +				.center = { .x = 0.5, .y = 1.0, .z = 0.0 }, +				.radius = 1.2f, +			} +		}, { +			.sphere = { +				.type = RAY_OBJECT_TYPE_SPHERE, +				.surface = { +					.color = { .x = 0.0, .y = 0.0, .z = 1.0 }, +					.diffuse = 1.0f, +					.specular = 1.0f, +				}, +				.center = { .x = -2.0, .y = 1.0, .z = 0.0 }, +				.radius = 0.9f, +			} +		}, { +			.sphere = { +				.type = RAY_OBJECT_TYPE_SPHERE, +				.surface = { +					.color = { .x = 0.0, .y = 1.0, .z = 1.0 }, +					.diffuse = 1.0f, +					.specular = 1.0f, +				}, +				.center = { .x = 2.0, .y = -1.0, .z = 0.0 }, +				.radius = 1.0f, +			} +		}, { +			.sphere = { +				.type = RAY_OBJECT_TYPE_SPHERE, +				.surface = { +					.color = { .x = 0.0, .y = 1.0, .z = 0.0 }, +					.diffuse = 1.0f, +					.specular = 1.0f, +				}, +				.center = { .x = 0.2, .y = -1.25, .z = 0.0 }, +				.radius = 0.6f, +			} +		}, { +			.light = { +				.type = RAY_OBJECT_TYPE_LIGHT, +				.brightness = 1.0, +				.emitter = { +					.point.type = RAY_LIGHT_EMITTER_TYPE_POINT, +					.point.center = { .x = 3.0f, .y = 3.0f, .z = 3.0f }, +					.point.surface = { +						.color = { .x = 1.0f, .y = 1.0f, .z = 1.0f }, +					}, +				} +			} +		} +	}; + +	ray_camera_t		camera = { +					.position = { .x = 0.0, .y = 0.0, .z = 6.0 }, +					.orientation = { +						.yaw = RAY_EULER_DEGREES(0.0f), +						.pitch = RAY_EULER_DEGREES(0.0f), +						.roll = RAY_EULER_DEGREES(180.0f), +					}, +					.focal_length = 700.0f, +					.width = fragment->width, +					.height = fragment->height, +				}; + +	static ray_scene_t	scene = { +					.objects = objects, +					.n_objects = nelems(objects), +					.lights = &objects[5], +					.n_lights = 1, +					.ambient_color = { .x = 1.0f, .y = 1.0f, .z = 1.0f }, +					.ambient_brightness = .04f, +				}; +	static int		initialized; +	static ray_threads_t	*threads; +	static fb_fragment_t	*fragments; +	static unsigned		ncpus; +#if 1 +	/* animated point light source */ +	static double		r; + +	r += .02; + +	scene.lights[0].light.emitter.point.center.x = cosf(r) * 3.5f; +	scene.lights[0].light.emitter.point.center.z = sinf(r) * 3.5f; +	camera.orientation.yaw = sinf(r) / 4; +	camera.orientation.pitch = sinf(r * 10) / 100; +	camera.orientation.roll = RAY_EULER_DEGREES(180.0f) + cosf(r) / 10; +	camera.position.x = cosf(r) / 10; +	camera.position.z = 4.0f + sinf(r) / 10; +#endif + +	if (!initialized) { +		initialized = 1; +		ncpus = get_ncpus(); + +		if (ncpus > 1) { +			threads = ray_threads_create(ncpus - 1); +			fragments = malloc(sizeof(fb_fragment_t) * ncpus); +		} +	} + +	if (ncpus > 1) { +		/* Always recompute the fragments[] geometry. +		 * This way the fragment geometry can change at any moment and things will +		 * continue functioning, which may prove important later on. +		 * (imagine things like a preview window, or perhaps composite modules +		 * which call on other modules supplying virtual fragments of varying dimensions..) +		 */ +		fb_fragment_divide(fragment, ncpus, fragments); +	} else { +		fragments = fragment; +	} + +	ray_scene_render_fragments(&scene, &camera, threads, fragments); +} + + +rototiller_renderer_t	ray_renderer = { +	.render = ray, +	.name = "ray", +	.description = "Multi-threaded ray tracer", +	.author = "Vito Caputo <vcaputo@pengaru.com>", +	.license = "GPLv2", +}; diff --git a/src/modules/ray/ray.h b/src/modules/ray/ray.h new file mode 100644 index 0000000..d33f96a --- /dev/null +++ b/src/modules/ray/ray.h @@ -0,0 +1,8 @@ +#ifndef _RAY_RAY_H +#define _RAY_RAY_H + +#include "fb.h" + +void ray(fb_fragment_t *fragment); + +#endif diff --git a/src/modules/ray/ray_3f.h b/src/modules/ray/ray_3f.h new file mode 100644 index 0000000..8408abb --- /dev/null +++ b/src/modules/ray/ray_3f.h @@ -0,0 +1,161 @@ +#ifndef _RAY_3F_H +#define _RAY_3F_H + +#include <math.h> + +typedef struct ray_3f_t { +	float	x, y, z; +} ray_3f_t; + + +/* return the result of (a + b) */ +static inline ray_3f_t ray_3f_add(ray_3f_t *a, ray_3f_t *b) +{ +	ray_3f_t	res = { +				.x = a->x + b->x, +				.y = a->y + b->y, +				.z = a->z + b->z, +			}; + +	return res; +} + + +/* return the result of (a - b) */ +static inline ray_3f_t ray_3f_sub(ray_3f_t *a, ray_3f_t *b) +{ +	ray_3f_t	res = { +				.x = a->x - b->x, +				.y = a->y - b->y, +				.z = a->z - b->z, +			}; + +	return res; +} + + +/* return the result of (-v) */ +static inline ray_3f_t ray_3f_negate(ray_3f_t *v) +{ +	ray_3f_t	res = { +				.x = -v->x, +				.y = -v->y, +				.z = -v->z, +			}; + +	return res; +} + + +/* return the result of (a * b) */ +static inline ray_3f_t ray_3f_mult(ray_3f_t *a, ray_3f_t *b) +{ +	ray_3f_t	res = { +				.x = a->x * b->x, +				.y = a->y * b->y, +				.z = a->z * b->z, +			}; + +	return res; +} + + +/* return the result of (v * scalar) */ +static inline ray_3f_t ray_3f_mult_scalar(ray_3f_t *v, float scalar) +{ +	ray_3f_t	res = { +				.x = v->x * scalar, +				.y = v->y * scalar, +				.z = v->z * scalar, +			}; + +	return res; +} + + +/* return the result of (uv / scalar) */ +static inline ray_3f_t ray_3f_div_scalar(ray_3f_t *v, float scalar) +{ +	ray_3f_t	res = { +				.x = v->x / scalar, +				.y = v->y / scalar, +				.z = v->z / scalar, +			}; + +	return res; +} + + +/* return the result of (a . b) */ +static inline float ray_3f_dot(ray_3f_t *a, ray_3f_t *b) +{ +	return a->x * b->x + a->y * b->y + a->z * b->z; +} + + +/* return the length of the supplied vector */ +static inline float ray_3f_length(ray_3f_t *v) +{ +	return sqrtf(ray_3f_dot(v, v)); +} + + +/* return the normalized form of the supplied vector */ +static inline ray_3f_t ray_3f_normalize(ray_3f_t *v) +{ +	ray_3f_t	nv; +	float		f; + +	f = 1.0f / ray_3f_length(v); + +	nv.x = f * v->x; +	nv.y = f * v->y; +	nv.z = f * v->z; + +	return nv; +} + + +/* return the distance between two arbitrary points */ +static inline float ray_3f_distance(ray_3f_t *a, ray_3f_t *b) +{ +	return sqrtf(powf(a->x - b->x, 2) + powf(a->y - b->y, 2) + powf(a->z - b->z, 2)); +} + + +/* return the cross product of two unit vectors */ +static inline ray_3f_t ray_3f_cross(ray_3f_t *a, ray_3f_t *b) +{ +	ray_3f_t	product; + +	product.x = a->y * b->z - a->z * b->y; +	product.y = a->z * b->x - a->x * b->z; +	product.z = a->x * b->y - a->y * b->x; + +	return product; +} + + +/* return the linearly interpolated vector between the two vectors at point alpha (0-1.0) */ +static inline ray_3f_t ray_3f_lerp(ray_3f_t *a, ray_3f_t *b, float alpha) +{ +	ray_3f_t	lerp_a, lerp_b; + +	lerp_a = ray_3f_mult_scalar(a, 1.0f - alpha); +	lerp_b = ray_3f_mult_scalar(b, alpha); + +	return ray_3f_add(&lerp_a, &lerp_b); +} + + +/* return the normalized linearly interpolated vector between the two vectors at point alpha (0-1.0) */ +static inline ray_3f_t ray_3f_nlerp(ray_3f_t *a, ray_3f_t *b, float alpha) +{ +	ray_3f_t	lerp; + +	lerp = ray_3f_lerp(a, b, alpha); + +	return ray_3f_normalize(&lerp); +} + +#endif diff --git a/src/modules/ray/ray_camera.c b/src/modules/ray/ray_camera.c new file mode 100644 index 0000000..0703c2e --- /dev/null +++ b/src/modules/ray/ray_camera.c @@ -0,0 +1,85 @@ +#include "fb.h" + +#include "ray_camera.h" +#include "ray_euler.h" + + +/* Produce a vector from the provided orientation vectors and proportions. */ +static ray_3f_t project_corner(ray_3f_t *forward, ray_3f_t *left, ray_3f_t *up, float focal_length, float horiz, float vert) +{ +	ray_3f_t	tmp; +	ray_3f_t	corner; + +	corner = ray_3f_mult_scalar(forward, focal_length); +	tmp = ray_3f_mult_scalar(left, horiz); +	corner = ray_3f_add(&corner, &tmp); +	tmp = ray_3f_mult_scalar(up, vert); +	corner = ray_3f_add(&corner, &tmp); + +	return ray_3f_normalize(&corner); +} + + +/* Produce vectors for the corners of the entire camera frame, used for interpolation. */ +static void project_corners(ray_camera_t *camera, ray_camera_frame_t *frame) +{ +	ray_3f_t	forward, left, up, right, down; +	float		half_horiz = (float)camera->width / 2.0f; +	float		half_vert = (float)camera->height / 2.0f; + +	ray_euler_basis(&camera->orientation, &forward, &up, &left); +	right = ray_3f_negate(&left); +	down = ray_3f_negate(&up); + +	frame->nw = project_corner(&forward, &left, &up, camera->focal_length, half_horiz,  half_vert); +	frame->ne = project_corner(&forward, &right, &up, camera->focal_length, half_horiz,  half_vert); +	frame->se = project_corner(&forward, &right, &down, camera->focal_length, half_horiz,  half_vert); +	frame->sw = project_corner(&forward, &left, &down, camera->focal_length, half_horiz,  half_vert); +} + + +/* Begin a frame for the fragment of camera projection, initializing frame and ray. */ +void ray_camera_frame_begin(ray_camera_t *camera, fb_fragment_t *fragment, ray_ray_t *ray, ray_camera_frame_t *frame) +{ +	/* References are kept to the camera, fragment, and ray to be traced. +	 * The ray is maintained as we step through the frame, that is the +	 * purpose of this api. +	 * +	 * Since the ray direction should be a normalized vector, the obvious +	 * implementation is a bit costly.  The camera frame api hides this +	 * detail so we can explore interpolation techniques to potentially +	 * lessen the per-pixel cost. +	 */ +	frame->camera = camera; +	frame->fragment = fragment; +	frame->ray = ray; + +	frame->x = frame->y = 0; + +	/* From camera->orientation and camera->focal_length compute the vectors +	 * through the viewport's corners, and place these normalized vectors +	 * in frame->(nw,ne,sw,se). +	 * +	 * These can than be interpolated between to produce the ray vectors +	 * throughout the frame's fragment.  The efficient option of linear +	 * interpolation will not maintain the unit vector length, so to +	 * produce normalized interpolated directions will require the costly +	 * normalize function. +	 * +	 * I'm hoping a simple length correction table can be used to fixup the +	 * linearly interpolated vectors to make them unit vectors with just +	 * scalar multiplication instead of the sqrt of normalize. +	 */ +	project_corners(camera, frame); + +	frame->x_delta = 1.0f / (float)camera->width; +	frame->y_delta = 1.0f / (float)camera->height; +	frame->x_alpha = frame->x_delta * (float)fragment->x; +	frame->y_alpha = frame->y_delta * (float)fragment->y; + +	frame->cur_w = ray_3f_nlerp(&frame->nw, &frame->sw, frame->y_alpha); +	frame->cur_e = ray_3f_nlerp(&frame->ne, &frame->se, frame->y_alpha); + +	ray->origin = camera->position; +	ray->direction = frame->cur_w; +} diff --git a/src/modules/ray/ray_camera.h b/src/modules/ray/ray_camera.h new file mode 100644 index 0000000..387f8c5 --- /dev/null +++ b/src/modules/ray/ray_camera.h @@ -0,0 +1,77 @@ +#ifndef _RAY_CAMERA_H +#define _RAY_CAMERA_H + +#include <math.h> + +#include "fb.h" + +#include "ray_3f.h" +#include "ray_euler.h" +#include "ray_ray.h" + + +typedef struct ray_camera_t { +	ray_3f_t	position;		/* position of camera, the origin of all its rays */ +	ray_euler_t	orientation;		/* orientation of the camera */ +	float		focal_length;		/* controls the field of view */ +	unsigned	width;			/* width of camera viewport in pixels */ +	unsigned	height;			/* height of camera viewport in pixels */ +} ray_camera_t; + + +typedef struct ray_camera_frame_t { +	ray_camera_t	*camera;		/* the camera supplied to frame_begin() */ +	fb_fragment_t	*fragment;		/* the fragment supplied to frame_begin() */ +	ray_ray_t	*ray;			/* the ray supplied to frame_begin(), which gets updated as we step through the frame. */ + +	ray_3f_t	nw, ne, sw, se;		/* directions pointing through the corners of the frame fragment */ +	ray_3f_t	cur_w, cur_e;		/* current row's west and east ends */ +	float		x_alpha, y_alpha;	/* interpolation position along the x and y axis */ +	float		x_delta, y_delta;	/* interpolation step delta along the x and y axis */ +	unsigned	x, y;			/* integral position within frame fragment */ +} ray_camera_frame_t; + + +void ray_camera_frame_begin(ray_camera_t *camera, fb_fragment_t *fragment, ray_ray_t *ray, ray_camera_frame_t *frame); + + +/* Step the ray through the frame on the x axis, returns 1 when rays remain on this axis, 0 at the end. */ +/* When 1 is returned, frame->ray is left pointing through the new coordinate. */ +static inline int ray_camera_frame_x_step(ray_camera_frame_t *frame) +{ +	frame->x++; + +	if (frame->x >= frame->fragment->width) { +		frame->x = 0; +		frame->x_alpha = frame->x_delta * (float)frame->fragment->x; +		return 0; +	} + +	frame->x_alpha += frame->x_delta; +	frame->ray->direction = ray_3f_nlerp(&frame->cur_w, &frame->cur_e, frame->x_alpha); + +	return 1; +} + + +/* Step the ray through the frame on the y axis, returns 1 when rays remain on this axis, 0 at the end. */ +/* When 1 is returned, frame->ray is left pointing through the new coordinate. */ +static inline int ray_camera_frame_y_step(ray_camera_frame_t *frame) +{ +	frame->y++; + +	if (frame->y >= frame->fragment->height) { +		frame->y = 0; +		frame->y_alpha = frame->y_delta * (float)frame->fragment->y; +		return 0; +	} + +	frame->y_alpha += frame->y_delta; +	frame->cur_w = ray_3f_nlerp(&frame->nw, &frame->sw, frame->y_alpha); +	frame->cur_e = ray_3f_nlerp(&frame->ne, &frame->se, frame->y_alpha); +	frame->ray->direction = frame->cur_w; + +	return 1; +} + +#endif diff --git a/src/modules/ray/ray_color.h b/src/modules/ray/ray_color.h new file mode 100644 index 0000000..9fe62c1 --- /dev/null +++ b/src/modules/ray/ray_color.h @@ -0,0 +1,29 @@ +#ifndef _RAY_COLOR_H +#define _RAY_COLOR_H + +#include <stdint.h> + +#include "ray_3f.h" + +typedef ray_3f_t ray_color_t; + +/* convert a vector into a packed, 32-bit rgb pixel value */ +static inline uint32_t ray_color_to_uint32_rgb(ray_color_t color) { +	uint32_t	pixel; + +	/* doing this all per-pixel, ugh. */ + +	if (color.x > 1.0f) color.x = 1.0f; +	if (color.y > 1.0f) color.y = 1.0f; +	if (color.z > 1.0f) color.z = 1.0f; + +	pixel = (uint32_t)(color.x * 255.0f); +	pixel <<= 8; +	pixel |= (uint32_t)(color.y * 255.0f); +	pixel <<= 8; +	pixel |= (uint32_t)(color.z * 255.0f); + +	return pixel; +} + +#endif diff --git a/src/modules/ray/ray_euler.h b/src/modules/ray/ray_euler.h new file mode 100644 index 0000000..86f5221 --- /dev/null +++ b/src/modules/ray/ray_euler.h @@ -0,0 +1,45 @@ +#ifndef _RAY_EULER_H +#define _RAY_EULER_H + +#include <math.h> + +#include "ray_3f.h" + + +/* euler angles are convenient for describing orientation */ +typedef struct ray_euler_t { +	float	pitch;	/* pitch in radiasn */ +	float	yaw;	/* yaw in radians */ +	float	roll;	/* roll in radians */ +} ray_euler_t; + + +/* convenience macro for converting degrees to radians */ +#define RAY_EULER_DEGREES(_deg) \ +	(_deg * (2 * M_PI / 360.0f)) + + +/* produce basis vectors from euler angles */ +static inline void ray_euler_basis(ray_euler_t *e, ray_3f_t *forward, ray_3f_t *up, ray_3f_t *left) +{ +	float	cos_yaw = cosf(e->yaw); +	float	sin_yaw = sinf(e->yaw); +	float	cos_roll = cosf(e->roll); +	float	sin_roll = sinf(e->roll); +	float	cos_pitch = cosf(e->pitch); +	float	sin_pitch = sinf(e->pitch); + +	forward->x = sin_yaw; +	forward->y = -sin_pitch * cos_yaw; +	forward->z = cos_pitch * cos_yaw; + +	up->x = -cos_yaw * sin_roll; +	up->y = -sin_pitch * sin_yaw * sin_roll + cos_pitch * cos_roll; +	up->z = cos_pitch * sin_yaw * sin_roll + sin_pitch * cos_roll; + +	left->x = cos_yaw * cos_roll; +	left->y = sin_pitch * sin_yaw * cos_roll + cos_pitch * sin_roll; +	left->z = -cos_pitch * sin_yaw * cos_roll + sin_pitch * sin_roll; +} + +#endif diff --git a/src/modules/ray/ray_light_emitter.h b/src/modules/ray/ray_light_emitter.h new file mode 100644 index 0000000..3b5509e --- /dev/null +++ b/src/modules/ray/ray_light_emitter.h @@ -0,0 +1,18 @@ +#ifndef _RAY_LIGHT_EMITTER_H +#define _RAY_LIGHT_EMITTER_H + +#include "ray_object_point.h" +#include "ray_object_sphere.h" + +typedef enum ray_light_emitter_type_t { +	RAY_LIGHT_EMITTER_TYPE_SPHERE, +	RAY_LIGHT_EMITTER_TYPE_POINT, +} ray_light_emitter_type_t; + +typedef union ray_light_emitter_t { +	ray_light_emitter_type_t	type; +	ray_object_sphere_t		sphere; +	ray_object_point_t		point; +} ray_light_emitter_t; + +#endif diff --git a/src/modules/ray/ray_object.c b/src/modules/ray/ray_object.c new file mode 100644 index 0000000..4c5ccaf --- /dev/null +++ b/src/modules/ray/ray_object.c @@ -0,0 +1,74 @@ +#include <assert.h> + +#include "ray_object.h" +#include "ray_object_light.h" +#include "ray_object_plane.h" +#include "ray_object_point.h" +#include "ray_object_sphere.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +/* Determine if a ray intersects object. + * If the object is intersected, store where along the ray the intersection occurs in res_distance. + */ +int ray_object_intersects_ray(ray_object_t *object, ray_ray_t *ray, float *res_distance) +{ +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		return ray_object_sphere_intersects_ray(&object->sphere, ray, res_distance); + +	case RAY_OBJECT_TYPE_POINT: +		return ray_object_point_intersects_ray(&object->point, ray, res_distance); + +	case RAY_OBJECT_TYPE_PLANE: +		return ray_object_plane_intersects_ray(&object->plane, ray, res_distance); + +	case RAY_OBJECT_TYPE_LIGHT: +		return ray_object_light_intersects_ray(&object->light, ray, res_distance); +	default: +		assert(0); +	} +} + + +/* Return the surface normal of object @ point */ +ray_3f_t ray_object_normal(ray_object_t *object, ray_3f_t *point) +{ +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		return ray_object_sphere_normal(&object->sphere, point); + +	case RAY_OBJECT_TYPE_POINT: +		return ray_object_point_normal(&object->point, point); + +	case RAY_OBJECT_TYPE_PLANE: +		return ray_object_plane_normal(&object->plane, point); + +	case RAY_OBJECT_TYPE_LIGHT: +		return ray_object_light_normal(&object->light, point); +	default: +		assert(0); +	} +} + + +/* Return the surface of object @ point */ +ray_surface_t ray_object_surface(ray_object_t *object, ray_3f_t *point) +{ +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		return ray_object_sphere_surface(&object->sphere, point); + +	case RAY_OBJECT_TYPE_POINT: +		return ray_object_point_surface(&object->point, point); + +	case RAY_OBJECT_TYPE_PLANE: +		return ray_object_plane_surface(&object->plane, point); + +	case RAY_OBJECT_TYPE_LIGHT: +		return ray_object_light_surface(&object->light, point); +	default: +		assert(0); +	} +} diff --git a/src/modules/ray/ray_object.h b/src/modules/ray/ray_object.h new file mode 100644 index 0000000..abdb254 --- /dev/null +++ b/src/modules/ray/ray_object.h @@ -0,0 +1,24 @@ +#ifndef _RAY_OBJECT_H +#define _RAY_OBJECT_H + +#include "ray_object_light.h" +#include "ray_object_plane.h" +#include "ray_object_point.h" +#include "ray_object_sphere.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + +typedef union ray_object_t { +	ray_object_type_t	type; +	ray_object_sphere_t	sphere; +	ray_object_point_t	point; +	ray_object_plane_t	plane; +	ray_object_light_t	light; +} ray_object_t; + +int ray_object_intersects_ray(ray_object_t *object, ray_ray_t *ray, float *res_distance); +ray_3f_t ray_object_normal(ray_object_t *object, ray_3f_t *point); +ray_surface_t ray_object_surface(ray_object_t *object, ray_3f_t *point); + +#endif diff --git a/src/modules/ray/ray_object_light.h b/src/modules/ray/ray_object_light.h new file mode 100644 index 0000000..342c050 --- /dev/null +++ b/src/modules/ray/ray_object_light.h @@ -0,0 +1,67 @@ +#ifndef _RAY_OBJECT_LIGHT_H +#define _RAY_OBJECT_LIGHT_H + +#include <assert.h> + +#include "ray_light_emitter.h" +#include "ray_object_light.h" +#include "ray_object_point.h" +#include "ray_object_sphere.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_object_light_t { +	ray_object_type_t	type; +	float			brightness; +	ray_light_emitter_t	emitter; +} ray_object_light_t; + + +/* TODO: point is really the only one I've implemented... */ +static inline int ray_object_light_intersects_ray(ray_object_light_t *light, ray_ray_t *ray, float *res_distance) +{ +	switch (light->emitter.type) { +	case RAY_LIGHT_EMITTER_TYPE_POINT: +		return ray_object_point_intersects_ray(&light->emitter.point, ray, res_distance); + +	case RAY_LIGHT_EMITTER_TYPE_SPHERE: +		return ray_object_sphere_intersects_ray(&light->emitter.sphere, ray, res_distance); +	default: +		assert(0); +	} +} + + +static inline ray_3f_t ray_object_light_normal(ray_object_light_t *light, ray_3f_t *point) +{ +	ray_3f_t	normal; + +	/* TODO */ +	switch (light->emitter.type) { +	case RAY_LIGHT_EMITTER_TYPE_SPHERE: +		return normal; + +	case RAY_LIGHT_EMITTER_TYPE_POINT: +		return normal; +	default: +		assert(0); +	} +} + + +static inline ray_surface_t ray_object_light_surface(ray_object_light_t *light, ray_3f_t *point) +{ +	switch (light->emitter.type) { +	case RAY_LIGHT_EMITTER_TYPE_SPHERE: +		return ray_object_sphere_surface(&light->emitter.sphere, point); + +	case RAY_LIGHT_EMITTER_TYPE_POINT: +		return ray_object_point_surface(&light->emitter.point, point); +	default: +		assert(0); +	} +} + +#endif diff --git a/src/modules/ray/ray_object_plane.h b/src/modules/ray/ray_object_plane.h new file mode 100644 index 0000000..b33f342 --- /dev/null +++ b/src/modules/ray/ray_object_plane.h @@ -0,0 +1,46 @@ +#ifndef _RAY_OBJECT_PLANE_H +#define _RAY_OBJECT_PLANE_H + +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_object_plane_t { +	ray_object_type_t	type; +	ray_surface_t		surface; +	ray_3f_t		normal; +	float			distance; +} ray_object_plane_t; + + +static inline int ray_object_plane_intersects_ray(ray_object_plane_t *plane, ray_ray_t *ray, float *res_distance) +{ +	float	d = ray_3f_dot(&plane->normal, &ray->direction); + +	if (d != 0) { +		float	distance = -(ray_3f_dot(&plane->normal, &ray->origin) + plane->distance) / d; + +		if (distance > 0) { +			*res_distance = distance; + +			return 1; +		} +	} + +	return 0; +} + + +static inline ray_3f_t ray_object_plane_normal(ray_object_plane_t *plane, ray_3f_t *point) +{ +	return plane->normal; +} + + +static inline ray_surface_t ray_object_plane_surface(ray_object_plane_t *plane, ray_3f_t *point) +{ +	return plane->surface; +} + +#endif diff --git a/src/modules/ray/ray_object_point.h b/src/modules/ray/ray_object_point.h new file mode 100644 index 0000000..c0c9610 --- /dev/null +++ b/src/modules/ray/ray_object_point.h @@ -0,0 +1,37 @@ +#ifndef _RAY_OBJECT_POINT_H +#define _RAY_OBJECT_POINT_H + +#include "ray_3f.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_object_point_t { +	ray_object_type_t	type; +	ray_surface_t		surface; +	ray_3f_t		center; +} ray_object_point_t; + + +static inline int ray_object_point_intersects_ray(ray_object_point_t *point, ray_ray_t *ray, float *res_distance) +{ +	/* TODO: determine a ray:point intersection */ +	return 0; +} + + +static inline ray_3f_t ray_object_point_normal(ray_object_point_t *point, ray_3f_t *_point) +{ +	ray_3f_t	normal; + +	return normal; +} + + +static inline ray_surface_t ray_object_point_surface(ray_object_point_t *point, ray_3f_t *_point) +{ +	return point->surface; +} + +#endif diff --git a/src/modules/ray/ray_object_sphere.h b/src/modules/ray/ray_object_sphere.h new file mode 100644 index 0000000..85b3d93 --- /dev/null +++ b/src/modules/ray/ray_object_sphere.h @@ -0,0 +1,65 @@ +#ifndef _RAY_OBJECT_SPHERE_H +#define _RAY_OBJECT_SPHERE_H + +#include <math.h> +#include <stdio.h> + +#include "ray_3f.h" +#include "ray_color.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_object_sphere_t { +	ray_object_type_t	type; +	ray_surface_t		surface; +	ray_3f_t		center; +	float			radius; +} ray_object_sphere_t; + + +static inline int ray_object_sphere_intersects_ray(ray_object_sphere_t *sphere, ray_ray_t *ray, float *res_distance) +{ +	ray_3f_t	v = ray_3f_sub(&ray->origin, &sphere->center); +	float		b = ray_3f_dot(&v, &ray->direction); +	float		disc = (sphere->radius * sphere->radius) - ray_3f_dot(&v, &v) + (b * b); + +	if (disc > 0) { +		float	i1, i2; + +		disc = sqrtf(disc); + +		i1 = b - disc; +		i2 = b + disc; + +		if (i2 > 0 && i1 > 0) { +			*res_distance = i1; +			return 1; +		} +	} + +	return 0; +} + + +/* return the normal of the surface at the specified point */ +static inline ray_3f_t ray_object_sphere_normal(ray_object_sphere_t *sphere, ray_3f_t *point) +{ +	ray_3f_t	normal; +	 +	normal = ray_3f_sub(point, &sphere->center); +	normal = ray_3f_div_scalar(&normal, sphere->radius);	/* normalize without the sqrt() */ + +	return normal; +} + + +/* return the surface of the sphere @ point */ +static inline ray_surface_t ray_object_sphere_surface(ray_object_sphere_t *sphere, ray_3f_t *point) +{ +	/* uniform solids for now... */ +	return sphere->surface; +} + +#endif diff --git a/src/modules/ray/ray_object_type.h b/src/modules/ray/ray_object_type.h new file mode 100644 index 0000000..6ce20f5 --- /dev/null +++ b/src/modules/ray/ray_object_type.h @@ -0,0 +1,11 @@ +#ifndef _RAY_OBJECT_TYPE_H +#define _RAY_OBJECT_TYPE_H + +typedef enum ray_object_type_t { +	RAY_OBJECT_TYPE_SPHERE, +	RAY_OBJECT_TYPE_POINT, +	RAY_OBJECT_TYPE_PLANE, +	RAY_OBJECT_TYPE_LIGHT, +} ray_object_type_t; + +#endif diff --git a/src/modules/ray/ray_ray.h b/src/modules/ray/ray_ray.h new file mode 100644 index 0000000..91469a2 --- /dev/null +++ b/src/modules/ray/ray_ray.h @@ -0,0 +1,11 @@ +#ifndef _RAY_RAY_H +#define _RAY_RAY_H + +#include "ray_3f.h" + +typedef struct ray_ray_t { +	ray_3f_t	origin; +	ray_3f_t	direction; +} ray_ray_t; + +#endif diff --git a/src/modules/ray/ray_scene.c b/src/modules/ray/ray_scene.c new file mode 100644 index 0000000..e44990b --- /dev/null +++ b/src/modules/ray/ray_scene.c @@ -0,0 +1,188 @@ +#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" +#include "ray_threads.h" + +#define MAX_RECURSION_DEPTH	5 + + +static ray_color_t trace_ray(ray_scene_t *scene, ray_ray_t *ray, unsigned depth); + + +/* 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, ray_ray_t *ray, float distance) +{ +	unsigned	i; + +	for (i = 0; i < scene->n_objects; i++) { +		float	ood; + +		if (scene->objects[i].type == RAY_OBJECT_TYPE_LIGHT) +			continue; + +		if (ray_object_intersects_ray(&scene->objects[i], ray, &ood) && +		    ood < distance) { +			return 1; +		} +	} + +	return 0; +} + + +/* Determine the color @ distance on ray on object viewed from origin */ +static inline ray_color_t shade_ray(ray_scene_t *scene, ray_ray_t *ray, ray_object_t *object, float distance, unsigned depth) +{ +	ray_surface_t	surface; +	ray_color_t	color; +	ray_3f_t	rvec = ray_3f_mult_scalar(&ray->direction, distance); +	ray_3f_t	intersection = ray_3f_sub(&ray->origin, &rvec); +	ray_3f_t	normal = ray_object_normal(object, &intersection); +	unsigned	i; + +	surface = ray_object_surface(object, &intersection); +	color = ray_3f_mult_scalar(&scene->ambient_color, scene->ambient_brightness); +	color = ray_3f_mult(&surface.color, &color); + +	/* visit lights for shadows and illumination */ +	for (i = 0; i < scene->n_lights; i++) { +		ray_3f_t	lvec = ray_3f_sub(&scene->lights[i].light.emitter.point.center, &intersection); +		float		ldist = ray_3f_length(&lvec); +		float		lvec_normal_dot; +		ray_ray_t	shadow_ray; + +		lvec = ray_3f_mult_scalar(&lvec, (1.0f / ldist)); /* normalize lvec */ +#if 1 +		/* skip this light if it's obstructed, +		 * we must shift the origin slightly towards the light to prevent +		 * spurious self-obstruction at the ray:object intersection */ +		/* negate the light vector so it's pointed at the light rather than from it */ +		shadow_ray.direction = ray_3f_negate(&lvec); +		shadow_ray.origin = ray_3f_mult_scalar(&shadow_ray.direction, 0.00001f); +		shadow_ray.origin = ray_3f_add(&shadow_ray.origin, &intersection); + +		if (ray_is_obstructed(scene, &shadow_ray, ldist)) +			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; +			ray_color_t	specular; + +			diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot); +			diffuse = ray_3f_mult_scalar(&diffuse, surface.diffuse); +			color = ray_3f_add(&color, &diffuse); + +			/* FIXME: assumes light is a point for its color, and 20 is a constant "Phong exponent", +			 * which should really be object/surface-specific +			 */ +			specular = ray_3f_mult_scalar(&scene->lights[i].light.emitter.point.surface.color, powf(rvec_lvec_dot, 20)); +			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 +		} +	} + +	/* generate a reflection ray */ +#if 1 +	float		dot = ray_3f_dot(&ray->direction, &normal); +	ray_ray_t	reflected_ray = { .direction = ray_3f_mult_scalar(&normal, dot * 2.0f) }; +	ray_3f_t	reflection; + +	reflected_ray.origin = intersection; +	reflected_ray.direction = ray_3f_sub(&ray->direction, &reflected_ray.direction); + +	reflection = trace_ray(scene, &reflected_ray, depth + 1); +	reflection = ray_3f_mult_scalar(&reflection, surface.specular); +	color = ray_3f_add(&color, &reflection); +#endif + +	/* TODO: generate a refraction ray */ + +	return color; +} + + +static ray_color_t trace_ray(ray_scene_t *scene, ray_ray_t *ray, unsigned depth) +{ +	ray_object_t	*nearest_object = NULL; +	float		nearest_object_distance = INFINITY; +	ray_color_t	color = { .x = 0.0, .y = 0.0, .z = 0.0 }; +	unsigned	i; + +	depth++; +	if (depth > MAX_RECURSION_DEPTH) +		return color; + +	for (i = 0; i < scene->n_objects; i++) { +		float	distance; + +		/* Does this ray intersect object? */ +		if (ray_object_intersects_ray(&scene->objects[i], ray, &distance)) { + +			/* Is it the nearest intersection? */ +			if (!nearest_object || +			    distance < nearest_object_distance) { +				nearest_object = &scene->objects[i]; +				nearest_object_distance = distance; +			} +		} +	} + +	if (nearest_object) +		color = shade_ray(scene, ray, nearest_object, nearest_object_distance, depth); + +	depth--; + +	return color; +} + + +void ray_scene_render_fragment(ray_scene_t *scene, ray_camera_t *camera, fb_fragment_t *fragment) +{ +	ray_camera_frame_t	frame; +	ray_ray_t		ray; +	uint32_t		*buf = fragment->buf; +	unsigned		stride = fragment->stride / 4; + +	ray_camera_frame_begin(camera, fragment, &ray, &frame); +	do { +		do { +			*buf = ray_color_to_uint32_rgb(trace_ray(scene, &ray, 0)); +			buf++; +		} while (ray_camera_frame_x_step(&frame)); + +		buf += stride; +	} while (ray_camera_frame_y_step(&frame)); +} + +/* we expect fragments[threads->n_threads + 1], or fragments[1] when threads == NULL */ +void ray_scene_render_fragments(ray_scene_t *scene, ray_camera_t *camera, ray_threads_t *threads, fb_fragment_t *fragments) +{ +	unsigned	n_threads = threads ? threads->n_threads + 1 : 1; +	unsigned	i; + +	for (i = 1; i < n_threads; i++) +		ray_thread_fragment_submit(&threads->threads[i - 1], scene, camera, &fragments[i]); + +	/* always render the zero fragment in-line */ +	ray_scene_render_fragment(scene, camera, &fragments[0]); + +	for (i = 1; i < n_threads; i++) +		ray_thread_wait_idle(&threads->threads[i - 1]); +} diff --git a/src/modules/ray/ray_scene.h b/src/modules/ray/ray_scene.h new file mode 100644 index 0000000..e9781c6 --- /dev/null +++ b/src/modules/ray/ray_scene.h @@ -0,0 +1,27 @@ +#ifndef _RAY_SCENE_H +#define _RAY_SCENE_H + +#include "fb.h" + +#include "ray_camera.h" +#include "ray_color.h" +#include "ray_ray.h" +#include "ray_threads.h" + +typedef union ray_object_t ray_object_t; + +typedef struct ray_scene_t { +	ray_object_t	*objects; +	unsigned	n_objects; + +	ray_object_t	*lights; +	unsigned	n_lights; + +	ray_color_t	ambient_color; +	float		ambient_brightness; +} ray_scene_t; + +void ray_scene_render_fragments(ray_scene_t *scene, ray_camera_t *camera, ray_threads_t *threads, fb_fragment_t *fragments); +void ray_scene_render_fragment(ray_scene_t *scene, ray_camera_t *camera, fb_fragment_t *fragment); + +#endif diff --git a/src/modules/ray/ray_surface.h b/src/modules/ray/ray_surface.h new file mode 100644 index 0000000..b3e3c68 --- /dev/null +++ b/src/modules/ray/ray_surface.h @@ -0,0 +1,14 @@ +#ifndef _RAY_MATERIAL_H +#define _RAY_MATERIAL_H + +#include "ray_3f.h" +#include "ray_color.h" + +/* Surface properties we expect every object to be able to introspect */ +typedef struct ray_surface_t { +	ray_color_t	color; +	float		specular; +	float		diffuse; +} ray_surface_t; + +#endif diff --git a/src/modules/ray/ray_threads.c b/src/modules/ray/ray_threads.c new file mode 100644 index 0000000..2369687 --- /dev/null +++ b/src/modules/ray/ray_threads.c @@ -0,0 +1,111 @@ +#include <pthread.h> +#include <stdlib.h> + +#include "fb.h" + +#include "ray_scene.h" +#include "ray_threads.h" + +#define BUSY_WAIT_NUM	1000000000	/* How much to spin before sleeping in pthread_cond_wait() */ + +/* for now assuming x86 */ +#define cpu_relax() \ +	__asm__ __volatile__ ( "pause\n" : : : "memory") + +/* This is a very simple/naive implementation, there's certainly room for improvement. + * + * Without the BUSY_WAIT_NUM spinning this approach seems to leave a fairly + * substantial proportion of CPU idle while waiting for the render thread to + * complete on my core 2 duo. + * + * It's probably just latency in getting the render thread woken when the work + * is submitted, and since the fragments are split equally the main thread gets + * a head start and has to wait when it finishes first.  The spinning is just + * an attempt to avoid going to sleep while the render threads finish, there + * still needs to be improvement in how the work is submitted. + * + * I haven't spent much time on optimizing the raytracer yet. + */ + +static void * ray_thread_func(void *_thread) +{ +	ray_thread_t	*thread = _thread; + +	for (;;) { +		pthread_mutex_lock(&thread->mutex); +		while (thread->fragment == NULL) +			pthread_cond_wait(&thread->cond, &thread->mutex); + +		ray_scene_render_fragment(thread->scene, thread->camera, thread->fragment); +		thread->fragment = NULL; +		pthread_mutex_unlock(&thread->mutex); +		pthread_cond_signal(&thread->cond); +	} + +	return NULL; +} + + +void ray_thread_fragment_submit(ray_thread_t *thread, ray_scene_t *scene, ray_camera_t *camera, fb_fragment_t *fragment) +{ +	pthread_mutex_lock(&thread->mutex); +	while (thread->fragment != NULL)	/* XXX: never true due to ray_thread_wait_idle() */ +		pthread_cond_wait(&thread->cond, &thread->mutex); + +	thread->fragment = fragment; +	thread->scene = scene; +	thread->camera = camera; + +	pthread_mutex_unlock(&thread->mutex); +	pthread_cond_signal(&thread->cond); +} + + +void ray_thread_wait_idle(ray_thread_t *thread) +{ +	unsigned	n; + +	/* Spin before going to sleep, the other thread should not take substantially longer. */ +	for (n = 0; thread->fragment != NULL && n < BUSY_WAIT_NUM; n++) +		cpu_relax(); + +	pthread_mutex_lock(&thread->mutex); +	while (thread->fragment != NULL) +		pthread_cond_wait(&thread->cond, &thread->mutex); +	pthread_mutex_unlock(&thread->mutex); +} + + +ray_threads_t * ray_threads_create(unsigned num) +{ +	ray_threads_t	*threads; +	unsigned	i; + +	threads = malloc(sizeof(ray_threads_t) + sizeof(ray_thread_t) * num); +	if (!threads) +		return NULL; + +	for (i = 0; i < num; i++) { +		pthread_mutex_init(&threads->threads[i].mutex, NULL); +		pthread_cond_init(&threads->threads[i].cond, NULL); +		threads->threads[i].fragment = NULL; +		pthread_create(&threads->threads[i].thread, NULL, ray_thread_func, &threads->threads[i]); +	} +	threads->n_threads = num; + +	return threads; +} + + +void ray_threads_destroy(ray_threads_t *threads) +{ +	unsigned	i; + +	for (i = 0; i < threads->n_threads; i++) +		pthread_cancel(threads->threads[i].thread); + +	for (i = 0; i < threads->n_threads; i++) +		pthread_join(threads->threads[i].thread, NULL); + +	free(threads);	 +} diff --git a/src/modules/ray/ray_threads.h b/src/modules/ray/ray_threads.h new file mode 100644 index 0000000..b4c601d --- /dev/null +++ b/src/modules/ray/ray_threads.h @@ -0,0 +1,30 @@ +#ifndef _RAY_THREADS_H +#define _RAY_THREADS_H + +#include <pthread.h> + +typedef struct ray_scene_t ray_scene_t; +typedef struct ray_camera_t ray_camera_t; +typedef struct fb_fragment_t fb_fragment_t; + +typedef struct ray_thread_t { +	pthread_t	thread; +	pthread_mutex_t	mutex; +	pthread_cond_t	cond; +	ray_scene_t	*scene; +	ray_camera_t	*camera; +	fb_fragment_t	*fragment; +} ray_thread_t; + +typedef struct ray_threads_t { +	unsigned	n_threads; +	ray_thread_t	threads[]; +} ray_threads_t; + + +ray_threads_t * ray_threads_create(unsigned num); +void ray_threads_destroy(ray_threads_t *threads); + +void ray_thread_fragment_submit(ray_thread_t *thread, ray_scene_t *scene, ray_camera_t *camera, fb_fragment_t *fragment); +void ray_thread_wait_idle(ray_thread_t *thread); +#endif  | 
