raytracer-c/module_raytracer/camera.c

207 lines
6.3 KiB
C

#include "camera.h"
#include "matrix.h"
#include "tuples.h"
#include "yamlloader.h"
#include <assert.h>
#include <logger.h>
#include <math.h>
#include <memory.h>
static void calculate_pixel_size(CAMERA_Camera *camera) {
assert(camera);
double half_view = tan(camera->field_of_view / 2.0);
double aspect = camera->hsize / (double)camera->vsize;
if (aspect >= 1) {
camera->half_width = half_view;
camera->half_height = half_view / aspect;
} else {
camera->half_width = half_view * aspect;
camera->half_height = half_view;
}
camera->pixel_size = (camera->half_width * 2.0) / camera->hsize;
}
CAMERA_Camera *CAMERA_new(unsigned int hsize, unsigned int vsize, double field_of_view) {
assert(hsize > 0);
assert(vsize > 0);
CAMERA_Camera *camera = malloc(sizeof(CAMERA_Camera));
camera->hsize = hsize;
camera->vsize = vsize;
camera->field_of_view = field_of_view;
camera->transform = MATRIX_new_identity(4);
camera->inv_transform = MATRIX_new_identity(4);
MATRIX_inverse(camera->inv_transform, camera->transform);
calculate_pixel_size(camera);
return camera;
}
void CAMERA_delete(CAMERA_Camera *camera) {
assert(camera);
assert(camera->transform);
MATRIX_delete_all(camera->transform, camera->inv_transform);
free(camera);
}
MATRIX_Matrix *CAMERA_view_transform(const TUPLES_Point *from, const TUPLES_Point *to, const TUPLES_Vector *up) {
assert(from);
assert(to);
assert(up);
TUPLES_Vector forward;
TUPLES_subtract(&forward, to, from);
TUPLES_normalize(&forward);
TUPLES_Vector upn;
TUPLES_copy(&upn, up);
TUPLES_normalize(&upn);
TUPLES_Vector left;
TUPLES_cross(&left, &forward, &upn);
TUPLES_Vector true_up;
TUPLES_cross(&true_up, &left, &forward);
MATRIX_Matrix orientation;
MATRIX_init(&orientation, 4, 4);
MATRIX_fill(&orientation, left.x, left.y, left.z, 0.0, true_up.x, true_up.y, true_up.z, 0.0, -forward.x, -forward.y, -forward.z, 0.0, 0.0, 0.0, 0.0, 1.0);
MATRIX_Matrix *translation = MATRIX_new_translation(-from->x, -from->y, -from->z);
MATRIX_Matrix *view = MATRIX_multiply(&orientation, translation);
MATRIX_delete(translation);
return view;
}
void CAMERA_set_transform(CAMERA_Camera *camera, MATRIX_Matrix *transform) {
assert(camera);
assert(transform);
assert(MATRIX_is_invertible(transform));
MATRIX_copy(camera->transform, transform);
MATRIX_inverse(camera->inv_transform, camera->transform);
}
void CAMERA_ray_for_pixel(RAY_Ray *dest, const CAMERA_Camera *camera, unsigned int px, unsigned int py) {
assert(dest);
assert(camera);
assert(px < camera->hsize);
assert(py < camera->vsize);
double xoffset = (px + 0.5) * camera->pixel_size;
double yoffset = (py + 0.5) * camera->pixel_size;
double world_x = camera->half_width - xoffset;
double world_y = camera->half_height - yoffset;
TUPLES_Point ptmp, pixel, origin;
TUPLES_init_point(&ptmp, world_x, world_y, -1.0);
MATRIX_multiply_tuple(&pixel, camera->inv_transform, &ptmp);
TUPLES_init_point(&ptmp, 0, 0, 0);
MATRIX_multiply_tuple(&origin, camera->inv_transform, &ptmp);
TUPLES_Vector direction;
TUPLES_subtract(&direction, &pixel, &origin);
TUPLES_normalize(&direction);
RAY_init_from_tuples(dest, &origin, &direction);
}
CANVAS_Canvas *CAMERA_render(const CAMERA_Camera *camera, const WORLD_World *world) {
assert(camera);
assert(world);
CANVAS_Canvas *canvas = CANVAS_new(camera->hsize, camera->vsize);
const uint total_pixels = camera->vsize * camera->hsize;
const uint one_percent_pixels = total_pixels / 100;
uint pixel_count = 0;
#ifdef _OPENMP
#pragma omp parallel for collapse(2) schedule(dynamic)
#endif
for (uint y = 0; y < camera->vsize - 1; y++) {
for (uint x = 0; x < camera->hsize - 1; x++) {
uint this_pixel_count;
#ifdef _OPENMP
#pragma omp atomic capture
#endif
this_pixel_count = pixel_count++;
if (this_pixel_count % one_percent_pixels == 0) {
const uint percent = this_pixel_count * 100 / total_pixels;
LOGGER_log(LOGGER_INFO, "Rendering %u%% complete\n", percent);
}
RAY_Ray ray;
TUPLES_Color color;
CAMERA_ray_for_pixel(&ray, camera, x, y);
WORLD_color_at(&color, world, &ray, WORLD_default_ttl);
CANVAS_write_pixel(canvas, x, y, &color);
}
}
return canvas;
}
struct camera_context {
unsigned int width, height;
double field_of_view;
TUPLES_Point from, to;
TUPLES_Vector up;
bool found_width, found_height, found_field_of_view,
found_from, found_to, found_up;
};
static void parse_camera_map_entry(char *key, char *value, void *context) {
assert(key);
assert(value);
assert(context);
struct camera_context *cc = (struct camera_context *)context;
if (strcmp("up", key) == 0) {
YAMLLOADER_parse_vector(value, &cc->up);
cc->found_up = true;
} else if (strcmp("from", key) == 0) {
YAMLLOADER_parse_point(value, &cc->from);
cc->found_from = true;
} else if (strcmp("to", key) == 0) {
YAMLLOADER_parse_point(value, &cc->to);
cc->found_to = true;
} else if (strcmp("width", key) == 0) {
YAMLLOADER_parse_uint(value, &cc->width);
cc->found_width = true;
} else if (strcmp("height", key) == 0) {
YAMLLOADER_parse_uint(value, &cc->height);
cc->found_height = true;
} else if (strcmp("field-of-view", key) == 0 ||
strcmp("field_of_view", key) == 0) {
YAMLLOADER_parse_double(value, &cc->field_of_view);
cc->found_field_of_view = true;
} else {
LOGGER_log(LOGGER_WARN, "Unrecognized map key while parsing camera: %s", key);
}
}
static bool validate_camera_context(struct camera_context *cc) {
assert(cc);
return cc->found_width && cc->found_height && cc->found_field_of_view && cc->found_from
&& cc->found_to && cc->found_up;
}
CAMERA_Camera *CAMERA_parse_camera(char *data) {
assert(data);
struct camera_context cc = {0};
YAMLLOADER_parse_map_entries(data, parse_camera_map_entry, &cc);
if (!validate_camera_context(&cc)) {
LOGGER_log(LOGGER_WARN, "Failed to parse camera. width, height, field of view, from, to, and up all must be specified");
return NULL;
}
CAMERA_Camera *camera = CAMERA_new(cc.width, cc.height, cc.field_of_view);
MATRIX_Matrix* camera_transform = CAMERA_view_transform(&cc.from, &cc.to, &cc.up);
CAMERA_set_transform(camera, camera_transform);
MATRIX_delete(camera_transform);
return camera;
}