From e312755e3d2963b47bf23ed1c9c6b8db93c2ddf2 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Fri, 9 Aug 2024 15:24:02 -0500 Subject: [PATCH] chapter 8 --- camera.h | 2 +- float.h | 3 +- intersections.h | 6 ++- materials.h | 21 ++++----- raytracer.c | 4 +- test/test_camera.c | 2 +- test/test_intersections.c | 16 +++++++ test/test_materials.c | 28 +++++++++--- test/test_world.c | 91 +++++++++++++++++++++++++++++++++------ world.h | 36 +++++++++++++--- 10 files changed, 168 insertions(+), 41 deletions(-) diff --git a/camera.h b/camera.h index 7912923..91e49fc 100644 --- a/camera.h +++ b/camera.h @@ -68,7 +68,7 @@ void camera_render(struct camera * camera, struct world * world, struct canvas * for (int y = 0; y < camera->vsize; y++) { for (int x = 0; x < camera->hsize; x++) { struct ray ray = camera_ray_for_pixel(camera, x, y); - struct tuple color = color_at(world, ray); + struct tuple color = world_color_at(world, ray); canvas_write_pixel(canvas, x, y, color); } } diff --git a/float.h b/float.h index 1b20cac..1ae42e1 100644 --- a/float.h +++ b/float.h @@ -4,8 +4,9 @@ #include "math.h" +static const float epsilon = 0.00001; + inline static bool float_equal(float a, float b) { - const float epsilon = 0.00001; return fabsf(a - b) < epsilon; } diff --git a/intersections.h b/intersections.h index 66a73b5..d4185ab 100644 --- a/intersections.h +++ b/intersections.h @@ -118,6 +118,7 @@ struct computations { struct tuple eyev; struct tuple normalv; bool inside; + struct tuple over_point; }; inline static struct computations prepare_computations(struct intersection intersection, struct ray ray) @@ -132,12 +133,15 @@ inline static struct computations prepare_computations(struct intersection inter normalv = tuple_neg(normalv); } + struct tuple over_point = tuple_add(point, tuple_mul(normalv, epsilon * 100)); + return (struct computations){ intersection.t, intersection.object, point, eyev, normalv, - inside + inside, + over_point }; } diff --git a/materials.h b/materials.h index fe2ed55..49e3054 100644 --- a/materials.h +++ b/materials.h @@ -37,7 +37,8 @@ inline static struct tuple lighting(struct material material, struct light light, struct tuple point, struct tuple eyev, - struct tuple normalv) + struct tuple normalv, + bool in_shadow) { struct tuple effective_color = hadmard_product(material.color, light.intensity); @@ -45,24 +46,24 @@ inline static struct tuple lighting(struct material material, struct tuple ambient = tuple_mul(effective_color, material.ambient); + if (in_shadow) { + return ambient; + } + float light_dot_normal = tuple_dot(lightv, normalv); - struct tuple diffuse; - struct tuple specular; if (light_dot_normal < 0.0f) { - diffuse = color(0.0f, 0.0f, 0.0f); // black - specular = color(0.0f, 0.0f, 0.0f); // black + return ambient; } else { - diffuse = tuple_mul(effective_color, material.diffuse * light_dot_normal); + struct tuple diffuse = tuple_mul(effective_color, material.diffuse * light_dot_normal); struct tuple reflectv = tuple_reflect(tuple_neg(lightv), normalv); float reflect_dot_eye = tuple_dot(reflectv, eyev); if (reflect_dot_eye <= 0.0f) { - specular = color(0.0f, 0.0f, 0.0f); // black + return tuple_add(ambient, diffuse); } else { float factor = powf(reflect_dot_eye, material.shininess); - specular = tuple_mul(light.intensity, material.specular * factor); + struct tuple specular = tuple_mul(light.intensity, material.specular * factor); + return tuple_add(tuple_add(ambient, diffuse), specular); } } - - return tuple_add(tuple_add(ambient, diffuse), specular); } diff --git a/raytracer.c b/raytracer.c index b0108af..faaab52 100644 --- a/raytracer.c +++ b/raytracer.c @@ -102,9 +102,9 @@ int main() for (int i = 0; i < 360; i++) { - _camera.transform = view_transform(point(5.0f * cosf(pi / 360 * (float)i), + _camera.transform = view_transform(point(5.0f * cosf(pi / 360 * (float)i / 10), 1.5f, - -5.0f * sinf(pi / 360 * (float)i)), + -5.0f * sinf(pi / 360 * (float)i / 10)), point(0.0f, 1.0f, 0.0f), vector(0.0f, 1.0f, 0.0f)); diff --git a/test/test_camera.c b/test/test_camera.c index 20bb923..9820d9c 100644 --- a/test/test_camera.c +++ b/test/test_camera.c @@ -85,7 +85,7 @@ static bool camera_test_6(const char ** scenario) { *scenario = "Rendering a world with a camera"; - struct world w = default_world(); + struct world w = world_default(); struct camera c = camera(11.0f, 11.0f, pi / 2.0f); struct tuple from = point(0.0f, 0.0f, -5.0f); struct tuple to = point(0.0f, 0.0f, 0.0f); diff --git a/test/test_intersections.c b/test/test_intersections.c index 9ce984a..98025dd 100644 --- a/test/test_intersections.c +++ b/test/test_intersections.c @@ -2,6 +2,7 @@ #include #include "intersections.h" +#include "transformations.h" #include "runner.h" static bool intersections_test_0(const char ** scenario) @@ -181,6 +182,20 @@ static bool intersections_test_9(const char ** scenario) tuple_equal(comps.normalv, vector(0.0f, 0.0f, -1.0f)); } +static bool intersections_test_10(const char ** scenario) +{ + *scenario = "The hit should offset the point"; + + struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f)); + struct sphere shape = sphere(); + shape.transform = translation(0.0f, 0.0f, 1.0f); + struct intersection i = intersection(5.0f, &shape); + struct computations comps = prepare_computations(i, r); + return + comps.over_point.z < -epsilon / 2.0f && + comps.point.z > comps.over_point.z; +} + test_t intersections_tests[] = { intersections_test_0, intersections_test_1, @@ -192,6 +207,7 @@ test_t intersections_tests[] = { intersections_test_7, intersections_test_8, intersections_test_9, + intersections_test_10, }; RUNNER(intersections_tests); diff --git a/test/test_materials.c b/test/test_materials.c index 916baba..802c9bd 100644 --- a/test/test_materials.c +++ b/test/test_materials.c @@ -28,7 +28,7 @@ static bool materials_test_1(const char ** scenario) struct tuple eyev = vector(0.0f, 0.0f, -1.0f); struct tuple normalv = vector(0.0f, 0.0f, -1.0f); struct light light = point_light(point(0.0f, 0.0f, -10.0f), color(1.0f, 1.0f, 1.0f)); - struct tuple result = lighting(m, light, position, eyev, normalv); + struct tuple result = lighting(m, light, position, eyev, normalv, false); return tuple_equal(result, color(1.9f, 1.9f, 1.9f)); } @@ -43,7 +43,7 @@ static bool materials_test_2(const char ** scenario) struct tuple eyev = vector(0.0f, 0.7071067811865476, -0.7071067811865476); struct tuple normalv = vector(0.0f, 0.0f, -1.0f); struct light light = point_light(point(0.0f, 0.0f, -10.0f), color(1.0f, 1.0f, 1.0f)); - struct tuple result = lighting(m, light, position, eyev, normalv); + struct tuple result = lighting(m, light, position, eyev, normalv, false); return tuple_equal(result, color(1.0f, 1.0f, 1.0f)); } @@ -58,7 +58,7 @@ static bool materials_test_3(const char ** scenario) struct tuple eyev = vector(0.0f, 0.0f, -1.0f); struct tuple normalv = vector(0.0f, 0.0f, -1.0f); struct light light = point_light(point(0.0f, 10.0f, -10.0f), color(1.0f, 1.0f, 1.0f)); - struct tuple result = lighting(m, light, position, eyev, normalv); + struct tuple result = lighting(m, light, position, eyev, normalv, false); return tuple_equal(result, color(0.7364f, 0.7364f, 0.7364f)); } @@ -73,7 +73,7 @@ static bool materials_test_4(const char ** scenario) struct tuple eyev = vector(0.0f, -0.7071067811865476, -0.7071067811865476); struct tuple normalv = vector(0.0f, 0.0f, -1.0f); struct light light = point_light(point(0.0f, 10.0f, -10.0f), color(1.0f, 1.0f, 1.0f)); - struct tuple result = lighting(m, light, position, eyev, normalv); + struct tuple result = lighting(m, light, position, eyev, normalv, false); return tuple_equal(result, color(1.63639, 1.63639, 1.63639)); } @@ -88,7 +88,24 @@ static bool materials_test_5(const char ** scenario) struct tuple eyev = vector(0.0f, 0.0f, -1.0f); struct tuple normalv = vector(0.0f, 0.0f, -1.0f); struct light light = point_light(point(0.0f, 0.0f, 10.0f), color(1.0f, 1.0f, 1.0f)); - struct tuple result = lighting(m, light, position, eyev, normalv); + struct tuple result = lighting(m, light, position, eyev, normalv, false); + + return tuple_equal(result, color(0.1f, 0.1f, 0.1f)); +} + +static bool materials_test_6(const char ** scenario) +{ + *scenario = "Lighting with the surface in shadow"; + + struct material m = material(); + struct tuple position = point(0.0f, 0.0f, 0.0f); + + struct tuple eyev = vector(0.0f, 0.0f, -1.0f); + struct tuple normalv = vector(0.0f, 0.0f, -1.0f); + struct light light = point_light(point(0.0f, 0.0f, -10.0f), color(1.0f, 1.0f, 1.0f)); + bool in_shadow = true; + + struct tuple result = lighting(m, light, position, eyev, normalv, in_shadow); return tuple_equal(result, color(0.1f, 0.1f, 0.1f)); } @@ -100,6 +117,7 @@ test_t materials_tests[] = { materials_test_3, materials_test_4, materials_test_5, + materials_test_6, }; RUNNER(materials_tests) diff --git a/test/test_world.c b/test/test_world.c index 24bec0d..c099367 100644 --- a/test/test_world.c +++ b/test/test_world.c @@ -26,7 +26,7 @@ static bool world_test_1(const char ** scenario) s1.material.specular = 0.2f; struct sphere s2 = sphere(); s2.transform = scaling(0.5f, 0.5f, 0.5f); - struct world w = default_world(); + struct world w = world_default(); return tuple_equal(w.light.position, light.position) && tuple_equal(w.light.intensity, light.intensity) && @@ -41,11 +41,11 @@ static bool world_test_2(const char ** scenario) { *scenario = "Intersect a world with a ray"; - struct world w = default_world(); + struct world w = world_default(); struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f)); struct intersections xs; - intersect_world(&w, r, &xs); + world_intersect(&w, r, &xs); return xs.count == 4 && @@ -59,12 +59,12 @@ static bool world_test_3(const char ** scenario) { *scenario = "Shading an intersection"; - struct world w = default_world(); + struct world w = world_default(); struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f)); struct sphere * shape = &w.objects[0]; struct intersection i = intersection(4.0f, shape); struct computations comps = prepare_computations(i, r); - struct tuple c = shade_hit(&w, &comps); + struct tuple c = world_shade_hit(&w, &comps); return tuple_equal(c, color(0.38066, 0.47583, 0.2855)); } @@ -73,13 +73,13 @@ static bool world_test_4(const char ** scenario) { *scenario = "Shading an intersection from the inside"; - struct world w = default_world(); + struct world w = world_default(); w.light = point_light(point(0.0f, 0.25f, 0.0f), color(1.0f, 1.0f, 1.0f)); struct ray r = ray(point(0.0f, 0.0f, 0.0f), vector(0.0f, 0.0f, 1.0f)); struct sphere * shape = &w.objects[1]; struct intersection i = intersection(0.5f, shape); struct computations comps = prepare_computations(i, r); - struct tuple c = shade_hit(&w, &comps); + struct tuple c = world_shade_hit(&w, &comps); return tuple_equal(c, color(0.90498, 0.90498, 0.90498)); } @@ -88,9 +88,9 @@ static bool world_test_5(const char ** scenario) { *scenario = "The color when a ray misses"; - struct world w = default_world(); + struct world w = world_default(); struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 1.0f, 0.0f)); - struct tuple c = color_at(&w, r); + struct tuple c = world_color_at(&w, r); return tuple_equal(c, color(0.0f, 0.0f, 0.0f)); } @@ -99,9 +99,9 @@ static bool world_test_6(const char ** scenario) { *scenario = "The color when a ray hits"; - struct world w = default_world(); + struct world w = world_default(); struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f)); - struct tuple c = color_at(&w, r); + struct tuple c = world_color_at(&w, r); return tuple_equal(c, color(0.38066f, 0.47583f, 0.2855f)); } @@ -110,17 +110,77 @@ static bool world_test_7(const char ** scenario) { *scenario = "The color with an intersection behind the ray"; - struct world w = default_world(); + struct world w = world_default(); struct sphere * outer = &w.objects[0]; outer->material.ambient = 1.0f; struct sphere * inner = &w.objects[1]; inner->material.ambient = 1.0f; struct ray r = ray(point(0.0f, 0.0f, 0.75f), vector(0.0f, 0.0f, -1.0f)); - struct tuple c = color_at(&w, r); + struct tuple c = world_color_at(&w, r); return tuple_equal(c, inner->material.color); } +static bool world_test_8(const char ** scenario) +{ + *scenario = "There is no shadow when nothing is collinear with point and light"; + + struct world w = world_default(); + struct tuple p = point(0.0f, 10.0f, 0.0f); + + return world_is_shadowed(&w, p) == false; +} + +static bool world_test_9(const char ** scenario) +{ + *scenario = "The shadow when an object is between the point and the light"; + + struct world w = world_default(); + struct tuple p = point(10.0f, -10.0f, 10.0f); + + return world_is_shadowed(&w, p) == true; +} + +static bool world_test_10(const char ** scenario) +{ + *scenario = "There is no shadow when an object is behind the light"; + + struct world w = world_default(); + struct tuple p = point(-20.0f, 20.0f, -20.0f); + + return world_is_shadowed(&w, p) == false; +} + +static bool world_test_11(const char ** scenario) +{ + *scenario = "There is not shadow when an object is behind the point"; + + struct world w = world_default(); + struct tuple p = point(-2.0f, 2.0f, -2.0f); + + return world_is_shadowed(&w, p) == false; +} + +static bool world_test_12(const char ** scenario) +{ + *scenario = "shade_hit() is given an intersection in shadow"; + + struct world w = world(); + w.light = point_light(point(0.0f, 0.0f, -10.0f), color(1.0f, 1.0f, 1.0f)); + struct sphere s1 = sphere(); + w.objects[0] = s1; + struct sphere s2 = sphere(); + s2.transform = translation(0.0f, 0.0f, 10.0f); + w.objects[1] = s2; + w.object_count = 2; + struct ray r = ray(point(0.0f, 0.0f, 5.0f), vector(0.0f, 0.0f, 1.0f)); + struct intersection i = intersection(4.0f, &s2); + struct computations comps = prepare_computations(i, r); + struct tuple c = world_shade_hit(&w, &comps); + + return tuple_equal(c, color(0.1f, 0.1f, 0.1f)); +} + test_t world_tests[] = { world_test_0, world_test_1, @@ -130,6 +190,11 @@ test_t world_tests[] = { world_test_5, world_test_6, world_test_7, + world_test_8, + world_test_9, + world_test_10, + world_test_11, + world_test_12, }; RUNNER(world_tests) diff --git a/world.h b/world.h index 4f28cf3..700ffe9 100644 --- a/world.h +++ b/world.h @@ -23,7 +23,7 @@ inline static struct world world() }; } -inline static struct world default_world() +inline static struct world world_default() { struct sphere s1 = sphere(); s1.material.color = color(0.8f, 1.0f, 0.6f); @@ -39,7 +39,7 @@ inline static struct world default_world() }; } -inline static void intersect_world(struct world * world, struct ray ray, struct intersections * intersections) +inline static void world_intersect(struct world * world, struct ray ray, struct intersections * intersections) { intersections->count = 0; for (int i = 0; i < world->object_count; i++) { @@ -48,24 +48,46 @@ inline static void intersect_world(struct world * world, struct ray ray, struct intersections_sort(intersections); } -inline static struct tuple shade_hit(struct world * world, struct computations * computations) +inline static bool world_is_shadowed(struct world * world, struct tuple point) { + struct tuple v = tuple_sub(world->light.position, point); + + float distance = tuple_magnitude(v); + struct tuple direction = tuple_normalize(v); + + struct ray r = ray(point, direction); + struct intersections xs; + world_intersect(world, r, &xs); + + struct intersection * h = hit(&xs); + if (h != NULL && h->t < distance) { + return true; + } else { + return false; + } +} + +inline static struct tuple world_shade_hit(struct world * world, struct computations * computations) +{ + bool is_shadowed = world_is_shadowed(world, computations->over_point); + struct tuple color = lighting(computations->object->material, world->light, computations->point, computations->eyev, - computations->normalv); + computations->normalv, + is_shadowed); return color; } -inline static struct tuple color_at(struct world * world, struct ray ray) +inline static struct tuple world_color_at(struct world * world, struct ray ray) { struct intersections xs; - intersect_world(world, ray, &xs); + world_intersect(world, ray, &xs); struct intersection * i = hit(&xs); if (i != NULL) { struct computations computations = prepare_computations(*i, ray); - struct tuple color = shade_hit(world, &computations); + struct tuple color = world_shade_hit(world, &computations); return color; } else { return color(0.0f, 0.0f, 0.0f);