feat(examples): add shading, the threaded render loop and two scenes

Lambertian and metal shading with recursive bounces, a row-claiming loop
across worker threads, and the studio and rings scene files.
This commit is contained in:
milner committed 2022-07-02 12:49:50 +00:00
1 parent 09d072a658
commit 13409a1e8b
4 files changed
+167

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
# A ring of alternating metal and diffuse spheres around a central emitter.
camera 0 3.2 8.0 0 0.6 0 30
background 0.010 0.012 0.020
sphere 0.0 -500.0 0.0 500.0 lambert 0.55 0.55 0.58
sphere 2.60 0.55 0.00 0.55 metal 0.95 0.80 0.45 0.02
sphere 1.84 0.55 1.84 0.55 lambert 0.85 0.30 0.30
sphere 0.00 0.55 2.60 0.55 metal 0.85 0.85 0.90 0.10
sphere -1.84 0.55 1.84 0.55 lambert 0.30 0.75 0.45
sphere -2.60 0.55 0.00 0.55 metal 0.60 0.70 0.95 0.02
sphere -1.84 0.55 -1.84 0.55 lambert 0.80 0.55 0.20
sphere 0.00 0.55 -2.60 0.55 metal 0.90 0.90 0.90 0.00
sphere 1.84 0.55 -1.84 0.55 lambert 0.45 0.35 0.80
sphere 0.00 1.10 0.00 0.70 light 4.20 3.60 2.60
+13
View File
@@ -0,0 +1,13 @@
# Three spheres on a ground plane, lit by one emissive sphere overhead.
camera 0 1.4 7.5 0 0.7 0 34
background 0.020 0.028 0.045
# centre radius material albedo fuzz
sphere 0.0 -500.0 0.0 500.0 lambert 0.62 0.62 0.60
sphere -2.1 0.75 0.1 0.75 lambert 0.86 0.24 0.22
sphere 0.0 0.85 -0.3 0.85 metal 0.92 0.88 0.78 0.03
sphere 2.1 0.70 0.2 0.70 lambert 0.22 0.42 0.86
sphere 0.6 0.32 1.5 0.32 metal 0.75 0.75 0.80 0.28
sphere -0.9 0.28 1.7 0.28 lambert 0.90 0.72 0.20
sphere 0.0 6.20 1.0 3.20 light 3.60 3.40 3.00
+76
View File
@@ -0,0 +1,76 @@
#include "rt_config.h"
#include "rt_scene.h"
#include <math.h>
#include <stdlib.h>
#if defined(HAVE_PTHREAD_CREATE) && defined(HAVE_PTHREAD_H)
#include <pthread.h>
#define RT_THREADED 1
#endif
static unsigned char to_srgb(double linear) {
double c = linear <= 0.0031308 ? linear * 12.92 : 1.055 * pow(linear, 1.0 / 2.4) - 0.055;
if (c < 0.0) c = 0.0;
if (c > 1.0) c = 1.0;
return (unsigned char)(c * 255.0 + 0.5);
}
static void render_row(render_job *job, int y) {
rng rs;
rng_seed(&rs, (unsigned long long)y * 0x9E3779B97F4A7C15ULL + 1u);
for (int x = 0; x < job->width; x++) {
vec3 sum = v3(0.0, 0.0, 0.0);
for (int s = 0; s < job->samples; s++) {
double u = ((double)x + rng_unit(&rs)) / (double)job->width;
double v = 1.0 - ((double)y + rng_unit(&rs)) / (double)job->height;
ray r = camera_ray(job->cam, u, v);
sum = v3add(sum, ray_colour(job->sc, r, job->max_depth, &rs));
}
vec3 c = v3scale(sum, 1.0 / (double)job->samples);
unsigned char *px = job->rgb + ((size_t)y * (size_t)job->width + (size_t)x) * 3u;
px[0] = to_srgb(c.x);
px[1] = to_srgb(c.y);
px[2] = to_srgb(c.z);
}
}
#ifdef RT_THREADED
static pthread_mutex_t row_lock = PTHREAD_MUTEX_INITIALIZER;
static void *worker(void *arg) {
render_job *job = (render_job *)arg;
for (;;) {
pthread_mutex_lock(&row_lock);
int y = job->row_next++;
pthread_mutex_unlock(&row_lock);
if (y >= job->height) break;
render_row(job, y);
}
return NULL;
}
#endif
int render_run(render_job *job) {
job->row_next = 0;
#ifdef RT_THREADED
int n = job->threads;
if (n < 1) n = 1;
if (n > 64) n = 64;
if (n > 1) {
pthread_t *ts = (pthread_t *)calloc((size_t)n, sizeof *ts);
if (ts) {
int started = 0;
for (int i = 0; i < n; i++)
if (pthread_create(&ts[i], NULL, worker, job) == 0) started++;
for (int i = 0; i < started; i++) pthread_join(ts[i], NULL);
free(ts);
if (started > 0) return started;
}
}
#endif
for (int y = 0; y < job->height; y++) render_row(job, y);
return 1;
}
+61
View File
@@ -0,0 +1,61 @@
#include "rt_scene.h"
#include <math.h>
static int scatter(const hit *h, ray in, rng *rs, ray *out, vec3 *attenuation) {
switch (h->mat.kind) {
case MAT_LAMBERT: {
vec3 dir = v3add(h->normal, rng_unit_vector(rs));
if (v3len2(dir) < 1e-12) dir = h->normal;
out->origin = h->point;
out->dir = v3norm(dir);
*attenuation = h->mat.albedo;
return 1;
}
case MAT_METAL: {
vec3 r = v3reflect(v3norm(in.dir), h->normal);
vec3 dir = v3add(r, v3scale(rng_in_sphere(rs), h->mat.fuzz));
out->origin = h->point;
out->dir = v3norm(dir);
*attenuation = h->mat.albedo;
return v3dot(out->dir, h->normal) > 0.0;
}
case MAT_LIGHT:
default:
return 0;
}
}
vec3 ray_colour(const scene *s, ray r, int depth, rng *rs) {
vec3 accumulated = v3(0.0, 0.0, 0.0);
vec3 throughput = v3(1.0, 1.0, 1.0);
for (int bounce = 0; bounce < depth; bounce++) {
hit h;
if (!scene_hit(s, r, 1e-4, 1e30, &h)) {
accumulated = v3add(accumulated, v3mul(throughput, s->background));
break;
}
if (h.mat.kind == MAT_LIGHT) {
accumulated = v3add(accumulated, v3mul(throughput, h.mat.albedo));
break;
}
ray next;
vec3 attenuation;
if (!scatter(&h, r, rs, &next, &attenuation)) break;
throughput = v3mul(throughput, attenuation);
r = next;
/* Russian roulette once the path stops carrying much energy. */
if (bounce > 3) {
double p = fmax(throughput.x, fmax(throughput.y, throughput.z));
if (p < 1.0) {
if (rng_unit(rs) > p) break;
throughput = v3scale(throughput, 1.0 / p);
}
}
}
return accumulated;
}