diff options
| author | Vito Caputo <vcaputo@pengaru.com> | 2018-03-19 21:39:09 -0700 | 
|---|---|---|
| committer | Vito Caputo <vcaputo@pengaru.com> | 2018-03-19 22:02:08 -0700 | 
| commit | b5bc962e834992eeba2abdd10f6e37ee2ba20295 (patch) | |
| tree | 11bf58a64208806817c0fcab481588086297ce6c /src/libs/ray | |
| parent | 3b9a4861d6937a66b03791b5b497e47c52189a7d (diff) | |
ray: libize raytracer core, introduces src/libs
This is the first step of breaking out all the core rendering stuffs
into reusable libraries and making modules purely compositional,
consumers of various included rendering/effects libraries.
Expect multiple modules leveraging libray for a variety of scenes and
such.  Also expect compositions mixing the various libraries for more
interesting visuals.
Diffstat (limited to 'src/libs/ray')
| -rw-r--r-- | src/libs/ray/ray_3f.h | 154 | ||||
| -rw-r--r-- | src/libs/ray/ray_camera.c | 70 | ||||
| -rw-r--r-- | src/libs/ray/ray_camera.h | 84 | ||||
| -rw-r--r-- | src/libs/ray/ray_color.h | 29 | ||||
| -rw-r--r-- | src/libs/ray/ray_euler.c | 90 | ||||
| -rw-r--r-- | src/libs/ray/ray_euler.h | 33 | ||||
| -rw-r--r-- | src/libs/ray/ray_light_emitter.h | 18 | ||||
| -rw-r--r-- | src/libs/ray/ray_object.h | 18 | ||||
| -rw-r--r-- | src/libs/ray/ray_object_light.h | 14 | ||||
| -rw-r--r-- | src/libs/ray/ray_object_plane.h | 16 | ||||
| -rw-r--r-- | src/libs/ray/ray_object_point.h | 15 | ||||
| -rw-r--r-- | src/libs/ray/ray_object_sphere.h | 16 | ||||
| -rw-r--r-- | src/libs/ray/ray_object_type.h | 12 | ||||
| -rw-r--r-- | src/libs/ray/ray_ray.h | 11 | ||||
| -rw-r--r-- | src/libs/ray/ray_render.c | 251 | ||||
| -rw-r--r-- | src/libs/ray/ray_render.h | 15 | ||||
| -rw-r--r-- | src/libs/ray/ray_render_object.h | 121 | ||||
| -rw-r--r-- | src/libs/ray/ray_render_object_plane.h | 61 | ||||
| -rw-r--r-- | src/libs/ray/ray_render_object_point.h | 45 | ||||
| -rw-r--r-- | src/libs/ray/ray_render_object_sphere.h | 90 | ||||
| -rw-r--r-- | src/libs/ray/ray_scene.h | 16 | ||||
| -rw-r--r-- | src/libs/ray/ray_surface.h | 15 | 
22 files changed, 1194 insertions, 0 deletions
| diff --git a/src/libs/ray/ray_3f.h b/src/libs/ray/ray_3f.h new file mode 100644 index 0000000..2c04a5a --- /dev/null +++ b/src/libs/ray/ray_3f.h @@ -0,0 +1,154 @@ +#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(const ray_3f_t *a, const 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(const ray_3f_t *a, const 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(const 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(const ray_3f_t *a, const 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(const 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(const 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(const ray_3f_t *a, const 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(const 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(const ray_3f_t *v) +{ +	return ray_3f_mult_scalar(v, 1.0f / ray_3f_length(v)); +} + + +/* return the distance between two arbitrary points */ +static inline float ray_3f_distance(const ray_3f_t *a, const ray_3f_t *b) +{ +	ray_3f_t	delta = ray_3f_sub(a, b); + +	return ray_3f_length(&delta); +} + + +/* return the cross product of two unit vectors */ +static inline ray_3f_t ray_3f_cross(const ray_3f_t *a, const 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(const ray_3f_t *a, const 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(const ray_3f_t *a, const 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/libs/ray/ray_camera.c b/src/libs/ray/ray_camera.c new file mode 100644 index 0000000..b942cf3 --- /dev/null +++ b/src/libs/ray/ray_camera.c @@ -0,0 +1,70 @@ +#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(const ray_camera_t *camera, ray_camera_frame_t *frame) +{ +	ray_3f_t	forward, left, up, right, down; +	float		half_horiz = (float)camera->width * 0.5f; +	float		half_vert = (float)camera->height * 0.5f; + +	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); +} + + +/* Prepare a frame of camera projection, initializing res_frame. */ +void ray_camera_frame_prepare(const ray_camera_t *camera, ray_camera_frame_t *res_frame) +{ +	res_frame->camera = camera; + +	project_corners(camera, res_frame); + +	res_frame->x_delta = 1.0f / (float)camera->width; +	res_frame->y_delta = 1.0f / (float)camera->height; +} + + +/* Begin a frame's fragment, initializing frame and ray. */ +void ray_camera_fragment_begin(ray_camera_frame_t *frame, fb_fragment_t *fb_fragment, ray_ray_t *res_ray, ray_camera_fragment_t *res_fragment) +{ +	res_fragment->frame = frame; +	res_fragment->fb_fragment = fb_fragment; +	res_fragment->ray = res_ray; + +	res_fragment->x = res_fragment->y = 0; + +	res_fragment->x_alpha = frame->x_delta * (float)fb_fragment->x; +	res_fragment->y_alpha = frame->y_delta * (float)fb_fragment->y; + +	res_fragment->cur_w = ray_3f_lerp(&frame->nw, &frame->sw, res_fragment->y_alpha); +	res_fragment->cur_e = ray_3f_lerp(&frame->ne, &frame->se, res_fragment->y_alpha); + +	res_ray->origin = frame->camera->position; +	res_ray->direction = ray_3f_nlerp(&res_fragment->cur_w, &res_fragment->cur_e, res_fragment->x_alpha); +} diff --git a/src/libs/ray/ray_camera.h b/src/libs/ray/ray_camera.h new file mode 100644 index 0000000..e393beb --- /dev/null +++ b/src/libs/ray/ray_camera.h @@ -0,0 +1,84 @@ +#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 { +	const ray_camera_t	*camera;		/* the camera supplied to frame_begin() */ + +	ray_3f_t		nw, ne, sw, se;		/* directions pointing through the corners of the frame fragment */ +	float			x_delta, y_delta;	/* interpolation step delta along the x and y axis */ +} ray_camera_frame_t; + + +typedef struct ray_camera_fragment_t { +	ray_camera_frame_t	*frame;			/* the frame supplied to fragment_begin() */ +	fb_fragment_t		*fb_fragment;		/* the fragment supplied to fragment_begin() */ +	ray_ray_t		*ray;			/* the ray supplied to frame_begin(), which gets updated as we step through the frame. */ + +	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 */ +	unsigned		x, y;			/* integral position within frame fragment */ +} ray_camera_fragment_t; + + +void ray_camera_frame_prepare(const ray_camera_t *camera, ray_camera_frame_t *res_frame); +void ray_camera_fragment_begin(ray_camera_frame_t *frame, fb_fragment_t *fb_fragment, ray_ray_t *res_ray, ray_camera_fragment_t *res_fragment); + + +/* Step the ray through the fragment on the x axis, returns 1 when rays remain on this axis, 0 at the end. */ +/* When 1 is returned, fragment->ray is left pointing through the new coordinate. */ +static inline int ray_camera_fragment_x_step(ray_camera_fragment_t *fragment) +{ +	fragment->x++; + +	if (fragment->x >= fragment->fb_fragment->width) { +		fragment->x = 0; +		fragment->x_alpha = fragment->frame->x_delta * (float)fragment->fb_fragment->x; +		return 0; +	} + +	fragment->x_alpha += fragment->frame->x_delta; +	fragment->ray->direction = ray_3f_nlerp(&fragment->cur_w, &fragment->cur_e, fragment->x_alpha); + +	return 1; +} + + +/* Step the ray through the fragment on the y axis, returns 1 when rays remain on this axis, 0 at the end. */ +/* When 1 is returned, fragment->ray is left pointing through the new coordinate. */ +static inline int ray_camera_fragment_y_step(ray_camera_fragment_t *fragment) +{ +	fragment->y++; + +	if (fragment->y >= fragment->fb_fragment->height) { +		fragment->y = 0; +		fragment->y_alpha = fragment->frame->y_delta * (float)fragment->fb_fragment->y; +		return 0; +	} + +	fragment->y_alpha += fragment->frame->y_delta; +	fragment->cur_w = ray_3f_lerp(&fragment->frame->nw, &fragment->frame->sw, fragment->y_alpha); +	fragment->cur_e = ray_3f_lerp(&fragment->frame->ne, &fragment->frame->se, fragment->y_alpha); +	fragment->ray->direction = ray_3f_nlerp(&fragment->cur_w, &fragment->cur_e, fragment->x_alpha); + +	return 1; +} + +#endif diff --git a/src/libs/ray/ray_color.h b/src/libs/ray/ray_color.h new file mode 100644 index 0000000..9fe62c1 --- /dev/null +++ b/src/libs/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/libs/ray/ray_euler.c b/src/libs/ray/ray_euler.c new file mode 100644 index 0000000..f143e25 --- /dev/null +++ b/src/libs/ray/ray_euler.c @@ -0,0 +1,90 @@ +#include <assert.h> +#include <math.h> + +#include "ray_3f.h" +#include "ray_euler.h" + +/* produce orthonormal basis vectors from euler angles, rotated in the specified order */ +void ray_euler_basis(const 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); + +	/* Rotation matrices from http://www.songho.ca/opengl/gl_anglestoaxes.html */ +	switch (e->order) { +	case RAY_EULER_ORDER_PYR: +		/* pitch, yaw, roll */ +		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; + +		forward->x = sin_yaw; +		forward->y = -sin_pitch * cos_yaw; +		forward->z = cos_pitch * cos_yaw; +		break; + +	case RAY_EULER_ORDER_YRP: +		/* yaw, roll, pitch */ +		up->x = -cos_yaw * sin_roll * cos_pitch + sin_yaw * sin_pitch; +		up->y = cos_roll * cos_pitch; +		up->z = sin_yaw * sin_roll * cos_pitch + cos_yaw * sin_pitch; + +		forward->x = cos_yaw * sin_roll * sin_pitch + sin_yaw * cos_pitch; +		forward->y = -cos_roll * sin_pitch; +		forward->z = -sin_yaw * sin_roll * sin_pitch + cos_yaw * cos_pitch; +		break; + +	case RAY_EULER_ORDER_RPY: +		/* roll, pitch, yaw */ +		up->x = -sin_roll * cos_pitch; +		up->y = cos_roll * cos_pitch; +		up->z = sin_pitch; + +		forward->x = cos_roll * sin_yaw + sin_roll * sin_pitch * cos_yaw; +		forward->y = sin_roll * sin_yaw - cos_roll * sin_pitch * cos_yaw; +		forward->z = cos_pitch * cos_yaw; +		break; + +	case RAY_EULER_ORDER_PRY: +		/* pitch, roll, yaw */ +		up->x = -sin_roll; +		up->y = cos_pitch * cos_roll; +		up->z = sin_pitch * cos_roll; + +		forward->x = cos_roll * sin_yaw; +		forward->y = cos_pitch * sin_roll * sin_yaw - sin_pitch * cos_yaw; +		forward->z = sin_pitch * sin_roll * sin_yaw + cos_pitch * cos_yaw; +		break; + +	case RAY_EULER_ORDER_RYP: +		/* roll, yaw, pitch */ +		up->x = -sin_roll * cos_pitch + cos_roll * sin_yaw * sin_pitch; +		up->y = cos_roll * cos_pitch + sin_roll * sin_yaw * sin_pitch; +		up->z = cos_yaw * sin_pitch; + +		forward->x = sin_roll * sin_pitch + cos_roll * sin_yaw * cos_pitch; +		forward->y = -cos_roll * sin_pitch + sin_roll * sin_yaw * cos_pitch; +		forward->z = cos_yaw * cos_pitch; +		break; + +	case RAY_EULER_ORDER_YPR: +		/* yaw, pitch, roll */ +		up->x = -cos_yaw * sin_roll + sin_yaw * sin_pitch * cos_roll; +		up->y = cos_pitch * cos_roll; +		up->z = sin_yaw * sin_roll + cos_yaw * sin_pitch * cos_roll; + +		forward->x = sin_yaw * cos_pitch; +		forward->y = -sin_pitch; +		forward->z = cos_yaw * cos_pitch; +		break; + +	default: +		assert(0); +	} + +	*left = ray_3f_cross(up, forward); +} diff --git a/src/libs/ray/ray_euler.h b/src/libs/ray/ray_euler.h new file mode 100644 index 0000000..39da17e --- /dev/null +++ b/src/libs/ray/ray_euler.h @@ -0,0 +1,33 @@ +#ifndef _RAY_EULER_H +#define _RAY_EULER_H + +#include <math.h> + +#include "ray_3f.h" + +/* Desired order to apply euler angle rotations */ +typedef enum ray_euler_order_t { +	RAY_EULER_ORDER_PYR, +	RAY_EULER_ORDER_YRP, +	RAY_EULER_ORDER_RPY, +	RAY_EULER_ORDER_PRY, +	RAY_EULER_ORDER_RYP, +	RAY_EULER_ORDER_YPR, +} ray_euler_order_t; + +/* euler angles are convenient for describing orientation */ +typedef struct ray_euler_t { +	ray_euler_order_t	order;	/* order to apply rotations in */ +	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)) + +void ray_euler_basis(const ray_euler_t *e, ray_3f_t *forward, ray_3f_t *up, ray_3f_t *left); + +#endif diff --git a/src/libs/ray/ray_light_emitter.h b/src/libs/ray/ray_light_emitter.h new file mode 100644 index 0000000..3b5509e --- /dev/null +++ b/src/libs/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/libs/ray/ray_object.h b/src/libs/ray/ray_object.h new file mode 100644 index 0000000..fc5ae1b --- /dev/null +++ b/src/libs/ray/ray_object.h @@ -0,0 +1,18 @@ +#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" + +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; + +#endif diff --git a/src/libs/ray/ray_object_light.h b/src/libs/ray/ray_object_light.h new file mode 100644 index 0000000..ca9bac9 --- /dev/null +++ b/src/libs/ray/ray_object_light.h @@ -0,0 +1,14 @@ +#ifndef _RAY_OBJECT_LIGHT_H +#define _RAY_OBJECT_LIGHT_H + +#include "ray_light_emitter.h" +#include "ray_object_type.h" + + +typedef struct ray_object_light_t { +	ray_object_type_t	type; +	float			brightness; +	ray_light_emitter_t	emitter; +} ray_object_light_t; + +#endif diff --git a/src/libs/ray/ray_object_plane.h b/src/libs/ray/ray_object_plane.h new file mode 100644 index 0000000..96ed437 --- /dev/null +++ b/src/libs/ray/ray_object_plane.h @@ -0,0 +1,16 @@ +#ifndef _RAY_OBJECT_PLANE_H +#define _RAY_OBJECT_PLANE_H + +#include "ray_3f.h" +#include "ray_object_type.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; + +#endif diff --git a/src/libs/ray/ray_object_point.h b/src/libs/ray/ray_object_point.h new file mode 100644 index 0000000..5685fdc --- /dev/null +++ b/src/libs/ray/ray_object_point.h @@ -0,0 +1,15 @@ +#ifndef _RAY_OBJECT_POINT_H +#define _RAY_OBJECT_POINT_H + +#include "ray_3f.h" +#include "ray_object_type.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; + +#endif diff --git a/src/libs/ray/ray_object_sphere.h b/src/libs/ray/ray_object_sphere.h new file mode 100644 index 0000000..71b6334 --- /dev/null +++ b/src/libs/ray/ray_object_sphere.h @@ -0,0 +1,16 @@ +#ifndef _RAY_OBJECT_SPHERE_H +#define _RAY_OBJECT_SPHERE_H + +#include "ray_3f.h" +#include "ray_object_type.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; + +#endif diff --git a/src/libs/ray/ray_object_type.h b/src/libs/ray/ray_object_type.h new file mode 100644 index 0000000..ab797d2 --- /dev/null +++ b/src/libs/ray/ray_object_type.h @@ -0,0 +1,12 @@ +#ifndef _RAY_OBJECT_TYPE_H +#define _RAY_OBJECT_TYPE_H + +typedef enum ray_object_type_t { +	RAY_OBJECT_TYPE_SENTINEL, +	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/libs/ray/ray_ray.h b/src/libs/ray/ray_ray.h new file mode 100644 index 0000000..91469a2 --- /dev/null +++ b/src/libs/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/libs/ray/ray_render.c b/src/libs/ray/ray_render.c new file mode 100644 index 0000000..8b930f6 --- /dev/null +++ b/src/libs/ray/ray_render.c @@ -0,0 +1,251 @@ +#include <stdlib.h> +#include <math.h> + +#include "fb.h" + +#include "ray_camera.h" +#include "ray_color.h" +#include "ray_render_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_object_t	objects[]; +} 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_render_object_t	*object; + +	for (object = render->objects; object->type; object++) { +		float	ood; + +		if (ray_render_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_render_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_render_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); +			float		intensity = light->light.brightness * (1.0 / (ldist * ldist)); +			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); +			} + +			color = ray_3f_mult_scalar(&color, intensity); +#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_render_object_t * find_nearest_intersection(ray_render_t *render, ray_render_object_t *reflector, ray_ray_t *ray, unsigned depth, float *res_distance) +{ +	ray_render_object_t	*nearest_object = NULL; +	float			nearest_object_distance = INFINITY; +	ray_render_object_t	*object; + +	for (object = render->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_render_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_render_object_t	*reflector = NULL; +	float			relevance = 1.0f, reflectivity; +	unsigned		depth = 0; +	ray_ray_t		reflected_ray, *ray = primary_ray; + +	do { +		ray_render_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_render_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(const ray_scene_t *scene, const ray_camera_t *camera) +{ +	ray_render_t	*render; +	ray_object_t	*object; +	unsigned	i; + +	for (i = 0, object = scene->objects; object->type; object++) +		i++; + +	render = malloc(sizeof(ray_render_t) + i * sizeof(ray_render_object_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 (i = 0, object = scene->objects; object->type; object++) +		render->objects[i++] = ray_render_object_prepare(object, camera); + +	return render; +} + + +void ray_render_free(ray_render_t *render) +{ +	free(render); +} diff --git a/src/libs/ray/ray_render.h b/src/libs/ray/ray_render.h new file mode 100644 index 0000000..dfd769f --- /dev/null +++ b/src/libs/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(const ray_scene_t *scene, const 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/libs/ray/ray_render_object.h b/src/libs/ray/ray_render_object.h new file mode 100644 index 0000000..1136eee --- /dev/null +++ b/src/libs/ray/ray_render_object.h @@ -0,0 +1,121 @@ +#ifndef _RAY_RENDER_OBJECT_H +#define _RAY_RENDER_OBJECT_H + +#include <assert.h> + +#include "ray_camera.h" +#include "ray_object.h" +#include "ray_object_type.h" +#include "ray_render_object_plane.h" +#include "ray_render_object_point.h" +#include "ray_render_object_sphere.h" +#include "ray_ray.h" +#include "ray_surface.h" + +typedef union ray_render_object_t { +	ray_object_type_t		type; +	ray_render_object_sphere_t	sphere; +	ray_render_object_point_t	point; +	ray_render_object_plane_t	plane; +} ray_render_object_t; + + + +/* Prepare an object for rendering. + * If the object has any pre-calculating to do, this is where it happens. + * The pre-calculated stuff is object-resident under a _prepared struct member. + */ +static inline ray_render_object_t ray_render_object_prepare(const ray_object_t *object, const ray_camera_t *camera) +{ +	ray_render_object_t	prepared = { .type = object->type }; + +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		prepared.sphere = ray_render_object_sphere_prepare(&object->sphere, camera); +		break; + +	case RAY_OBJECT_TYPE_POINT: +		prepared.point = ray_render_object_point_prepare(&object->point, camera); +		break; + +	case RAY_OBJECT_TYPE_PLANE: +		prepared.plane = ray_render_object_plane_prepare(&object->plane, camera); +		break; + +	case RAY_OBJECT_TYPE_LIGHT: +		/* TODO */ +		break; + +	default: +		assert(0); +	} + +	return prepared; +} + + +/* Determine if a ray intersects object. + * If the object is intersected, store where along the ray the intersection occurs in res_distance. + */ +static inline int ray_render_object_intersects_ray(ray_render_object_t *object, unsigned depth, ray_ray_t *ray, float *res_distance) +{ +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		return ray_render_object_sphere_intersects_ray(&object->sphere, depth, ray, res_distance); + +	case RAY_OBJECT_TYPE_POINT: +		return ray_render_object_point_intersects_ray(&object->point, depth, ray, res_distance); + +	case RAY_OBJECT_TYPE_PLANE: +		return ray_render_object_plane_intersects_ray(&object->plane, depth, ray, res_distance); + +	case RAY_OBJECT_TYPE_LIGHT: +		/* TODO */ +	default: +		assert(0); +	} +} + + +/* Return the surface normal of object @ point */ +static inline ray_3f_t ray_render_object_normal(ray_render_object_t *object, ray_3f_t *point) +{ +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		return ray_render_object_sphere_normal(&object->sphere, point); + +	case RAY_OBJECT_TYPE_POINT: +		return ray_render_object_point_normal(&object->point, point); + +	case RAY_OBJECT_TYPE_PLANE: +		return ray_render_object_plane_normal(&object->plane, point); + +	case RAY_OBJECT_TYPE_LIGHT: +		/* TODO */ +	default: +		assert(0); +	} +} + + +/* Return the surface of object @ point */ +static inline ray_surface_t ray_render_object_surface(ray_render_object_t *object, ray_3f_t *point) +{ +	switch (object->type) { +	case RAY_OBJECT_TYPE_SPHERE: +		return ray_render_object_sphere_surface(&object->sphere, point); + +	case RAY_OBJECT_TYPE_POINT: +		return ray_render_object_point_surface(&object->point, point); + +	case RAY_OBJECT_TYPE_PLANE: +		return ray_render_object_plane_surface(&object->plane, point); + +	case RAY_OBJECT_TYPE_LIGHT: +		/* TODO */ +	default: +		assert(0); +	} +} + +#endif diff --git a/src/libs/ray/ray_render_object_plane.h b/src/libs/ray/ray_render_object_plane.h new file mode 100644 index 0000000..6e00089 --- /dev/null +++ b/src/libs/ray/ray_render_object_plane.h @@ -0,0 +1,61 @@ +#ifndef _RAY_RENDER_OBJECT_PLANE_H +#define _RAY_RENDER_OBJECT_PLANE_H + +#include "ray_3f.h" +#include "ray_camera.h" +#include "ray_object_plane.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_render_object_plane_t { +	ray_object_plane_t	object; +	float			primary_dot_plus; +} ray_render_object_plane_t; + + +static ray_render_object_plane_t ray_render_object_plane_prepare(const ray_object_plane_t *plane, const ray_camera_t *camera) +{ +	ray_render_object_plane_t	prepared = { .object = *plane }; + +	prepared.primary_dot_plus = ray_3f_dot(&plane->normal, &camera->position) + plane->distance; + +	return prepared; +} + + +static inline int ray_render_object_plane_intersects_ray(ray_render_object_plane_t *plane, unsigned depth, ray_ray_t *ray, float *res_distance) +{ +	float	d = ray_3f_dot(&plane->object.normal, &ray->direction); + +	if (d < 0.0f) { +		float	distance = plane->primary_dot_plus; + +		if (depth) +			distance = (ray_3f_dot(&plane->object.normal, &ray->origin) + plane->object.distance); + +		distance /= -d; +		if (distance > 0.0f) { +			*res_distance = distance; + +			return 1; +		} +	} + +	return 0; +} + + +static inline ray_3f_t ray_render_object_plane_normal(ray_render_object_plane_t *plane, ray_3f_t *point) +{ +	return plane->object.normal; +} + + +static inline ray_surface_t ray_render_object_plane_surface(ray_render_object_plane_t *plane, ray_3f_t *point) +{ +	return plane->object.surface; +} + +#endif diff --git a/src/libs/ray/ray_render_object_point.h b/src/libs/ray/ray_render_object_point.h new file mode 100644 index 0000000..52c6fd6 --- /dev/null +++ b/src/libs/ray/ray_render_object_point.h @@ -0,0 +1,45 @@ +#ifndef _RAY_RENDER_OBJECT_POINT_H +#define _RAY_RENDER_OBJECT_POINT_H + +#include "ray_3f.h" +#include "ray_camera.h" +#include "ray_object_point.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_render_object_point_t { +	ray_object_point_t	object; +} ray_render_object_point_t; + + +static ray_render_object_point_t ray_render_object_point_prepare(const ray_object_point_t *point, const ray_camera_t *camera) +{ +	ray_render_object_point_t	prepared = { .object = *point }; + +	return prepared; +} + + +static inline int ray_render_object_point_intersects_ray(ray_render_object_point_t *point, unsigned depth, ray_ray_t *ray, float *res_distance) +{ +	/* TODO: determine a ray:point intersection */ +	return 0; +} + + +static inline ray_3f_t ray_render_object_point_normal(ray_render_object_point_t *point, ray_3f_t *_point) +{ +	ray_3f_t	normal; + +	return normal; +} + + +static inline ray_surface_t ray_render_object_point_surface(ray_render_object_point_t *point, ray_3f_t *_point) +{ +	return point->object.surface; +} + +#endif diff --git a/src/libs/ray/ray_render_object_sphere.h b/src/libs/ray/ray_render_object_sphere.h new file mode 100644 index 0000000..addf1f5 --- /dev/null +++ b/src/libs/ray/ray_render_object_sphere.h @@ -0,0 +1,90 @@ +#ifndef _RAY_RENDER_OBJECT_SPHERE_H +#define _RAY_RENDER_OBJECT_SPHERE_H + +#include <math.h> + +#include "ray_3f.h" +#include "ray_camera.h" +#include "ray_color.h" +#include "ray_object_sphere.h" +#include "ray_object_type.h" +#include "ray_ray.h" +#include "ray_surface.h" + + +typedef struct ray_render_object_sphere_t { +	ray_object_sphere_t	object; +	ray_3f_t		primary_v; +	float			primary_dot_vv; +	float			r2; +	float			r_inv; +} ray_render_object_sphere_t; + + +static ray_render_object_sphere_t ray_render_object_sphere_prepare(const ray_object_sphere_t *sphere, const ray_camera_t *camera) +{ +	ray_render_object_sphere_t	prepared = { .object = *sphere }; + +	prepared.primary_v = ray_3f_sub(&sphere->center, &camera->position); +	prepared.primary_dot_vv = ray_3f_dot(&prepared.primary_v, &prepared.primary_v); + +	prepared.r2 = sphere->radius * sphere->radius; + +	/* to divide by radius via multiplication in ray_object_sphere_normal() */ +	prepared.r_inv = 1.0f / sphere->radius; + +	return prepared; +} + + +static inline int ray_render_object_sphere_intersects_ray(ray_render_object_sphere_t *sphere, unsigned depth, ray_ray_t *ray, float *res_distance) +{ +	ray_3f_t	v = sphere->primary_v; +	float		dot_vv = sphere->primary_dot_vv; +	float		b, disc; + +	if (depth) { +		v = ray_3f_sub(&sphere->object.center, &ray->origin); +		dot_vv = ray_3f_dot(&v, &v); +	} + +	b = ray_3f_dot(&v, &ray->direction); +	disc = sphere->r2 - (dot_vv - (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_render_object_sphere_normal(ray_render_object_sphere_t *sphere, ray_3f_t *point) +{ +	ray_3f_t	normal; + +	normal = ray_3f_sub(point, &sphere->object.center); +	normal = ray_3f_mult_scalar(&normal, sphere->r_inv);	/* normalize without the sqrt() */ + +	return normal; +} + + +/* return the surface of the sphere @ point */ +static inline ray_surface_t ray_render_object_sphere_surface(ray_render_object_sphere_t *sphere, ray_3f_t *point) +{ +	/* uniform solids for now... */ +	return sphere->object.surface; +} + +#endif diff --git a/src/libs/ray/ray_scene.h b/src/libs/ray/ray_scene.h new file mode 100644 index 0000000..ff9c440 --- /dev/null +++ b/src/libs/ray/ray_scene.h @@ -0,0 +1,16 @@ +#ifndef _RAY_SCENE_H +#define _RAY_SCENE_H + +#include "ray_color.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_scene_t; + +#endif diff --git a/src/libs/ray/ray_surface.h b/src/libs/ray/ray_surface.h new file mode 100644 index 0000000..2e7544b --- /dev/null +++ b/src/libs/ray/ray_surface.h @@ -0,0 +1,15 @@ +#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		highlight_exponent; +	float		diffuse; +} ray_surface_t; + +#endif | 
