summaryrefslogtreecommitdiff
path: root/src/modules/ray/ray_camera.c
blob: 8a06aa3384ca26389365ea3eb43d3c97c7cead95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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_lerp(&frame->nw, &frame->sw, frame->y_alpha);
	frame->cur_e = ray_3f_lerp(&frame->ne, &frame->se, frame->y_alpha);

	ray->origin = camera->position;
	ray->direction = ray_3f_normalize(&frame->cur_w);
}
© All Rights Reserved