115 lines
2.9 KiB
C
115 lines
2.9 KiB
C
#include "camera.h"
|
|
|
|
#include "ray.h"
|
|
#include "rng.h"
|
|
|
|
#include <float.h>
|
|
#include <stdio.h>
|
|
|
|
#define MAX_ITER 10
|
|
#define MIN_T 1e-6
|
|
#define NSAMPLES 100
|
|
|
|
static const vec3_t lightblue = { 0.4, 0.6, 1.0 };
|
|
static const vec3_t white = { 1.0, 1.0, 1.0 };
|
|
static const vec3_t black = { 0.0, 0.0, 0.0 };
|
|
|
|
static ray_t scatter(hit_t hit)
|
|
{
|
|
const vec3_t rand = rng_vec3();
|
|
const vec3_t dir
|
|
= vec3_dot(rand, hit.normal) > 0.0 ? rand : vec3_scale(rand, -1.0);
|
|
return (ray_t) { .orig = hit.point, .dir = dir };
|
|
}
|
|
|
|
static vec3_t trace(ray_t ray, const obj_t *scene, unsigned scene_count)
|
|
{
|
|
double coeff = 1.0;
|
|
for (unsigned i = 0; i < MAX_ITER; ++i) {
|
|
hit_t hit = { .t = DBL_MAX };
|
|
for (unsigned j = 0; j < scene_count; ++j)
|
|
scene[j].intersect(scene[j].params, ray, &hit, MIN_T, hit.t);
|
|
|
|
if (hit.t == DBL_MAX) {
|
|
const double a = (ray.dir.y + 1.0) / 2.0;
|
|
return vec3_scale(
|
|
vec3_add(vec3_scale(lightblue, a), vec3_scale(white, 1 - a)),
|
|
coeff);
|
|
}
|
|
|
|
ray = scatter(hit);
|
|
coeff *= 0.5;
|
|
}
|
|
|
|
return black;
|
|
}
|
|
|
|
static void setpix(vec3_t col, pix_t *out)
|
|
{
|
|
out->r = UINT16_MAX * col.x;
|
|
out->g = UINT16_MAX * col.y;
|
|
out->b = UINT16_MAX * col.z;
|
|
out->a = UINT16_MAX;
|
|
}
|
|
|
|
camera_t camera_init(
|
|
vec3_t pos, double focal_len, double viewport_height, uint32_t img_width,
|
|
uint32_t img_height)
|
|
{
|
|
const double aspect = (double)img_width / (double)img_height;
|
|
const double viewport_width = viewport_height * aspect;
|
|
const vec3_t viewport_disp = { 0, 0, focal_len };
|
|
const vec3_t u = { viewport_width, 0, 0 };
|
|
const vec3_t v = { 0, -viewport_height, 0 };
|
|
const vec3_t topleft = vec3_sub(
|
|
vec3_sub(pos, viewport_disp), vec3_scale(vec3_add(u, v), 0.5));
|
|
|
|
const vec3_t x_step = vec3_scale(u, 1.0 / (double)img_width);
|
|
const vec3_t y_step = vec3_scale(v, 1.0 / (double)img_height);
|
|
const vec3_t pix_origin
|
|
= vec3_add(topleft, vec3_scale(vec3_add(x_step, y_step), 0.5));
|
|
|
|
return (camera_t) {
|
|
.pos = pos,
|
|
.pix_origin = pix_origin,
|
|
.x_step = x_step,
|
|
.y_step = y_step,
|
|
.img_width = img_width,
|
|
.img_height = img_height,
|
|
};
|
|
}
|
|
|
|
void camera_render(
|
|
const camera_t *camera, const obj_t *scene, unsigned scene_count,
|
|
img_t *img_out)
|
|
{
|
|
const uint32_t w = img_out->w = camera->img_width;
|
|
const uint32_t h = img_out->h = camera->img_height;
|
|
|
|
for (unsigned y = 0; y < h; ++y) {
|
|
fprintf(stderr, "\r[%3d/%3d]", y, h);
|
|
fflush(stderr);
|
|
|
|
const vec3_t row
|
|
= vec3_add(camera->pix_origin, vec3_scale(camera->y_step, y));
|
|
for (unsigned x = 0; x < w; ++x) {
|
|
const vec3_t pix = vec3_add(row, vec3_scale(camera->x_step, x));
|
|
const ray_t ray = {
|
|
.orig = camera->pos,
|
|
.dir = vec3_unit(pix),
|
|
};
|
|
|
|
vec3_t colour = black;
|
|
for (unsigned i = 0; i < NSAMPLES; ++i) {
|
|
const double weight = 1.0 / NSAMPLES;
|
|
const vec3_t sample = trace(ray, scene, scene_count);
|
|
colour = vec3_add(colour, vec3_scale(sample, weight));
|
|
}
|
|
|
|
setpix(colour, img_out->pix + (w * y + x));
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "\r[%3d/%3d]\n", h, h);
|
|
}
|