diff --git a/arena.c b/arena.c index 4773eac..7c217e1 100644 --- a/arena.c +++ b/arena.c @@ -1,6 +1,7 @@ #include "arena.h" #include +#include #include #include @@ -46,6 +47,7 @@ void *arena_alloc_align(Arena *arena, size_t size, size_t alignment) { return ptr; } + fprintf(stderr, "Arena is out of memory"); abort(); } diff --git a/hittable.c b/hittable.c index a753618..724723a 100644 --- a/hittable.c +++ b/hittable.c @@ -191,6 +191,13 @@ bool hittable_list_bounding_box(const HittableList *list, double time_start, return true; } +static void get_sphere_uv(Point3 p, double *u, double *v) { + double theta = acos(-p.x); + double phi = atan2(-p.z, p.x) + M_PI; + *u = phi / (2 * M_PI); + *v = theta / M_PI; +} + bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, HitRecord *record) { Vec3 oc = point3_sub(r.origin, sphere->center); @@ -214,6 +221,8 @@ bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, Vec3 outward_normal = vec3_div(point3_sub(record->p, sphere->center), sphere->radius); hit_record_set_face_normal(record, r, outward_normal); + get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z}, + &record->u, &record->v); record->material = sphere->material; return true; } @@ -270,6 +279,8 @@ bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, vec3_div(point3_sub(record->p, moving_sphere_center(sphere, r.time)), sphere->radius); hit_record_set_face_normal(record, r, outward_normal); + get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z}, + &record->u, &record->v); record->material = sphere->material; return true; } diff --git a/hittable.h b/hittable.h index 6757bac..0eac955 100644 --- a/hittable.h +++ b/hittable.h @@ -15,6 +15,7 @@ typedef struct HitRecord { const Material *material; Point3 p; Vec3 normal; + double u, v; double t; bool front_face; } HitRecord; diff --git a/main.c b/main.c index 3309a53..3097f2e 100644 --- a/main.c +++ b/main.c @@ -11,9 +11,15 @@ #include "material.h" #include "point3.h" #include "ray.h" +#include "texture.h" #include "utils.h" #include "vec3.h" +#define SCENE_RANDOM 0 +#define SCENE_TWO_SPHERES 1 + +#define SCENE_SELECT SCENE_TWO_SPHERES + static Color ray_color(Ray r, const Hittable *world, int depth) { if (depth <= 0) return (Color){0, 0, 0}; @@ -37,10 +43,11 @@ static Color ray_color(Ray r, const Hittable *world, int depth) { static const Hittable *generate_random_scene(Arena *arena) { Hittable *world = hittable_create_hittable_list(arena); - const Material *ground_material = - material_create_lambertian((Color){0.5, 0.5, 0.5}, arena); - const Hittable *ground_sphere = hittable_create_sphere((Point3){0.0, -1000.0, 0.0}, - 1000.0, ground_material, arena); + const Texture *checker = texture_create_checker_solid_color( + (Color){0.2, 0.3, 0.1}, (Color){0.9, 0.9, 0.9}, 10.0, arena); + const Material *ground_material = material_create_lambertian(checker, arena); + const Hittable *ground_sphere = hittable_create_sphere( + (Point3){0.0, -1000.0, 0.0}, 1000.0, ground_material, arena); hittable_list_add(&world->list, ground_sphere, arena); const Material *glass = material_create_dielectric(1.5, arena); @@ -53,21 +60,21 @@ static const Hittable *generate_random_scene(Arena *arena) { if (vec3_length(point3_sub(center, (Point3){4.0, 0.2, 0.0})) > 0.9) { if (choose_material < 0.8) { - const Material *material = material_create_lambertian( + const Material *material = material_create_lambertian_color( color_mul(color_random(), color_random()), arena); - Point3 center2 = point3_add( - center, (Vec3){0, random_double_in_range(0.0, 0.5), 0}); - const Hittable *sphere = hittable_create_moving_sphere( - center, center2, 0.0, 1.0, 0.2, material, arena); + const Hittable *sphere = + hittable_create_sphere(center, 0.2, material, arena); hittable_list_add(&world->list, sphere, arena); } else if (choose_material < 0.95) { - const Material *material = - material_create_metal(color_random_in_range(0.5, 1), - random_double_in_range(0.5, 1.0), arena); - const Hittable *sphere = hittable_create_sphere(center, 0.2, material, arena); + const Material *material = material_create_metal_color( + color_random_in_range(0.5, 1), random_double_in_range(0.5, 1.0), + arena); + const Hittable *sphere = + hittable_create_sphere(center, 0.2, material, arena); hittable_list_add(&world->list, sphere, arena); } else { - const Hittable *sphere = hittable_create_sphere(center, 0.2, glass, arena); + const Hittable *sphere = + hittable_create_sphere(center, 0.2, glass, arena); hittable_list_add(&world->list, sphere, arena); } } @@ -75,9 +82,9 @@ static const Hittable *generate_random_scene(Arena *arena) { } const Material *lambertian = - material_create_lambertian((Color){0.4, 0.2, 0.1}, arena); + material_create_lambertian_color((Color){0.4, 0.2, 0.1}, arena); const Material *metal = - material_create_metal((Color){0.7, 0.6, 0.5}, 0.0, arena); + material_create_metal_color((Color){0.7, 0.6, 0.5}, 0.0, arena); const Hittable *sphere1 = hittable_create_sphere((Point3){0.0, 1.0, 0.0}, 1.0, glass, arena); @@ -89,11 +96,30 @@ static const Hittable *generate_random_scene(Arena *arena) { hittable_create_sphere((Point3){4.0, 1.0, 0.0}, 1.0, metal, arena); hittable_list_add(&world->list, sphere3, arena); - Hittable *bvh_root = hittable_create_bvh_node(world->list.objects, 0, world->list.size, - 0.0, 1.0, arena); + Hittable *bvh_root = hittable_create_bvh_node( + world->list.objects, 0, world->list.size, 0.0, 1.0, arena); return bvh_root; } +static Hittable *two_spheres(Arena *arena) { + Hittable *world = hittable_create_hittable_list(arena); + + Texture *checker = texture_create_checker_solid_color( + (Color){0.2, 0.3, 0.1}, (Color){0.9, 0.9, 0.9}, 10.0, arena); + hittable_list_add( + &world->list, + hittable_create_sphere((Point3){0.0, -10.0, 0.0}, 10.0, + material_create_lambertian(checker, arena), arena), + arena); + hittable_list_add( + &world->list, + hittable_create_sphere((Point3){0.0, 10.0, 0.0}, 10.0, + material_create_lambertian(checker, arena), arena), + arena); + + return world; +} + int main(void) { srand(42); @@ -116,18 +142,31 @@ int main(void) { /* World */ - const Hittable *world = generate_random_scene(&arena); - - /* Camera */ - Point3 look_from = {13.0, 2.0, 3.0}; Point3 look_at = {0.0, 0.0, 0.0}; - Vec3 up = {0.0, 1.0, 0.0}; + double vfov = 40.0; + double aperture = 0.0; double dist_to_focus = 10.0; - double aperture = 0.1; + + const Hittable *world = 0; + +#if SCENE_SELECT == SCENE_RANDOM + world = generate_random_scene(&arena); + 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); + look_from = (Point3){13.0, 2.0, 3.0}; + look_at = (Point3){0.0, 0.0, 0.0}; + vfov = 20.0; +#endif + + Vec3 up = {0.0, 1.0, 0.0}; Camera camera; - camera_init(&camera, look_from, look_at, up, 20, aspect_ratio, aperture, + camera_init(&camera, look_from, look_at, up, vfov, aspect_ratio, aperture, dist_to_focus, 0.0, 1.0); printf("P3\n%u %u\n255\n", image_width, image_height); @@ -161,5 +200,6 @@ int main(void) { #include "material.c" #include "point3.c" #include "ray.c" +#include "texture.c" #include "utils.c" #include "vec3.c" diff --git a/material.c b/material.c index b249d5f..da26171 100644 --- a/material.c +++ b/material.c @@ -23,13 +23,18 @@ bool material_scatter(const Material *material, Ray r, return false; } -Material *material_create_lambertian(Color albedo, Arena *arena) { +Material *material_create_lambertian(const Texture *albedo, Arena *arena) { Material *result = arena_alloc(arena, sizeof(Material)); result->type = MATERIAL_LAMBERTIAN; result->lambertian.albedo = albedo; return result; } +Material *material_create_lambertian_color(Color albedo, Arena *arena) { + return material_create_lambertian(texture_create_solid_color(albedo, arena), + arena); +} + bool lambertian_scatter(const Lambertian *lambertian, Ray r, const HitRecord *record, Color *attenuation, Ray *scattered) { @@ -41,11 +46,13 @@ bool lambertian_scatter(const Lambertian *lambertian, Ray r, scatter_direction = record->normal; *scattered = (Ray){record->p, scatter_direction, r.time}; - *attenuation = lambertian->albedo; + *attenuation = + texture_value(lambertian->albedo, record->u, record->v, record->p); return true; } -Material *material_create_metal(Color albedo, double fuzziness, Arena *arena) { +Material *material_create_metal(const Texture *albedo, double fuzziness, + Arena *arena) { Material *result = arena_alloc(arena, sizeof(Material)); result->type = MATERIAL_METAL; result->metal.albedo = albedo; @@ -53,6 +60,12 @@ Material *material_create_metal(Color albedo, double fuzziness, Arena *arena) { return result; } +Material *material_create_metal_color(Color albedo, double fuzziness, + Arena *arena) { + return material_create_metal(texture_create_solid_color(albedo, arena), + fuzziness, arena); +} + bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered) { Vec3 reflected = vec3_reflect(vec3_normalize(r.direction), record->normal); @@ -63,7 +76,7 @@ bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, vec3_mul(metal->fuzziness, vec3_random_in_unit_sphere())), r.time, }; - *attenuation = metal->albedo; + *attenuation = texture_value(metal->albedo, record->u, record->v, record->p); return vec3_dot(scattered->direction, record->normal) > 0; } diff --git a/material.h b/material.h index fdf2ecb..9b3d985 100644 --- a/material.h +++ b/material.h @@ -2,8 +2,8 @@ #define INCLUDED_MATERIAL_H #include "arena.h" -#include "color.h" #include "ray.h" +#include "texture.h" #include @@ -16,11 +16,11 @@ typedef enum MaterialType { } MaterialType; typedef struct Lambertian { - Color albedo; + const Texture *albedo; } Lambertian; typedef struct Metal { - Color albedo; + const Texture *albedo; double fuzziness; } Metal; @@ -41,12 +41,16 @@ bool material_scatter(const Material *material, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); -Material *material_create_lambertian(Color albedo, Arena *arena); +Material *material_create_lambertian(const Texture *albedo, Arena *arena); +Material *material_create_lambertian_color(Color albedo, Arena *arena); bool lambertian_scatter(const Lambertian *lambertian, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); -Material *material_create_metal(Color albedo, double fuzziness, Arena *arena); +Material *material_create_metal(const Texture *albedo, double fuzziness, + Arena *arena); +Material *material_create_metal_color(Color albedo, double fuzziness, + Arena *arena); bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, Color *attenuation, Ray *scattered); diff --git a/texture.c b/texture.c new file mode 100644 index 0000000..773346b --- /dev/null +++ b/texture.c @@ -0,0 +1,53 @@ +#include "texture.h" +#include "arena.h" + +#include + +Texture *texture_create_solid_color(Color value, Arena *arena) { + Texture *result = arena_alloc(arena, sizeof(Texture)); + result->type = TEXTURE_SOLID_COLOR; + result->solid_color.value = value; + return result; +} + +Texture *texture_create_checker(const Texture *odd, const Texture *even, + double size, Arena *arena) { + Texture *result = arena_alloc(arena, sizeof(Texture)); + result->type = TEXTURE_CHECKER; + result->checker.odd = odd; + result->checker.even = even; + result->checker.size = size; + return result; +} + +Texture *texture_create_checker_solid_color(Color odd, Color even, double size, + Arena *arena) { + Texture *odd_texture = texture_create_solid_color(odd, arena); + Texture *even_texture = texture_create_solid_color(even, arena); + return texture_create_checker(odd_texture, even_texture, size, arena); +} + +Color texture_value(const Texture *texture, double u, double v, Point3 p) { + switch (texture->type) { + case TEXTURE_SOLID_COLOR: + return solid_color_value(&texture->solid_color, u, v, p); + case TEXTURE_CHECKER: + return checker_value(&texture->checker, u, v, p); + } + return (Color){0.0, 0.0, 0.0}; +} + +Color solid_color_value(const SolidColor *solid_color, double u, double v, + Point3 p) { + (void)u, (void)v, (void)p; + return solid_color->value; +} + +Color checker_value(const CheckerTexture *checker, double u, double v, + Point3 p) { + double sines = sin(checker->size * p.x) * sin(checker->size * p.y) * + sin(checker->size * p.z); + if (sines < 0.0) + return texture_value(checker->odd, u, v, p); + return texture_value(checker->even, u, v, p); +} diff --git a/texture.h b/texture.h new file mode 100644 index 0000000..f45d358 --- /dev/null +++ b/texture.h @@ -0,0 +1,47 @@ +#ifndef INCLUDED_TEXTURE_H +#define INCLUDED_TEXTURE_H + +#include "arena.h" +#include "color.h" +#include "point3.h" + +typedef enum TextureType { + TEXTURE_SOLID_COLOR, + TEXTURE_CHECKER, +} TextureType; + +typedef struct Texture Texture; + +typedef struct SolidColor { + Color value; +} SolidColor; + +typedef struct CheckerTexture { + const Texture *odd; + const Texture *even; + double size; +} CheckerTexture; + +struct Texture { + TextureType type; + union { + SolidColor solid_color; + CheckerTexture checker; + }; +}; + +Texture *texture_create_solid_color(Color value, Arena *arena); +Texture *texture_create_checker(const Texture *odd, const Texture *even, + double size, Arena *arena); +Texture *texture_create_checker_solid_color(Color odd, Color even, double size, + Arena *arena); + +Color texture_value(const Texture *texture, double u, double v, Point3 p); + +Color solid_color_value(const SolidColor *solid_color, double u, double v, + Point3 p); + +Color checker_value(const CheckerTexture *checker, double u, double v, + Point3 p); + +#endif /* INCLUDED_TEXTURE_H */