diff --git a/hittable.c b/hittable.c index 49c0e78..8e2a22b 100644 --- a/hittable.c +++ b/hittable.c @@ -247,6 +247,24 @@ Hittable *hittable_create_y_rotation(Hittable *ptr, double angle, return result; } +Hittable *hittable_create_constant_medium(Hittable *boundary, double density, + const Texture *phase, Arena *arena) { + Hittable *result = arena_alloc(arena, sizeof(Hittable)); + result->type = HITTABLE_CONSTANT_MEDIUM; + result->constant_medium.boundary = boundary; + result->constant_medium.neg_inv_density = -1.0 / density; + result->constant_medium.phase_function = + material_create_isotropic(phase, arena); + return result; +} + +Hittable *hittable_create_constant_medium_color(Hittable *boundary, + double density, Color albedo, + Arena *arena) { + return hittable_create_constant_medium( + boundary, density, texture_create_solid_color(albedo, arena), arena); +} + /*===----------------------------------------------------------------------===*/ static bool hittable_list_hit(const HittableList *list, Ray r, double t_min, @@ -469,6 +487,43 @@ static bool y_rotation_hit(const YRotation *rotation, Ray r, double t_min, return true; } +static bool constant_medium_hit(const ConstantMedium *constant_medium, Ray r, + double t_min, double t_max, HitRecord *record) { + /* TODO: Handle non-convex boundaries */ + + HitRecord record1, record2; + if (!hittable_hit(constant_medium->boundary, r, -DBL_MAX, DBL_MAX, &record1)) + return false; + if (!hittable_hit(constant_medium->boundary, r, record1.t + 0.0001, DBL_MAX, + &record2)) + return false; + + if (record1.t < t_min) + record1.t = t_min; + if (record2.t > t_max) + record2.t = t_max; + + if (record1.t >= record2.t) + return false; + + if (record1.t < 0) + record1.t = 0; + + double ray_length = vec3_length(r.direction); + double distance_inside_boundary = (record2.t - record1.t) * ray_length; + double hit_distance = constant_medium->neg_inv_density * log(random_double()); + + if (hit_distance > distance_inside_boundary) + return false; + + /* Arbitrary values */ + record->normal = (Vec3){1.0, 0.0, 0.0}; + record->front_face = true; + + record->material = constant_medium->phase_function; + return true; +} + bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, HitRecord *record) { switch (hittable->type) { @@ -492,6 +547,9 @@ bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, return translation_hit(&hittable->translation, r, t_min, t_max, record); case HITTABLE_Y_ROTATION: return y_rotation_hit(&hittable->y_rotation, r, t_min, t_max, record); + case HITTABLE_CONSTANT_MEDIUM: + return constant_medium_hit(&hittable->constant_medium, r, t_min, t_max, + record); } return false; } @@ -608,6 +666,13 @@ static bool y_rotation_bounding_box(const YRotation *rotation, return true; } +static bool constant_medium_bounding_box(const ConstantMedium *constant_medium, + double time_start, double time_end, + AABB *bounding_box) { + return hittable_bounding_box(constant_medium->boundary, time_start, time_end, + bounding_box); +} + bool hittable_bounding_box(const Hittable *hittable, double time_start, double time_end, AABB *bounding_box) { switch (hittable->type) { @@ -634,6 +699,9 @@ bool hittable_bounding_box(const Hittable *hittable, double time_start, time_end, bounding_box); case HITTABLE_Y_ROTATION: return y_rotation_bounding_box(&hittable->y_rotation, bounding_box); + case HITTABLE_CONSTANT_MEDIUM: + return constant_medium_bounding_box(&hittable->constant_medium, time_start, + time_end, bounding_box); } return false; } diff --git a/hittable.h b/hittable.h index 796545c..c1ecb35 100644 --- a/hittable.h +++ b/hittable.h @@ -33,6 +33,7 @@ typedef enum HittableType { HITTABLE_BOX, HITTABLE_TRANSLATION, HITTABLE_Y_ROTATION, + HITTABLE_CONSTANT_MEDIUM, } HittableType; typedef struct Hittable Hittable; @@ -96,6 +97,12 @@ typedef struct YRotation { AABB bounding_box; } YRotation; +typedef struct ConstantMedium { + const Hittable *boundary; + const Material *phase_function; + double neg_inv_density; +} ConstantMedium; + struct Hittable { HittableType type; union { @@ -109,6 +116,7 @@ struct Hittable { Box box; Translation translation; YRotation y_rotation; + ConstantMedium constant_medium; }; }; @@ -134,6 +142,11 @@ Hittable *hittable_create_box(Point3 p0, Point3 p1, const Material *material, Arena *arena); Hittable *hittable_create_translation(Hittable *ptr, Vec3 offset, Arena *arena); Hittable *hittable_create_y_rotation(Hittable *ptr, double angle, Arena *arena); +Hittable *hittable_create_constant_medium(Hittable *boundary, double density, + const Texture *phase, Arena *arena); +Hittable *hittable_create_constant_medium_color(Hittable *boundary, + double density, Color albedo, + 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 9438e2a..054d48b 100644 --- a/main.c +++ b/main.c @@ -23,9 +23,10 @@ #define SCENE_EARTH 3 #define SCENE_SIMPLE_LIGHT 4 #define SCENE_CORNELL_BOX 5 +#define SCENE_CORNELL_BOX_SMOKE 6 #ifndef SCENE_SELECT -#define SCENE_SELECT SCENE_CORNELL_BOX +#define SCENE_SELECT SCENE_CORNELL_BOX_SMOKE #endif static Color ray_color(Ray r, Color background_color, const Hittable *world, @@ -231,9 +232,60 @@ static Hittable *cornell_box(Arena *arena) { box2 = hittable_create_translation(box2, (Vec3){130, 0, 65}, arena); hittable_list_add(&world->list, box2, arena); - Hittable *bvh_root = hittable_create_bvh_node( - world->list.objects, 0, world->list.size, 0.0, 1.0, arena); - return bvh_root; + return world; +} + +static Hittable *cornell_box_smoke(Arena *arena) { + Hittable *world = hittable_create_hittable_list(arena); + + Material *red = + material_create_lambertian_color((Color){0.65, 0.05, 0.05}, arena); + Material *white = + material_create_lambertian_color((Color){0.73, 0.73, 0.73}, arena); + Material *green = + material_create_lambertian_color((Color){0.12, 0.45, 0.15}, arena); + Material *light = + material_create_diffuse_light_color((Color){7.0, 7.0, 7.0}, arena); + + hittable_list_add( + &world->list, + hittable_create_yz_rectangle(0, 555, 0, 555, 555, green, arena), arena); + hittable_list_add(&world->list, + hittable_create_yz_rectangle(0, 555, 0, 555, 0, red, arena), + arena); + hittable_list_add( + &world->list, + hittable_create_xz_rectangle(113, 443, 127, 432, 554, light, arena), + arena); + hittable_list_add( + &world->list, + hittable_create_xz_rectangle(0, 555, 0, 555, 0, white, arena), arena); + hittable_list_add( + &world->list, + hittable_create_xz_rectangle(0, 555, 0, 555, 555, white, arena), arena); + hittable_list_add( + &world->list, + hittable_create_xy_rectangle(0, 555, 0, 555, 555, white, arena), arena); + + Hittable *box1 = hittable_create_box((Point3){0, 0, 0}, + (Point3){165, 330, 165}, white, arena); + box1 = hittable_create_y_rotation(box1, 15, arena); + box1 = hittable_create_translation(box1, (Vec3){265, 0, 295}, arena); + hittable_list_add(&world->list, + hittable_create_constant_medium_color( + box1, 0.01, (Color){0.0, 0.0, 0.0}, arena), + arena); + + Hittable *box2 = hittable_create_box((Point3){0, 0, 0}, + (Point3){165, 165, 165}, white, arena); + box2 = hittable_create_y_rotation(box2, -18, arena); + box2 = hittable_create_translation(box2, (Vec3){130, 0, 65}, arena); + hittable_list_add(&world->list, + hittable_create_constant_medium_color( + box2, 0.01, (Color){1.0, 1.0, 1.0}, arena), + arena); + + return world; } int main(int argc, char *argv[]) { @@ -318,6 +370,14 @@ int main(int argc, char *argv[]) { look_from = (Point3){278.0, 278.0, -800.0}; look_at = (Point3){278.0, 278.0, 0.0}; vfov = 40.0; +#elif SCENE_SELECT == SCENE_CORNELL_BOX_SMOKE + world = cornell_box_smoke(&arena); + aspect_ratio = 1.0; + image_width = 600; + samples_per_pixel = 200; + look_from = (Point3){278.0, 278.0, -800.0}; + look_at = (Point3){278.0, 278.0, 0.0}; + vfov = 40.0; #else #error Unknown scene selected #endif diff --git a/material.c b/material.c index defc062..4d012d2 100644 --- a/material.c +++ b/material.c @@ -70,6 +70,15 @@ static bool dielectric_scatter(const Dielectric *dielectric, Ray r, return true; } +static bool isotropic_scatter(const Isotropic *isotropic, Ray r, + const struct HitRecord *record, + Color *attenuation, Ray *scattered) { + *scattered = (Ray){record->p, vec3_random_in_unit_sphere(), r.time}; + *attenuation = + texture_value(isotropic->albedo, record->u, record->v, record->p); + return true; +} + bool material_scatter(const Material *material, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered) { @@ -84,6 +93,9 @@ bool material_scatter(const Material *material, Ray r, scattered); case MATERIAL_DIFFUSE_LIGHT: return false; + case MATERIAL_ISOTROPIC: + return isotropic_scatter(&material->isotropic, r, record, attenuation, + scattered); } return false; } @@ -145,8 +157,13 @@ Material *material_create_diffuse_light(const Texture *emit, Arena *arena) { } Material *material_create_diffuse_light_color(Color color, Arena *arena) { + return material_create_diffuse_light(texture_create_solid_color(color, arena), + arena); +} + +Material *material_create_isotropic(const Texture *albedo, Arena *arena) { Material *result = arena_alloc(arena, sizeof(Material)); - result->type = MATERIAL_DIFFUSE_LIGHT; - result->diffuse_light.emit = texture_create_solid_color(color, arena); + result->type = MATERIAL_ISOTROPIC; + result->isotropic.albedo = albedo; return result; } diff --git a/material.h b/material.h index 1ca0119..20dcb2d 100644 --- a/material.h +++ b/material.h @@ -14,6 +14,7 @@ typedef enum MaterialType { MATERIAL_METAL, MATERIAL_DIELECTRIC, MATERIAL_DIFFUSE_LIGHT, + MATERIAL_ISOTROPIC, } MaterialType; typedef struct Lambertian { @@ -33,6 +34,10 @@ typedef struct DiffuseLight { const Texture *emit; } DiffuseLight; +typedef struct Isotropic { + const Texture *albedo; +} Isotropic; + typedef struct Material { MaterialType type; union { @@ -40,6 +45,7 @@ typedef struct Material { Metal metal; Dielectric dielectric; DiffuseLight diffuse_light; + Isotropic isotropic; }; } Material; @@ -57,5 +63,6 @@ Material *material_create_metal_color(Color albedo, double fuzziness, 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); +Material *material_create_isotropic(const Texture *albedo, Arena *arena); #endif /* INCLUDED_MATERIAL_H */