raytracer

raytracer.git
git clone git://git.lenczewski.org/raytracer.git
Log | Files | Refs | README | LICENSE

commit 3a9fe44d65a54a96c824025df4b4237d9799da8a
parent eb9dc03a5a62e74b8b1fc459c6067fc6e4aec848
Author: MikoĊ‚aj Lenczewski <mblenczewski@gmail.com>
Date:   Mon,  9 Jun 2025 23:32:07 +0100

Implemented up to chapter 8 of book 1.

Diffstat:
Art1/bitmap.c | 11+++++++++++
Mrt1/main.c | 57++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Art1/math.c | 1+
Art1/memory.c | 25+++++++++++++++++++++++++
Art1/raytracing.c | 14++++++++++++++
Art1/rt.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art1/rt.h | 538+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Art1/utils.c | 11+++++++++++
8 files changed, 734 insertions(+), 1 deletion(-)

diff --git a/rt1/bitmap.c b/rt1/bitmap.c @@ -0,0 +1,11 @@ +#include "rt.h" + +extern inline pixel_t +pixel_argb32(uint8_t a, uint8_t r, uint8_t g, uint8_t b); + +extern inline void +pixel_format_set_masks(enum pixel_format format, + uint32_t *r, uint32_t *g, uint32_t *b, uint32_t *a); + +extern inline void +bitmap_write(int fd, pixel_t *buf, size_t width, size_t height, enum pixel_format format); diff --git a/rt1/main.c b/rt1/main.c @@ -1,5 +1,60 @@ +#include "rt.h" + int main(int argc, char **argv) { - return 0; + char *filepath = "/tmp/test.bmp"; + + int fd = open(filepath, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + fprintf(stderr, "Failed to open file: %s: ", filepath); + perror("open"); + exit(EXIT_FAILURE); + } + + // =================================================================== + + // image + size_t width = 1024, height = 1024; + pixel_t *buf = malloc(sizeof *buf * width * height); + assert(buf); + + // world + struct world world; + + world_init(&world); + + struct sphere sphere = { + .centre = VEC3(point_t, 0, 0, -1), + .radius = 0.5, + + .hittable.impl = sphere_hittable_impl, + }; + + world_push(&world, &sphere.hittable); + + struct sphere ground = { + .centre = VEC3(point_t, 0, -100.5, -1), + .radius = 100, + + .hittable.impl = sphere_hittable_impl, + }; + + world_push(&world, &ground.hittable); + + // rendering + render(buf, width, height, &world); + + // =================================================================== + + bitmap_write(fd, buf, width, height, PIXEL_FORMAT_ARGB32); + + close(fd); + + exit(EXIT_SUCCESS); } + +#include "math.c" +#include "raytracing.c" +#include "bitmap.c" +#include "rt.c" diff --git a/rt1/math.c b/rt1/math.c @@ -0,0 +1 @@ +#include "rt.h" diff --git a/rt1/memory.c b/rt1/memory.c @@ -0,0 +1,25 @@ +#include "rt.h" + +extern inline void +arena_reset(struct arena *arena); + +extern inline void * +arena_alloc(struct arena *arena, size_t size, size_t alignment); + +extern inline void +list_node_link(struct list_node *node, struct list_node *prev, struct list_node *next); + +extern inline struct list_node * +list_node_unlink(struct list_node *node); + +extern inline void +list_push_head(struct list_node *list, struct list_node *node); + +extern inline void +list_push_tail(struct list_node *list, struct list_node *node); + +extern inline struct list_node * +list_pop_head(struct list_node *list); + +extern inline struct list_node * +list_pop_tail(struct list_node *list); diff --git a/rt1/raytracing.c b/rt1/raytracing.c @@ -0,0 +1,14 @@ +#include "rt.h" + +extern inline vec3f_t +ray_at(struct ray const *ray, float t); + +extern inline int +interval_contains(struct interval interval, float v); + +extern inline int +interval_surrounds(struct interval interval, float v); + +extern inline int +sphere_hittable_impl(struct hittable *data, struct ray const *ray, + struct interval t, struct hit_record *record); diff --git a/rt1/rt.c b/rt1/rt.c @@ -0,0 +1,78 @@ +#include "rt.h" + +extern inline void +world_init(struct world *world); + +extern inline void +world_push(struct world *world, struct hittable *hittable); + +inline color_t +ray_color(struct ray *ray, struct list_node *objects) +{ + struct hit_record hit_record; + struct interval t = { .min = 0, .max = INFINITY, }; + int hit_something = 0; + + struct hittable *it; + LIST_ENTRY_ITER(objects, it, list_node) { + if (it->impl(it, ray, t, &hit_record)) { + hit_something = 1; + t.max = hit_record.t; + } + } + + if (hit_something) { + return VEC3MULS(color_t, + VEC3ADDS(vec3f_t, hit_record.normal, 1), 0.5); + } + + vec3f_t unit_direction = VEC3UNIT(ray->direction); + float a = 0.5 * (unit_direction.xyz.y + 1); + return VEC3LERP(VEC31(color_t), VEC3(color_t, 0, 0, 1), a); +} + +void +render(pixel_t *image, size_t image_width, size_t image_height, struct world *world) +{ + // camera + float aspect_ratio = (float) image_width / image_height; + + float focal_length = 1.0; + + vec3f_t camera_centre = VEC30(vec3f_t); + + float viewport_width = 4.0, viewport_height = viewport_width / aspect_ratio; + vec3f_t viewport_u = VEC3(vec3f_t, +viewport_width, 0, 0); + vec3f_t viewport_v = VEC3(vec3f_t, 0, -viewport_height, 0); + vec3f_t pixel_du = VEC3DIVS(vec3f_t, viewport_u, image_width); + vec3f_t pixel_dv = VEC3DIVS(vec3f_t, viewport_v, image_height); + + vec3f_t viewport_origin = VEC3SUB(vec3f_t, + VEC3SUB(vec3f_t, + VEC3SUB(vec3f_t, camera_centre, VEC3(vec3f_t, 0, 0, focal_length)), + VEC3DIVS(vec3f_t, viewport_u, 2)), + VEC3DIVS(vec3f_t, viewport_v, 2)); + + vec3f_t pixel00 = VEC3ADD(vec3f_t, + viewport_origin, + VEC3MULS(vec3f_t, VEC3ADD(vec3f_t, pixel_du, pixel_dv), 0.5)); + + // rendering + for (size_t j = 0; j < image_height; j++) { + fprintf(stderr, "Scanline: %zu/%zu\n", j, image_height); + for (size_t i = 0; i < image_width; i++) { + vec3f_t pixel_centre = VEC3ADD(vec3f_t, + pixel00, + VEC3ADD(vec3f_t, + VEC3MULS(vec3f_t, pixel_du, i), + VEC3MULS(vec3f_t, pixel_dv, j))); + + vec3f_t ray_direction = VEC3SUB(vec3f_t, pixel_centre, camera_centre); + + struct ray ray = { .origin = camera_centre, .direction = ray_direction, }; + color_t color = ray_color(&ray, &world->objects); + + image[j * image_width + i] = pixel_from_color(color, PIXEL_FORMAT_ARGB32); + } + } +} diff --git a/rt1/rt.h b/rt1/rt.h @@ -0,0 +1,538 @@ +#ifndef RT_H +#define RT_H + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> + +// memory utilities +// =========================================================================== + +#include <stdalign.h> +#include <stddef.h> + +#define IS_POW2(v) (((v) & ((v) - 1)) == 0) + +#define IS_ALIGNED(v, align) (((v) & ((align) - 1)) == 0) +#define ALIGN_PREV(v, align) ((v) & ~((align) - 1)) +#define ALIGN_NEXT(v, align) ALIGN_PREV((v) + ((align) - 1), (align)) + +struct arena { + void *ptr; + size_t cap, len; +}; + +inline void +arena_reset(struct arena *arena) +{ + arena->len = 0; +} + +inline void * +arena_alloc(struct arena *arena, size_t size, size_t alignment) +{ + assert(size); + assert(alignment); + assert(IS_POW2(alignment)); + + uintptr_t cur_ptr = (uintptr_t) arena->ptr, end_ptr = cur_ptr + arena->cap; + + uintptr_t aligned_ptr = ALIGN_NEXT(cur_ptr, alignment); + if (aligned_ptr + size > end_ptr) + return NULL; + + arena->len = (aligned_ptr + size) - end_ptr; + + return (void *) aligned_ptr; +} + +#define ARENA_ALLOC_ARRAY(arena, T, n) \ + arena_alloc((arena), sizeof(T) * (n), alignof(T)) + +#define ARENA_ALLOC_SIZED(arena, T) ARENA_ALLOC_ARRAY((arena), T, 1) + +#define TO_PARENT_PTR(ptr, T, member) \ + ((ptr) ? ((T *) (((uintptr_t) (ptr)) - offsetof(T, member))) : NULL) + +struct list_node { + struct list_node *prev, *next; +}; + +#define LIST_INIT(list) ((struct list_node) { &(list), &(list), }) + +#define LIST_HEAD(list) ((list)->next) +#define LIST_TAIL(list) ((list)->prev) + +#define LIST_IS_EMPTY(list) \ + (LIST_HEAD(list) == (list) && LIST_TAIL(list) == (list)) + +#define LIST_NODE_ITER(list, it) \ + for ((it) = LIST_HEAD(list); (it) != (list); (it) = LIST_HEAD(it)) + +#define LIST_NODE_RITER(list, it) \ + for ((it) = LIST_TAIL(list); (it) != (list); (it) = LIST_TAIL(it)) + +#define LIST_NODE_ENTRY(node, T, member) TO_PARENT_PTR((node), T, member) + +#define LIST_ENTRY_ITER(list, it, member) \ + for ((it) = LIST_NODE_ENTRY(LIST_HEAD(list), __typeof__ (*(it)), member); \ + &(it)->member != (list); \ + (it) = LIST_NODE_ENTRY(LIST_HEAD(&(it)->member), __typeof__ (*(it)), member)) + +#define LIST_ENTRY_RITER(list, it, member) \ + for ((it) = LIST_NODE_ENTRY(LIST_TAIL(list), __typeof__ (*(it)), member); \ + &(it)->member != (list); \ + (it) = LIST_NODE_ENTRY(LIST_TAIL(&(it)->member), __typeof__ (*(it)), member)) + +inline void +list_node_link(struct list_node *node, struct list_node *prev, struct list_node *next) +{ + node->prev = prev; + prev->next = node; + node->next = next; + next->prev = node; +} + +inline struct list_node * +list_node_unlink(struct list_node *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + return node; +} + +inline void +list_push_head(struct list_node *list, struct list_node *node) +{ + list_node_link(node, list, LIST_HEAD(list)); +} + +inline void +list_push_tail(struct list_node *list, struct list_node *node) +{ + list_node_link(node, LIST_TAIL(list), list); +} + +inline struct list_node * +list_pop_head(struct list_node *list) +{ + struct list_node *res = list_node_unlink(LIST_HEAD(list)); + return res; +} + +inline struct list_node * +list_pop_tail(struct list_node *list) +{ + struct list_node *res = list_node_unlink(LIST_TAIL(list)); + return res; +} + +// math utilities +// =========================================================================== + +#include <tgmath.h> + +typedef union vec3i { + int32_t vs[3]; + struct { + int32_t x, y, z; + } xyz; +} vec3i_t; + +typedef union vec3u { + uint32_t vs[3]; + struct { + uint32_t x, y, z; + } xyz; +} vec3u_t; + +typedef union vec3f { + float vs[3]; + struct { + float x, y, z; + } xyz; +} vec3f_t; + +typedef union vec3d { + double vs[3]; + struct { + double x, y, z; + } xyz; +} vec3d_t; + +#define VEC3(T, x, y, z) ((T) { .vs = { (x), (y), (z), }, }) + +#define VEC30(T) VEC3(T, 0, 0, 0) +#define VEC31(T) VEC3(T, 1, 1, 1) + +#define VEC3NEG(lhs) \ + VEC3(__typeof__ (lhs), -(lhs).xyz.x, -(lhs).xyz.y, -(lhs).xyz.z) + +#define VEC3ADD(T, lhs, rhs) \ + VEC3(T, (lhs).xyz.x + (rhs).xyz.x, (lhs).xyz.y + (rhs).xyz.y, (lhs).xyz.z + (rhs).xyz.z) +#define VEC3SUB(T, lhs, rhs) \ + VEC3(T, (lhs).xyz.x - (rhs).xyz.x, (lhs).xyz.y - (rhs).xyz.y, (lhs).xyz.z - (rhs).xyz.z) +#define VEC3MUL(T, lhs, rhs) \ + VEC3(T, (lhs).xyz.x * (rhs).xyz.x, (lhs).xyz.y * (rhs).xyz.y, (lhs).xyz.z * (rhs).xyz.z) +#define VEC3DIV(T, lhs, rhs) \ + VEC3(T, (lhs).xyz.x / (rhs).xyz.x, (lhs).xyz.y / (rhs).xyz.y, (lhs).xyz.z / (rhs).xyz.z) + +#define VEC3ADDS(T, lhs, scalar) \ + VEC3(T, (lhs).xyz.x + scalar, (lhs).xyz.y + scalar, (lhs).xyz.z + scalar) +#define VEC3SUBS(T, lhs, scalar) \ + VEC3(T, (lhs).xyz.x - scalar, (lhs).xyz.y - scalar, (lhs).xyz.z - scalar) +#define VEC3MULS(T, lhs, scalar) \ + VEC3(T, (lhs).xyz.x * scalar, (lhs).xyz.y * scalar, (lhs).xyz.z * scalar) +#define VEC3DIVS(T, lhs, scalar) \ + VEC3(T, (lhs).xyz.x / scalar, (lhs).xyz.y / scalar, (lhs).xyz.z / scalar) + +#define VEC3LEN2(lhs) \ + (((lhs).xyz.x * (lhs).xyz.x) + ((lhs).xyz.y * (lhs).xyz.y) + ((lhs).xyz.z * (lhs).xyz.z)) + +#define VEC3LEN(lhs) sqrt(VEC3LEN2(lhs)) + +#define VEC3LERP(a, b, t) \ + VEC3ADD(__typeof__ (a), \ + VEC3MULS(__typeof__ (a), (a), (1 - (t))), \ + VEC3MULS(__typeof__ (b), (b), (t))) + +#define VEC3DOT(lhs, rhs) \ + (((lhs).xyz.x * (rhs).xyz.x) + ((lhs).xyz.y * (rhs).xyz.y) + ((lhs).xyz.z * (rhs).xyz.z)) + +#define VEC3CROSS(T, lhs, rhs) \ + VEC3(T, \ + (((lhs).xyz.y * (rhs).xyz.z) - ((lhs).xyz.z * (rhs).xyz.y)), \ + (((lhs).xyz.z * (rhs).xyz.x) - ((lhs).xyz.x * (rhs).xyz.z)), \ + (((lhs).xyz.x * (rhs).xyz.y) - ((lhs).xyz.y * (rhs).xyz.x))) + +#define VEC3UNIT(lhs) \ + VEC3(__typeof__ (lhs), \ + (lhs).xyz.x / VEC3LEN(lhs), \ + (lhs).xyz.y / VEC3LEN(lhs), \ + (lhs).xyz.z / VEC3LEN(lhs)) + +typedef union vec4i { + int32_t vs[4]; + struct { + int32_t x, y, z, w; + } xyzw; +} vec4i_t; + +typedef union vec4u { + uint32_t vs[4]; + struct { + uint32_t x, y, z, w; + } xyzw; +} vec4u_t; + +typedef union vec4f { + float vs[4]; + struct { + float x, y, z, w; + } xyzw; +} vec4f_t; + +typedef union vec4d { + double vs[4]; + struct { + double x, y, z, w; + } xyzw; +} vec4d_t; + +#define VEC4(T, x, y, z, w) ((T) { .vs = { (x), (y), (z), (w), }, }) + +#define VEC40(T) VEC4(T, 0, 0, 0, 0) +#define VEC41(T) VEC4(T, 1, 1, 1, 1) + +#define VEC4NEG(lhs) \ + VEC4(__typeof__ (lhs), -(lhs).xyzw.x, -(lhs).xyzw.y, -(lhs).xyzw.z, -(lhs).xyzw.w) + +#define VEC4ADD(T, lhs, rhs) \ + VEC4(T, (lhs).xyzw.x + (rhs).xyzw.x, (lhs).xyzw.y + (rhs).xyzw.y, (lhs).xyzw.z + (rhs).xyzw.z, (lhs).xyzw.w + (rhs).xyzw.w) +#define VEC4SUB(T, lhs, rhs) \ + VEC4(T, (lhs).xyzw.x - (rhs).xyzw.x, (lhs).xyzw.y - (rhs).xyzw.y, (lhs).xyzw.z - (rhs).xyzw.z, (lhs).xyzw.w - (rhs).xyzw.w) +#define VEC4MUL(T, lhs, rhs) \ + VEC4(T, (lhs).xyzw.x * (rhs).xyzw.x, (lhs).xyzw.y * (rhs).xyzw.y, (lhs).xyzw.z * (rhs).xyzw.z, (lhs).xyzw.w * (rhs).xyzw.w) +#define VEC4DIV(T, lhs, rhs) \ + VEC4(T, (lhs).xyzw.x / (rhs).xyzw.x, (lhs).xyzw.y / (rhs).xyzw.y, (lhs).xyzw.z / (rhs).xyzw.z, (lhs).xyzw.w / (rhs).xyzw.w) + +#define VEC4ADDS(T, lhs, scalar) \ + VEC4(T, (lhs).xyzw.x + scalar, (lhs).xyzw.y + scalar, (lhs).xyzw.z + scalar, (lhs).xyzw.w + scalar) +#define VEC4SUBS(T, lhs, scalar) \ + VEC4(T, (lhs).xyzw.x - scalar, (lhs).xyzw.y - scalar, (lhs).xyzw.z - scalar, (lhs).xyzw.w - scalar) +#define VEC4MULS(T, lhs, scalar) \ + VEC4(T, (lhs).xyzw.x * scalar, (lhs).xyzw.y * scalar, (lhs).xyzw.z * scalar, (lhs).xyzw.w * scalar) +#define VEC4DIVS(T, lhs, scalar) \ + VEC4(T, (lhs).xyzw.x / scalar, (lhs).xyzw.y / scalar, (lhs).xyzw.z / scalar, (lhs).xyzw.w / scalar) + +#define VEC4LEN2(lhs) \ + (((lhs).xyzw.x * (lhs).xyzw.x) + ((lhs).xyzw.y * (lhs).xyzw.y) + ((lhs).xyzw.z * (lhs).xyzw.z) + ((lhs).xyzw.w * (lhs).xyzw.w)) + +#define VEC4LEN(lhs) sqrt(VEC4LEN2(lhs)) + +#define VEC4LERP(a, b, t) \ + VEC4ADD(__typeof__ (a), \ + VEC4MULS(__typeof__ (a), (a), (1 - (t))), \ + VEC4MULS(__typeof__ (b), (b), (t))) + +#define VEC4DOT(lhs, rhs) \ + (((lhs).xyzw.x * (rhs).xyzw.x) + ((lhs).xyzw.y * (rhs).xyzw.y) + ((lhs).xyzw.z * (rhs).xyzw.z)) + +#define VEC4UNIT(lhs) \ + VEC4(__typeof__ (lhs), \ + (lhs).xyzw.x / VEC4LEN(lhs), \ + (lhs).xyzw.y / VEC4LEN(lhs), \ + (lhs).xyzw.z / VEC4LEN(lhs), \ + (lhs).xyzw.w / VEC4LEN(lhs)) + +#define DEG2RAD(degrees) (((degrees) * M_PI) / 180) + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define CLAMP(max, min, v) MAX(MIN((max), (v)), (min)) + +// raytracing utilities +// ========================================================================== + +typedef vec3f_t point_t; + +struct ray { + point_t origin; + vec3f_t direction; +}; + +inline vec3f_t +ray_at(struct ray const *ray, float t) +{ + return VEC3ADD(vec3f_t, ray->origin, VEC3MULS(vec3f_t, ray->direction, t)); +} + +struct hit_record { + point_t point; + vec3f_t normal; + float t; + + int front_face; +}; + +inline void +hit_record_set_face_normal(struct hit_record *record, struct ray const *ray, + vec3f_t outward_normal) +{ + record->front_face = VEC3DOT(ray->direction, outward_normal) < 0; + record->normal = record->front_face ? outward_normal : VEC3NEG(outward_normal); +} + +struct interval { + float min, max; +}; + +#define EMPTY_INTERVAL ((struct interval) { +INFINITY, -INFINITY, }) +#define UNIVERSE_INTERVAL ((struct interval) { -INFINITY, +INFINITY, }) + +inline int +interval_contains(struct interval interval, float v) +{ + return interval.min <= v && v <= interval.max; +} + +inline int +interval_surrounds(struct interval interval, float v) +{ + return interval.min < v && v < interval.max; +} + +struct hittable { + int (*impl)(struct hittable *data, struct ray const *ray, + struct interval t, struct hit_record *record); + + struct list_node list_node; +}; + +struct sphere { + point_t centre; + float radius; + + struct hittable hittable; +}; + +inline int +sphere_hittable_impl(struct hittable *data, struct ray const *ray, + struct interval t, struct hit_record *record) +{ + struct sphere *self = TO_PARENT_PTR(data, struct sphere, hittable); + + vec3f_t oc = VEC3SUB(point_t, self->centre, ray->origin); + + float a = VEC3LEN2(ray->direction); + float h = VEC3DOT(ray->direction, oc); + float c = VEC3LEN2(oc) - (self->radius * self->radius); + float discriminant = (h * h) - (a * c); + + if (discriminant < 0) + return 0; + + float sqrtd = sqrt(discriminant); + + float root = (h - sqrtd) / a; + if (interval_surrounds(t, root)) + goto found_valid_root; + + root = (h + sqrtd) / a; + if (interval_surrounds(t, root)) + goto found_valid_root; + + return 0; + +found_valid_root: + record->t = root; + record->point = ray_at(ray, record->t); + + vec3f_t outward_normal = VEC3DIVS(vec3f_t, + VEC3SUB(vec3f_t, record->point, self->centre), + self->radius); + hit_record_set_face_normal(record, ray, outward_normal); + + return 1; +} + +typedef vec3f_t color_t; + +// bitmap utilities +// =========================================================================== + +typedef uint32_t pixel_t; + +enum pixel_format { + PIXEL_FORMAT_ARGB32, +}; + +inline pixel_t +pixel_from_argb32(uint8_t a, uint8_t r, uint8_t g, uint8_t b) +{ + return (((uint32_t) a) << 24) | \ + (((uint32_t) r) << 16) | \ + (((uint32_t) g) << 8) | \ + (((uint32_t) b)); +} + +inline pixel_t +pixel_from_color(color_t color, enum pixel_format format) +{ + assert(format == PIXEL_FORMAT_ARGB32); + + uint8_t a = 255; + uint8_t r = color.xyz.x * 255; + uint8_t g = color.xyz.y * 255; + uint8_t b = color.xyz.z * 255; + + switch (format) { + case PIXEL_FORMAT_ARGB32: return pixel_from_argb32(a, r, g, b); + } +} + +inline void +pixel_format_set_masks(enum pixel_format format, + uint32_t *r, uint32_t *g, uint32_t *b, uint32_t *a) +{ + /* NOTE: bitmap field masks are big endian! */ + switch (format) { + case PIXEL_FORMAT_ARGB32: + *r = 0x0000ff00; + *g = 0x00ff0000; + *b = 0xff000000; + *a = 0x000000ff; + break; + } +} + +/* see: https://en.wikipedia.org/wiki/BMP_file_format */ +inline void +bitmap_write(int fd, pixel_t *buf, size_t width, size_t height, enum pixel_format format) +{ + uint32_t pixels = width * height; + uint32_t pixel_bytes = pixels * sizeof(pixel_t); + + uint32_t dib_header_size = 108; + uint32_t bitmap_header_size = 14; + uint32_t header_size = bitmap_header_size + dib_header_size; + + uint32_t file_size = header_size + pixel_bytes; + uint32_t reserved = 0; + + int32_t xres = width, yres = -height; + uint16_t planes = 1, bits_per_pixel = sizeof(pixel_t) * 8; + uint32_t compression = 3; /* 3 = bitfields */ + uint32_t xdpi = 0, ydpi = 0; + uint32_t palette = 0, important = 0; + + uint32_t r_mask, g_mask, b_mask, a_mask; + pixel_format_set_masks(format, &r_mask, &g_mask, &b_mask, &a_mask); + + unsigned char colorspace[4] = "sRGB"; + unsigned char endpoints[36]; + memset(endpoints, 0, sizeof endpoints); + + uint32_t r_gamma = 0, g_gamma = 0, b_gamma = 0; + + /* bitmap header */ + write(fd, "BM", 2); + write(fd, &file_size, sizeof file_size); + write(fd, &reserved, sizeof reserved); + write(fd, &header_size, sizeof header_size); + + /* dib header: BITMAPV4HEADER */ + write(fd, &dib_header_size, sizeof dib_header_size); + write(fd, &xres, sizeof xres); + write(fd, &yres, sizeof yres); + write(fd, &planes, sizeof planes); + write(fd, &bits_per_pixel, sizeof bits_per_pixel); + write(fd, &compression, sizeof compression); + write(fd, &pixel_bytes, sizeof pixel_bytes); + write(fd, &xdpi, sizeof xdpi); + write(fd, &ydpi, sizeof ydpi); + write(fd, &palette, sizeof palette); + write(fd, &important, sizeof important); + write(fd, &r_mask, sizeof r_mask); + write(fd, &g_mask, sizeof g_mask); + write(fd, &b_mask, sizeof b_mask); + write(fd, &a_mask, sizeof a_mask); + write(fd, colorspace, sizeof colorspace); + write(fd, endpoints, sizeof endpoints); + write(fd, &r_gamma, sizeof r_gamma); + write(fd, &g_gamma, sizeof g_gamma); + write(fd, &b_gamma, sizeof b_gamma); + + /* pixel data */ + write(fd, buf, pixel_bytes); +} + +// rt definitions +// =========================================================================== + +struct world { + struct list_node objects; +}; + +inline void +world_init(struct world *world) +{ + world->objects = LIST_INIT(world->objects); +} + +inline void +world_push(struct world *world, struct hittable *hittable) +{ + list_push_tail(&world->objects, &hittable->list_node); +} + +void +render(pixel_t *image, size_t image_width, size_t image_height, struct world *world); + +#endif /* RT_H */ diff --git a/rt1/utils.c b/rt1/utils.c @@ -0,0 +1,11 @@ +#include "rt.h" + +extern inline pixel_t +pixel_argb32(uint8_t a, uint8_t r, uint8_t g, uint8_t b); + +extern inline void +pixel_format_set_masks(enum pixel_format format, + uint32_t *r, uint32_t *g, uint32_t *b, uint32_t *a); + +extern inline void +bitmap_write(int fd, pixel_t *buf, size_t width, size_t height, enum pixel_format format);