diff --git a/demo.c b/demo.c index 7ec8c1d..5ccf95c 100644 --- a/demo.c +++ b/demo.c @@ -24,8 +24,6 @@ static pix_t pixbuf[W * H]; int main() { - rng_init(); - img_t img = { .pix = pixbuf }; camera_t camera = camera_init(camera_pos, FOCAL_LEN, VIEWPORT_H, W, H); camera_render(&camera, scene, NELEMS(scene), &img); diff --git a/include/rng.h b/include/rng.h index 17fda3c..e16b01a 100644 --- a/include/rng.h +++ b/include/rng.h @@ -3,9 +3,15 @@ #include "vec3.h" -void rng_init(); +#include -double rng_double(); -vec3_t rng_vec3(); +typedef struct { + uint32_t state; +} rng_t; + +rng_t rng_init(unsigned seed); + +double rng_double(rng_t *rng); +vec3_t rng_vec3(rng_t *rng); #endif diff --git a/src/camera.c b/src/camera.c index 86669ea..f91b30b 100644 --- a/src/camera.c +++ b/src/camera.c @@ -5,24 +5,36 @@ #include #include +#include #define MAX_ITER 10 #define MIN_T 1e-6 #define NSAMPLES 100 +#define NTHREADS 40 + +typedef struct { + const camera_t *camera; + const obj_t *scene; + pix_t *pixels; + rng_t rng; + unsigned start_y, row_count, scene_count; +} work_slice_t; + 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) +static ray_t scatter(hit_t hit, rng_t *rng) { - const vec3_t rand = rng_vec3(); + const vec3_t rand = rng_vec3(rng); 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) +static vec3_t +trace(ray_t ray, const obj_t *scene, unsigned scene_count, rng_t *rng) { double coeff = 1.0; for (unsigned i = 0; i < MAX_ITER; ++i) { @@ -37,7 +49,7 @@ static vec3_t trace(ray_t ray, const obj_t *scene, unsigned scene_count) coeff); } - ray = scatter(hit); + ray = scatter(hit, rng); coeff *= 0.5; } @@ -52,6 +64,39 @@ static void setpix(vec3_t col, pix_t *out) out->a = UINT16_MAX; } +static int render_thread(void *arg) +{ + work_slice_t *slice = (work_slice_t *)arg; + const camera_t *camera = slice->camera; + const uint32_t w = camera->img_width; + + const unsigned stop_y = slice->start_y + slice->row_count; + for (unsigned y = slice->start_y; y < stop_y; ++y) { + 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, slice->scene, slice->scene_count, &slice->rng); + colour = vec3_add(colour, vec3_scale(sample, weight)); + } + + setpix(colour, slice->pixels + (w * y + x)); + } + } + + return 0; +} + camera_t camera_init( vec3_t pos, double focal_len, double viewport_height, uint32_t img_width, uint32_t img_height) @@ -83,32 +128,29 @@ 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; + img_out->w = camera->img_width; + img_out->h = camera->img_height; - for (unsigned y = 0; y < h; ++y) { - fprintf(stderr, "\r[%3d/%3d]", y, h); - fflush(stderr); + const unsigned rows_per_thread = img_out->h / NTHREADS; + const unsigned rem_rows = img_out->h % NTHREADS; - 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), - }; + thrd_t threads[NTHREADS]; + work_slice_t slices[NTHREADS]; + for (unsigned i = 0; i < NTHREADS; ++i) { + slices[i].camera = camera; + slices[i].scene = scene; + slices[i].scene_count = scene_count; + slices[i].pixels = img_out->pix; + slices[i].rng = rng_init(i); - 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)); - } + slices[i].start_y = i * rows_per_thread; + slices[i].row_count = rows_per_thread; + if (rem_rows != 0 && i == NTHREADS - 1) + slices[i].row_count += rem_rows; - setpix(colour, img_out->pix + (w * y + x)); - } + thrd_create(threads + i, render_thread, slices + i); } - fprintf(stderr, "\r[%3d/%3d]\n", h, h); + for (unsigned i = 0; i < NTHREADS; ++i) + thrd_join(threads[i], 0); } diff --git a/src/rng.c b/src/rng.c index 5bd179e..364157b 100644 --- a/src/rng.c +++ b/src/rng.c @@ -1,23 +1,31 @@ #include "rng.h" #include -#include #include -void rng_init() +static uint32_t next(rng_t *rng) +{ + uint32_t x = rng->state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return rng->state = x; +} + +rng_t rng_init(unsigned seed) { struct timeval tv; - gettimeofday(&tv, NULL); - srand(tv.tv_usec); + gettimeofday(&tv, nullptr); + return (rng_t) { .state = tv.tv_usec + seed }; } -double rng_double() +double rng_double(rng_t *rng) { - return 2.0 * (double)rand() / (double)INT_MAX - 1.0; + return 2.0 * (double)next(rng) / (double)UINT32_MAX - 1.0; } -vec3_t rng_vec3() +vec3_t rng_vec3(rng_t *rng) { - const vec3_t v = { rng_double(), rng_double(), rng_double() }; + const vec3_t v = { rng_double(rng), rng_double(rng), rng_double(rng) }; return vec3_unit(v); }