diff --git a/bin/defocus.c b/bin/defocus.c index e96f4b4..7a1ff2d 100644 --- a/bin/defocus.c +++ b/bin/defocus.c @@ -1,6 +1,7 @@ /* Main command line executable. */ #include +#include #define STB_IMAGE_WRITE_IMPLEMENTATION #include @@ -49,17 +50,43 @@ int main(int argc, char **argv) { df_image_handle input_image = df_load_image("../test_image.png", &image_width, &image_height); float aspect = (float)image_width / (float)image_height; - df_plane plane = get_image_filling_plane(image_width, image_height, input_image, focal_length); + + + df_plane plane; + plane.base_x = 0.f; + plane.base_y = 0.f; + plane.base_z = -2; + plane.normal_x = 0.f; + plane.normal_y = 0.f; + plane.normal_z = 1.f; + plane.img_p0_x = -aspect; + plane.img_p0_y = -1.f; + plane.img_p0_z = plane.base_z; + plane.img_w = 2.f * aspect; + plane.img_h = 2.f; + plane.img_ax0_x = 1.f; + plane.img_ax0_y = 0.f; + plane.img_ax0_z = 0.f; + plane.img_ax1_x = 0.f; + plane.img_ax1_y = 1.f; + plane.img_ax1_z = 0.f; + plane.image = input_image; uint8_t *image = NULL; + + df_v3 look_from = {1, 1.5, 5}; + df_v3 look_at = {0, 0, -2}; + df_v3 up = {0, 1, 0}; - df_thin_lense_camera_data camera_data = df_create_thin_lense_camera_data(image_width, image_height, 42.f, focal_length); + float dist_to_focus = df_len_v3(df_sub_v3(look_at, look_from)); + + df_thin_lense_camera_data camera_data = df_create_thin_lense_camera_data(look_from, look_at, up, 45.f, aspect, 2.0, dist_to_focus); df_trace_rays((df_trace_rays_settings){ .image_width = image_width, .image_height = image_height, .samples_per_pixel = 64, - .camera = df_thin_lense_camera, + .camera = df_pinhole_camera, .camera_data = &camera_data, }, spheres, 0, diff --git a/include/defocus/defocus.h b/include/defocus/defocus.h index 5c1cb47..9a5c560 100644 --- a/include/defocus/defocus.h +++ b/include/defocus/defocus.h @@ -26,6 +26,9 @@ #define DF_PIF32 3.1415926f #define DF_PI_OVER_4F32 (DF_PIF32 / 4.f) #define DF_PI_OVER_2F32 (DF_PIF32 / 2.f) +#define DF_PI_OVER_180F32 (DF_PIF32 / 180.f) + +#define DF_DEGREES_TO_RADIANS(deg) ((deg) * DF_PI_OVER_180F32) #define DF_ARRAY_COUNT(A) (sizeof((A)) / sizeof((A)[0])) #define DF_UNUSED(x) ((void)sizeof((x))) @@ -58,6 +61,12 @@ DF_API df_image_handle df_load_image(const char *path, int *w, int *h); /* cameras */ +/* The camera data structs (df_pinhole_camera_data etc.) are defined here + * to enable you to store these on the stack. + * However - unless you know what you are doing (-: - you should treat these + * structs as opaque. + */ + typedef struct { df_v3 origin; @@ -70,25 +79,31 @@ typedef df_ray (*df_camera_fn)(float u, float v, unsigned int sample_idx, void * typedef struct { - df_v2 viewport_size; + df_v3 horizontal; + df_v3 vertical; df_v3 lower_left; + df_v3 origin; } df_pinhole_camera_data; -DF_API df_pinhole_camera_data df_create_pinhole_camera_data(int image_width, int image_height, float focal_length); +/* Creates data for a pinhole camera. + * The vertical fov (vfov) is given in degrees. + */ +DF_API df_pinhole_camera_data df_create_pinhole_camera_data(df_v3 look_from, df_v3 look_at, df_v3 up, + float vfov, float aspect); DF_API df_ray df_pinhole_camera(float u, float v, unsigned int sample_idx, void *camera_data); typedef struct { - df_v2 viewport_size; + df_v3 horizontal; + df_v3 vertical; df_v3 lower_left; + df_v3 origin; float lens_radius; } df_thin_lense_camera_data; -DF_API df_thin_lense_camera_data df_create_thin_lense_camera_data(int image_width, - int image_height, - float aperture, - float focal_distance); +DF_API df_thin_lense_camera_data df_create_thin_lense_camera_data(df_v3 look_from, df_v3 look_at, df_v3 up, + float vfov, float aspect, float aperture, float focal_distance); DF_API df_ray df_thin_lense_camera(float u, float v, unsigned int sample_idx, void *camera_data); @@ -100,6 +115,9 @@ typedef struct int image_height; int samples_per_pixel; + df_v3 look_from; + df_v3 look_at; + df_camera_fn camera; void *camera_data; } df_trace_rays_settings; diff --git a/include/defocus/df_math.h b/include/defocus/df_math.h new file mode 100644 index 0000000..8bd56e5 Binary files /dev/null and b/include/defocus/df_math.h differ diff --git a/lib/df_math.c b/lib/df_math.c new file mode 100644 index 0000000..d7961ec Binary files /dev/null and b/lib/df_math.c differ diff --git a/lib/raytracer.c b/lib/raytracer.c index 3e67ff3..24a22b9 100644 --- a/lib/raytracer.c +++ b/lib/raytracer.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -9,23 +10,6 @@ #include "pcg/pcg_basic.h" -/* ************* * - * * - * Vectors * - * * - * ************* */ - -static df_v3 normalize(df_v3 v) -{ - float l = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); - return (df_v3){v.x / l, v.y / l, v.z / l}; -} - -static float dot(df_v3 a, df_v3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } - -static float v2_len(df_v2 v) { return sqrtf(v.x * v.x + v.y * v.y); } - - /* ********************* * * * * Image functions * @@ -72,22 +56,32 @@ DF_API df_image_handle df_load_image(const char *path, int *w, int *h) * * * *************************** */ -DF_API df_pinhole_camera_data df_create_pinhole_camera_data(int image_width, int image_height, float focal_length) +DF_API df_pinhole_camera_data +df_create_pinhole_camera_data(df_v3 look_from, df_v3 look_at, df_v3 up, float vfov, float aspect) { - float aspect_ratio = (float)image_width / (float)image_height; - float viewport_height = 2.f; - float viewport_width = aspect_ratio * viewport_height; + float theta = DF_DEGREES_TO_RADIANS(vfov); + float h = tanf(theta * .5f); + float viewport_height = 2.f * h; + float viewport_width = aspect * viewport_height; - /* Simple perspective projection. - * The lens is placed at (0, 0, 0) - */ - float lower_left_x = -viewport_width / 2.f; - float lower_left_y = -viewport_height / 2.f; - float lower_left_z = -focal_length; + df_v3 view_vec = df_sub_v3(look_from, look_at); + df_v3 w = df_normalize_v3(view_vec); + df_v3 u = df_normalize_v3(df_cross(up, w)); + df_v3 v = df_cross(w, u); + + df_v3 horizontal = df_mul_v3(viewport_width, u); + df_v3 vertical = df_mul_v3(viewport_height, v); + df_v3 lower_left = { + .x = look_from.x - horizontal.x / 2.f - vertical.x / 2.f - w.x, + .y = look_from.y - horizontal.y / 2.f - vertical.y / 2.f - w.y, + .z = look_from.z - horizontal.z / 2.f - vertical.z / 2.f - w.z, + }; return (df_pinhole_camera_data){ - .viewport_size = {.x = viewport_width, .y = viewport_height}, - .lower_left = { .x = lower_left_x, .y = lower_left_y, .z = lower_left_z }, + .origin = look_from, + .horizontal = horizontal, + .vertical = vertical, + .lower_left = lower_left, }; } @@ -96,11 +90,14 @@ DF_API df_ray df_pinhole_camera(float u, float v, unsigned int sample_idx, void DF_UNUSED(sample_idx); df_pinhole_camera_data *data = camera_data; - df_v3 origin = {0.f, 0.f, 0.f}; + df_v3 origin = data->origin; + df_v3 horizontal = data->horizontal; + df_v3 vertical = data->vertical; + df_v3 target = { - data->lower_left.x + u * data->viewport_size.x, - data->lower_left.y + v * data->viewport_size.y, - data->lower_left.z + data->lower_left.x + u * horizontal.x + v * vertical.x - origin.x, + data->lower_left.y + u * horizontal.y + v * vertical.y - origin.y, + data->lower_left.z + u * horizontal.z + v * vertical.z - origin.z, }; return (df_ray){ @@ -116,40 +113,38 @@ static df_v2 random_point_on_disk(unsigned int i) while (1) { df_v2 p = {.x = (float)pcg32_boundedrand(1024) / 1023.f, .y = (float)pcg32_boundedrand(1024) / 1023.f}; - if (v2_len(p) <= 1.f) + if (df_len_v2(p) <= 1.f) return p; } } -DF_API df_thin_lense_camera_data df_create_thin_lense_camera_data(int image_width, - int image_height, - float aperture, - float focal_distance) +DF_API df_thin_lense_camera_data df_create_thin_lense_camera_data( + df_v3 look_from, df_v3 look_at, df_v3 up, float vfov, float aspect, float aperture, float focal_distance) { - float aspect_ratio = (float)image_width / (float)image_height; - float viewport_height = 2.f; - float viewport_width = aspect_ratio * viewport_height; + float theta = DF_DEGREES_TO_RADIANS(vfov); + float h = tanf(theta * .5f); + float viewport_height = 2.f * h; + float viewport_width = aspect * viewport_height; - /* Simple perspective projection. - * The lens is placed at (0, 0, 0) - */ - float lower_left_x = -viewport_width / 2.f; - float lower_left_y = -viewport_height / 2.f; - float lower_left_z = -focal_distance; + df_v3 view_vec = df_sub_v3(look_from, look_at); + df_v3 w = df_normalize_v3(view_vec); + df_v3 u = df_normalize_v3(df_cross(up, w)); + df_v3 v = df_cross(w, u); - float lens_radius = aperture / 2.f; + df_v3 horizontal = df_mul_v3(viewport_width, u); + df_v3 vertical = df_mul_v3(viewport_height, v); + df_v3 lower_left = { + .x = look_from.x - horizontal.x / 2.f - vertical.x / 2.f - focal_distance * w.x, + .y = look_from.y - horizontal.y / 2.f - vertical.y / 2.f - focal_distance * w.y, + .z = look_from.z - horizontal.z / 2.f - vertical.z / 2.f - focal_distance * w.z, + }; return (df_thin_lense_camera_data){ - .lens_radius = lens_radius, - .lower_left = { - .x = lower_left_x, - .y = lower_left_y, - .z = lower_left_z, - }, - .viewport_size = { - .x = viewport_width, - .y = viewport_height - } + .origin = look_from, + .horizontal = horizontal, + .vertical = vertical, + .lower_left = lower_left, + .lens_radius = aperture / 2.f, }; } @@ -162,12 +157,20 @@ DF_API df_ray df_thin_lense_camera(float u, float v, unsigned int sample_idx, vo lensp.y *= data->lens_radius; df_v3 offset = {u * lensp.x, v * lensp.y, .z = 0.f}; + df_v3 origin = df_add_v3(data->origin, offset); + df_v3 horizontal = data->horizontal; + df_v3 vertical = data->vertical; - df_v3 origin = offset; - df_v3 target = {data->lower_left.x + u * data->viewport_size.x + offset.x, - data->lower_left.y + v * data->viewport_size.y + offset.y, - data->lower_left.z + offset.z}; - return (df_ray){.origin = origin, .dir = target}; + df_v3 target = { + data->lower_left.x + u * horizontal.x + v * vertical.x - origin.x, + data->lower_left.y + u * horizontal.y + v * vertical.y - origin.y, + data->lower_left.z + u * horizontal.z + v * vertical.z - origin.z, + }; + + return (df_ray){ + .origin = origin, + .dir = target, + }; } @@ -305,17 +308,17 @@ DF_API int df_trace_rays(df_trace_rays_settings settings, return 0; void *camera_data = settings.camera_data; - uint8_t *pixels = malloc(image_width * image_height * 3); - if (!pixels) + float *fpixels = malloc(sizeof(float) * image_width * image_height * 3); + if (!fpixels) return 0; - memset(pixels, 0, image_width * image_height * 3); + memset(fpixels, 0, sizeof(float) * image_width * image_height * 3); df_ray_packet packet; packet.samples_per_pixel = samples_per_pixel; packet.ray_count = image_width * image_height * samples_per_pixel; void *packet_mem = malloc(packet.ray_count * (2 * sizeof(df_v3) + sizeof(df_v2i))); if (!packet_mem) { - free(pixels); + free(fpixels); return 0; } packet.ray_origins = (df_v3 *)packet_mem; @@ -329,7 +332,6 @@ DF_API int df_trace_rays(df_trace_rays_settings settings, unsigned int ray_idx = 0; for (int y = 0; y < image_height; ++y) { /* TODO(Kevin): SIMD */ - uint8_t *row = pixels + y * image_width * 3; for (int x = 0; x < image_width; ++x) { float u = (float)x / (float)(image_width - 1); float v = (float)y / (float)(image_height - 1); @@ -418,11 +420,12 @@ DF_API int df_trace_rays(df_trace_rays_settings settings, } } + float fsamples = (float)samples_per_pixel; /* Trace the rays */ for (unsigned int i = 0; i < packet.ray_count; ++i) { df_v2i pixelp = packet.ray_pixels[i]; - uint8_t *row = pixels + pixelp.y * image_width * 3; + float *row = fpixels + pixelp.y * image_width * 3; df_hit plane_hit = plane_test(packet.ray_origins[i], packet.ray_deltas[i], planes, plane_count); @@ -438,13 +441,30 @@ DF_API int df_trace_rays(df_trace_rays_settings settings, stbi_uc *pixel = _image_table.images[plane_hit.image].pixels + 4 * (pixely * _image_table.images[plane_hit.image].w + pixelx); - row[pixelp.x * 3 + 0] += pixel[0] / samples_per_pixel; - row[pixelp.x * 3 + 1] += pixel[1] / samples_per_pixel; - row[pixelp.x * 3 + 2] += pixel[2] / samples_per_pixel; + row[pixelp.x * 3 + 0] += (float)pixel[0]; + row[pixelp.x * 3 + 1] += (float)pixel[1]; + row[pixelp.x * 3 + 2] += (float)pixel[2]; } } } + /* Map down to 8bit rgb */ + uint8_t *pixels = malloc(image_width * image_height * 3); + if (!pixels) { + free(packet_mem); + free(fpixels); + return 0; + } + for (unsigned int i = 0; i < image_width * image_height; ++i) { + float *fp = &fpixels[i * 3]; + uint8_t *p = &pixels[i * 3]; + + p[0] = (uint8_t)(fp[0] / fsamples); + p[1] = (uint8_t)(fp[1] / fsamples); + p[2] = (uint8_t)(fp[2] / fsamples); + } + + free(fpixels); free(packet_mem); *result = pixels; return 1; diff --git a/meson.build b/meson.build index 3e909bf..2af57a0 100644 --- a/meson.build +++ b/meson.build @@ -13,7 +13,9 @@ endif lib = library('df', 'lib/raytracer.c', 'lib/utils.c', + 'lib/df_math.c', 'include/defocus/defocus.h', + 'include/defocus/df_math.h', '3p/stb_image.h', '3p/pcg/pcg_basic.c', '3p/pcg/pcg_basic.h',