From 007bb10b7b2aaca88f8dd07d24b1f2e2995fb163 Mon Sep 17 00:00:00 2001 From: Jean-Michel Gorius Date: Sun, 13 Nov 2022 17:28:20 +0100 Subject: [PATCH] Add diffuse lights --- hittable.c | 103 +++++++++++++++++++++++++++++++++++++++-------------- hittable.h | 10 ++++++ main.c | 81 +++++++++++++++++++++++++++++++---------- material.c | 32 +++++++++++++++++ material.h | 9 +++++ 5 files changed, 190 insertions(+), 45 deletions(-) diff --git a/hittable.c b/hittable.c index ceb0d90..ff6d65a 100644 --- a/hittable.c +++ b/hittable.c @@ -1,7 +1,9 @@ #include "hittable.h" #include "aabb.h" #include "arena.h" +#include "material.h" #include "point3.h" +#include "ray.h" #include "utils.h" #include "vec3.h" @@ -32,12 +34,28 @@ Hittable *hittable_create_sphere(Point3 center, double radius, return result; } -Point3 moving_sphere_center(const MovingSphere *sphere, double t) { +static Point3 moving_sphere_center(const MovingSphere *sphere, double t) { Vec3 dir = point3_sub(sphere->center_end, sphere->center_start); double c = (t - sphere->start) / (sphere->end - sphere->start); return point3_add(sphere->center_start, vec3_mul(c, dir)); } +Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end, + double start, double end, double radius, + const Material *material, + Arena *arena) { + assert(start <= end); + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_MOVING_SPHERE; + result->moving_sphere.center_start = center_start; + result->moving_sphere.center_end = center_end; + result->moving_sphere.start = start; + result->moving_sphere.end = end; + result->moving_sphere.radius = radius; + result->moving_sphere.material = material; + return result; +} + typedef int BoxCompareFunc(const void *lhs, const void *rhs); #define BOX_COMPARATOR(axis) \ @@ -101,6 +119,20 @@ Hittable *hittable_create_bvh_node(const Hittable **objects, size_t start, return result; } +Hittable *hittable_create_xy_rectangle(double x0, double x1, double y0, + double y1, double k, + const Material *material, Arena *arena) { + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_XY_RECTANGLE; + result->xy_rectangle.x0 = x0; + result->xy_rectangle.x1 = x1; + result->xy_rectangle.y0 = y0; + result->xy_rectangle.y1 = y1; + result->xy_rectangle.k = k; + result->xy_rectangle.material = material; + return result; +} + static bool hittable_list_hit(const HittableList *list, Ray r, double t_min, double t_max, HitRecord *record) { bool hit_anything = false; @@ -194,6 +226,31 @@ static bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max, return hit_left || hit_right; } +static bool xy_rectangle_hit(const XYRectangle *rectangle, Ray r, double t_min, + double t_max, HitRecord *record) { + double t = (rectangle->k - r.origin.z) / r.direction.z; + if (t < t_min || t > t_max) + return false; + + double x = r.origin.x + t * r.direction.x; + double y = r.origin.y + t * r.direction.y; + + if (x < rectangle->x0 || x > rectangle->x1 || y < rectangle->y0 || + y > rectangle->y1) + return false; + + record->u = (x - rectangle->x0) / (rectangle->x1 - rectangle->x0); + record->v = (y - rectangle->y0) / (rectangle->y1 - rectangle->y0); + record->t = t; + + Vec3 outward_normal = {0.0, 0.0, 1.0}; + hit_record_set_face_normal(record, r, outward_normal); + record->material = rectangle->material; + record->p = ray_at(r, t); + + return true; +} + bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, HitRecord *record) { switch (hittable->type) { @@ -205,6 +262,8 @@ bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, return moving_sphere_hit(&hittable->moving_sphere, r, t_min, t_max, record); case HITTABLE_BVH_NODE: return bvh_node_hit(&hittable->bvh_node, r, t_min, t_max, record); + case HITTABLE_XY_RECTANGLE: + return xy_rectangle_hit(&hittable->xy_rectangle, r, t_min, t_max, record); } return false; } @@ -230,9 +289,7 @@ static bool hittable_list_bounding_box(const HittableList *list, return true; } -static bool sphere_bounding_box(const Sphere *sphere, double time_start, - double time_end, AABB *bounding_box) { - (void)time_start, (void)time_end; +static bool sphere_bounding_box(const Sphere *sphere, AABB *bounding_box) { *bounding_box = (AABB){ .min = point3_add(sphere->center, (Vec3){-sphere->radius, -sphere->radius, -sphere->radius}), @@ -263,13 +320,21 @@ static bool moving_sphere_bounding_box(const MovingSphere *sphere, return true; } -static bool bvh_node_bounding_box(const BVHNode *node, double time_start, - double time_end, AABB *bounding_box) { - (void)time_start, (void)time_end; +static bool bvh_node_bounding_box(const BVHNode *node, AABB *bounding_box) { *bounding_box = node->box; return true; } +static bool xy_rectangle_bouding_box(const XYRectangle *rectangle, + AABB *bounding_box) { + /* Pad the bounding box to make sure it is not zero-width */ + *bounding_box = (AABB){ + .min = {rectangle->x0, rectangle->y0, rectangle->k - 0.0001}, + .max = {rectangle->x1, rectangle->y1, rectangle->k + 0.0001}, + }; + return true; +} + bool hittable_bounding_box(const Hittable *hittable, double time_start, double time_end, AABB *bounding_box) { switch (hittable->type) { @@ -277,14 +342,14 @@ bool hittable_bounding_box(const Hittable *hittable, double time_start, return hittable_list_bounding_box(&hittable->list, time_start, time_end, bounding_box); case HITTABLE_SPHERE: - return sphere_bounding_box(&hittable->sphere, time_start, time_end, - bounding_box); + return sphere_bounding_box(&hittable->sphere, bounding_box); case HITTABLE_MOVING_SPHERE: return moving_sphere_bounding_box(&hittable->moving_sphere, time_start, time_end, bounding_box); case HITTABLE_BVH_NODE: - return bvh_node_bounding_box(&hittable->bvh_node, time_start, time_end, - bounding_box); + return bvh_node_bounding_box(&hittable->bvh_node, bounding_box); + case HITTABLE_XY_RECTANGLE: + return xy_rectangle_bouding_box(&hittable->xy_rectangle, bounding_box); } return false; } @@ -309,19 +374,3 @@ void hittable_list_add(HittableList *list, const Hittable *hittable, hittable_list_grow(list, list->capacity == 0 ? 16 : list->capacity, arena); list->objects[list->size++] = hittable; } - -Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end, - double start, double end, double radius, - const Material *material, - Arena *arena) { - assert(start <= end); - Hittable *result = arena_alloc(arena, sizeof(Hittable)); - result->type = HITTABLE_MOVING_SPHERE; - result->moving_sphere.center_start = center_start; - result->moving_sphere.center_end = center_end; - result->moving_sphere.start = start; - result->moving_sphere.end = end; - result->moving_sphere.radius = radius; - result->moving_sphere.material = material; - return result; -} diff --git a/hittable.h b/hittable.h index c26c0b8..0481815 100644 --- a/hittable.h +++ b/hittable.h @@ -27,6 +27,7 @@ typedef enum HittableType { HITTABLE_SPHERE, HITTABLE_MOVING_SPHERE, HITTABLE_BVH_NODE, + HITTABLE_XY_RECTANGLE, } HittableType; typedef struct Hittable Hittable; @@ -56,6 +57,11 @@ typedef struct BVHNode { AABB box; } BVHNode; +typedef struct XYRectangle { + const Material *material; + double x0, x1, y0, y1, k; +} XYRectangle; + struct Hittable { HittableType type; union { @@ -63,6 +69,7 @@ struct Hittable { Sphere sphere; MovingSphere moving_sphere; BVHNode bvh_node; + XYRectangle xy_rectangle; }; }; @@ -75,6 +82,9 @@ Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end, Hittable *hittable_create_bvh_node(const Hittable **objects, size_t start, size_t end, double time_start, double time_end, Arena *arena); +Hittable *hittable_create_xy_rectangle(double x0, double x1, double y0, + double y1, double k, + const Material *material, Arena *arena); bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, HitRecord *record); diff --git a/main.c b/main.c index 5a502a8..13f9326 100644 --- a/main.c +++ b/main.c @@ -20,27 +20,32 @@ #define SCENE_TWO_SPHERES 1 #define SCENE_TWO_PERLIN_SPHERES 2 #define SCENE_EARTH 3 +#define SCENE_SIMPLE_LIGHT 4 -#define SCENE_SELECT SCENE_EARTH +#ifndef SCENE_SELECT +#define SCENE_SELECT SCENE_SIMPLE_LIGHT +#endif -static Color ray_color(Ray r, const Hittable *world, int depth) { +static Color ray_color(Ray r, Color background_color, const Hittable *world, + int depth) { if (depth <= 0) return (Color){0, 0, 0}; HitRecord record; - if (hittable_hit(world, r, 0.001, DBL_MAX, &record)) { - Ray scattered; - Color attenuation; - if (material_scatter(record.material, r, &record, &attenuation, &scattered)) - return color_mul(attenuation, ray_color(scattered, world, depth - 1)); - return (Color){0, 0, 0}; - } + if (!hittable_hit(world, r, 0.001, DBL_MAX, &record)) + return background_color; - Vec3 unit_direction = vec3_normalize(r.direction); - double t = 0.5 * (unit_direction.y + 1.0); - Color gradient1 = {1.0, 1.0, 1.0}; - Color gradient2 = {0.5, 0.7, 1.0}; - return color_lerp(gradient1, gradient2, t); + Ray scattered; + Color attenuation; + Color emitted = + material_emitted(record.material, record.u, record.v, record.p); + + if (!material_scatter(record.material, r, &record, &attenuation, &scattered)) + return emitted; + + return color_add(emitted, + color_mul(attenuation, ray_color(scattered, background_color, + world, depth - 1))); } static const Hittable *generate_random_scene(Arena *arena) { @@ -152,6 +157,34 @@ static Hittable *earth(Arena *arena) { return globe; } +static Hittable *simple_light(Arena *arena) { + Hittable *world = hittable_create_hittable_list(arena); + + Texture *perlin_texture = + texture_create_perlin_noise(4.0, PERLIN_DEFAULT_POINT_COUNT, arena); + hittable_list_add( + &world->list, + hittable_create_sphere((Point3){0.0, -1000.0, 0.0}, 1000.0, + material_create_lambertian(perlin_texture, arena), + arena), + arena); + hittable_list_add( + &world->list, + hittable_create_sphere((Point3){0.0, 2.0, 0.0}, 2.0, + material_create_lambertian(perlin_texture, arena), + arena), + arena); + + Material *diffuse_light = + material_create_diffuse_light_color((Color){4.0, 4.0, 4.0}, arena); + hittable_list_add(&world->list, + hittable_create_xy_rectangle(3.0, 5.0, 1.0, 3.0, -2.0, + diffuse_light, arena), + arena); + + return world; +} + int main(void) { srand(42); @@ -169,40 +202,51 @@ int main(void) { const double aspect_ratio = 16.0 / 9.0; const int image_width = 400; const int image_height = (int)(image_width / aspect_ratio); - const int samples_per_pixel = 100; + const int samples_per_pixel = 400; const int max_depth = 50; - /* World */ - Point3 look_from = {0.0, 0.0, 1.0}; Point3 look_at = {0.0, 0.0, 0.0}; double vfov = 40.0; double aperture = 0.0; double dist_to_focus = 10.0; + Color background_color = {0.0, 0.0, 0.0}; + + /* World */ const Hittable *world = 0; #if SCENE_SELECT == SCENE_RANDOM world = generate_random_scene(&arena); + background_color = (Color){0.7, 0.8, 1.0}; look_from = (Point3){13.0, 2.0, 3.0}; look_at = (Point3){0.0, 0.0, 0.0}; vfov = 20.0; aperture = 0.1; #elif SCENE_SELECT == SCENE_TWO_SPHERES world = two_spheres(&arena); + background_color = (Color){0.7, 0.8, 1.0}; look_from = (Point3){13.0, 2.0, 3.0}; look_at = (Point3){0.0, 0.0, 0.0}; vfov = 20.0; #elif SCENE_SELECT == SCENE_TWO_PERLIN_SPHERES world = two_perlin_spheres(&arena); + background_color = (Color){0.7, 0.8, 1.0}; look_from = (Point3){13.0, 2.0, 3.0}; look_at = (Point3){0.0, 0.0, 0.0}; vfov = 20.0; #elif SCENE_SELECT == SCENE_EARTH world = earth(&arena); + background_color = (Color){0.7, 0.8, 1.0}; look_from = (Point3){13.0, 2.0, 3.0}; look_at = (Point3){0.0, 0.0, 0.0}; vfov = 20.0; +#elif SCENE_SELECT == SCENE_SIMPLE_LIGHT + world = simple_light(&arena); + background_color = (Color){0.0, 0.0, 0.0}; + look_from = (Point3){26.0, 3.0, 6.0}; + look_at = (Point3){0.0, 2.0, 0.0}; + vfov = 20.0; #endif Vec3 up = {0.0, 1.0, 0.0}; @@ -221,7 +265,8 @@ int main(void) { double u = (i + random_double()) / (image_width - 1); double v = (j + random_double()) / (image_height - 1); Ray r = camera_get_ray(&camera, u, v); - pixel_color = color_add(pixel_color, ray_color(r, world, max_depth)); + pixel_color = color_add( + pixel_color, ray_color(r, background_color, world, max_depth)); } color_write(stdout, pixel_color, samples_per_pixel); } diff --git a/material.c b/material.c index 92c0ac8..defc062 100644 --- a/material.c +++ b/material.c @@ -1,6 +1,7 @@ #include "material.h" #include "arena.h" #include "hittable.h" +#include "texture.h" #include "utils.h" #include "vec3.h" @@ -81,10 +82,27 @@ bool material_scatter(const Material *material, Ray r, case MATERIAL_DIELECTRIC: return dielectric_scatter(&material->dielectric, r, record, attenuation, scattered); + case MATERIAL_DIFFUSE_LIGHT: + return false; } return false; } +static Color diffuse_light_emitted(const DiffuseLight *diffuse_light, double u, + double v, Point3 p) { + return texture_value(diffuse_light->emit, u, v, p); +} + +Color material_emitted(const Material *material, double u, double v, Point3 p) { + switch (material->type) { + case MATERIAL_DIFFUSE_LIGHT: + return diffuse_light_emitted(&material->diffuse_light, u, v, p); + default: + break; + } + return (Color){0.0, 0.0, 0.0}; +} + Material *material_create_lambertian(const Texture *albedo, Arena *arena) { Material *result = arena_alloc(arena, sizeof(Material)); result->type = MATERIAL_LAMBERTIAN; @@ -118,3 +136,17 @@ Material *material_create_dielectric(double eta, Arena *arena) { result->dielectric.eta = eta; return result; } + +Material *material_create_diffuse_light(const Texture *emit, Arena *arena) { + Material *result = arena_alloc(arena, sizeof(Material)); + result->type = MATERIAL_DIFFUSE_LIGHT; + result->diffuse_light.emit = emit; + return result; +} + +Material *material_create_diffuse_light_color(Color color, Arena *arena) { + Material *result = arena_alloc(arena, sizeof(Material)); + result->type = MATERIAL_DIFFUSE_LIGHT; + result->diffuse_light.emit = texture_create_solid_color(color, arena); + return result; +} diff --git a/material.h b/material.h index cedb630..1ca0119 100644 --- a/material.h +++ b/material.h @@ -13,6 +13,7 @@ typedef enum MaterialType { MATERIAL_LAMBERTIAN, MATERIAL_METAL, MATERIAL_DIELECTRIC, + MATERIAL_DIFFUSE_LIGHT, } MaterialType; typedef struct Lambertian { @@ -28,18 +29,24 @@ typedef struct Dielectric { double eta; } Dielectric; +typedef struct DiffuseLight { + const Texture *emit; +} DiffuseLight; + typedef struct Material { MaterialType type; union { Lambertian lambertian; Metal metal; Dielectric dielectric; + DiffuseLight diffuse_light; }; } Material; bool material_scatter(const Material *material, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); +Color material_emitted(const Material *material, double u, double v, Point3 p); Material *material_create_lambertian(const Texture *albedo, Arena *arena); Material *material_create_lambertian_color(Color albedo, Arena *arena); @@ -48,5 +55,7 @@ Material *material_create_metal(const Texture *albedo, double fuzziness, Material *material_create_metal_color(Color albedo, double fuzziness, Arena *arena); Material *material_create_dielectric(double eta, Arena *arena); +Material *material_create_diffuse_light(const Texture *emit, Arena *arena); +Material *material_create_diffuse_light_color(Color color, Arena *arena); #endif /* INCLUDED_MATERIAL_H */