chapter 8

This commit is contained in:
Zack Buhman 2024-08-09 15:24:02 -05:00
parent 617adaf2a1
commit e312755e3d
10 changed files with 168 additions and 41 deletions

View File

@ -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 y = 0; y < camera->vsize; y++) {
for (int x = 0; x < camera->hsize; x++) { for (int x = 0; x < camera->hsize; x++) {
struct ray ray = camera_ray_for_pixel(camera, x, y); 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); canvas_write_pixel(canvas, x, y, color);
} }
} }

View File

@ -4,8 +4,9 @@
#include "math.h" #include "math.h"
static const float epsilon = 0.00001;
inline static bool float_equal(float a, float b) inline static bool float_equal(float a, float b)
{ {
const float epsilon = 0.00001;
return fabsf(a - b) < epsilon; return fabsf(a - b) < epsilon;
} }

View File

@ -118,6 +118,7 @@ struct computations {
struct tuple eyev; struct tuple eyev;
struct tuple normalv; struct tuple normalv;
bool inside; bool inside;
struct tuple over_point;
}; };
inline static struct computations prepare_computations(struct intersection intersection, struct ray ray) 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); normalv = tuple_neg(normalv);
} }
struct tuple over_point = tuple_add(point, tuple_mul(normalv, epsilon * 100));
return (struct computations){ return (struct computations){
intersection.t, intersection.t,
intersection.object, intersection.object,
point, point,
eyev, eyev,
normalv, normalv,
inside inside,
over_point
}; };
} }

View File

@ -37,7 +37,8 @@ inline static struct tuple lighting(struct material material,
struct light light, struct light light,
struct tuple point, struct tuple point,
struct tuple eyev, struct tuple eyev,
struct tuple normalv) struct tuple normalv,
bool in_shadow)
{ {
struct tuple effective_color = hadmard_product(material.color, light.intensity); 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); struct tuple ambient = tuple_mul(effective_color, material.ambient);
if (in_shadow) {
return ambient;
}
float light_dot_normal = tuple_dot(lightv, normalv); float light_dot_normal = tuple_dot(lightv, normalv);
struct tuple diffuse;
struct tuple specular;
if (light_dot_normal < 0.0f) { if (light_dot_normal < 0.0f) {
diffuse = color(0.0f, 0.0f, 0.0f); // black return ambient;
specular = color(0.0f, 0.0f, 0.0f); // black
} else { } 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); struct tuple reflectv = tuple_reflect(tuple_neg(lightv), normalv);
float reflect_dot_eye = tuple_dot(reflectv, eyev); float reflect_dot_eye = tuple_dot(reflectv, eyev);
if (reflect_dot_eye <= 0.0f) { if (reflect_dot_eye <= 0.0f) {
specular = color(0.0f, 0.0f, 0.0f); // black return tuple_add(ambient, diffuse);
} else { } else {
float factor = powf(reflect_dot_eye, material.shininess); 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);
}
}
} }

View File

@ -102,9 +102,9 @@ int main()
for (int i = 0; i < 360; i++) { 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, 1.5f,
-5.0f * sinf(pi / 360 * (float)i)), -5.0f * sinf(pi / 360 * (float)i / 10)),
point(0.0f, 1.0f, 0.0f), point(0.0f, 1.0f, 0.0f),
vector(0.0f, 1.0f, 0.0f)); vector(0.0f, 1.0f, 0.0f));

View File

@ -85,7 +85,7 @@ static bool camera_test_6(const char ** scenario)
{ {
*scenario = "Rendering a world with a camera"; *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 camera c = camera(11.0f, 11.0f, pi / 2.0f);
struct tuple from = point(0.0f, 0.0f, -5.0f); struct tuple from = point(0.0f, 0.0f, -5.0f);
struct tuple to = point(0.0f, 0.0f, 0.0f); struct tuple to = point(0.0f, 0.0f, 0.0f);

View File

@ -2,6 +2,7 @@
#include <stdio.h> #include <stdio.h>
#include "intersections.h" #include "intersections.h"
#include "transformations.h"
#include "runner.h" #include "runner.h"
static bool intersections_test_0(const char ** scenario) 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)); 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[] = { test_t intersections_tests[] = {
intersections_test_0, intersections_test_0,
intersections_test_1, intersections_test_1,
@ -192,6 +207,7 @@ test_t intersections_tests[] = {
intersections_test_7, intersections_test_7,
intersections_test_8, intersections_test_8,
intersections_test_9, intersections_test_9,
intersections_test_10,
}; };
RUNNER(intersections_tests); RUNNER(intersections_tests);

View File

@ -28,7 +28,7 @@ static bool materials_test_1(const char ** scenario)
struct tuple eyev = vector(0.0f, 0.0f, -1.0f); struct tuple eyev = vector(0.0f, 0.0f, -1.0f);
struct tuple normalv = 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 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)); 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 eyev = vector(0.0f, 0.7071067811865476, -0.7071067811865476);
struct tuple normalv = 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 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)); 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 eyev = vector(0.0f, 0.0f, -1.0f);
struct tuple normalv = 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 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)); 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 eyev = vector(0.0f, -0.7071067811865476, -0.7071067811865476);
struct tuple normalv = 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 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)); 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 eyev = vector(0.0f, 0.0f, -1.0f);
struct tuple normalv = 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 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)); return tuple_equal(result, color(0.1f, 0.1f, 0.1f));
} }
@ -100,6 +117,7 @@ test_t materials_tests[] = {
materials_test_3, materials_test_3,
materials_test_4, materials_test_4,
materials_test_5, materials_test_5,
materials_test_6,
}; };
RUNNER(materials_tests) RUNNER(materials_tests)

View File

@ -26,7 +26,7 @@ static bool world_test_1(const char ** scenario)
s1.material.specular = 0.2f; s1.material.specular = 0.2f;
struct sphere s2 = sphere(); struct sphere s2 = sphere();
s2.transform = scaling(0.5f, 0.5f, 0.5f); s2.transform = scaling(0.5f, 0.5f, 0.5f);
struct world w = default_world(); struct world w = world_default();
return return
tuple_equal(w.light.position, light.position) && tuple_equal(w.light.position, light.position) &&
tuple_equal(w.light.intensity, light.intensity) && 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"; *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 ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
struct intersections xs; struct intersections xs;
intersect_world(&w, r, &xs); world_intersect(&w, r, &xs);
return return
xs.count == 4 && xs.count == 4 &&
@ -59,12 +59,12 @@ static bool world_test_3(const char ** scenario)
{ {
*scenario = "Shading an intersection"; *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 ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
struct sphere * shape = &w.objects[0]; struct sphere * shape = &w.objects[0];
struct intersection i = intersection(4.0f, shape); struct intersection i = intersection(4.0f, shape);
struct computations comps = prepare_computations(i, r); 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)); 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"; *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)); 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 ray r = ray(point(0.0f, 0.0f, 0.0f), vector(0.0f, 0.0f, 1.0f));
struct sphere * shape = &w.objects[1]; struct sphere * shape = &w.objects[1];
struct intersection i = intersection(0.5f, shape); struct intersection i = intersection(0.5f, shape);
struct computations comps = prepare_computations(i, r); 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)); 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"; *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 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)); 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"; *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 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)); 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"; *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]; struct sphere * outer = &w.objects[0];
outer->material.ambient = 1.0f; outer->material.ambient = 1.0f;
struct sphere * inner = &w.objects[1]; struct sphere * inner = &w.objects[1];
inner->material.ambient = 1.0f; inner->material.ambient = 1.0f;
struct ray r = ray(point(0.0f, 0.0f, 0.75f), vector(0.0f, 0.0f, -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); 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[] = { test_t world_tests[] = {
world_test_0, world_test_0,
world_test_1, world_test_1,
@ -130,6 +190,11 @@ test_t world_tests[] = {
world_test_5, world_test_5,
world_test_6, world_test_6,
world_test_7, world_test_7,
world_test_8,
world_test_9,
world_test_10,
world_test_11,
world_test_12,
}; };
RUNNER(world_tests) RUNNER(world_tests)

36
world.h
View File

@ -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(); struct sphere s1 = sphere();
s1.material.color = color(0.8f, 1.0f, 0.6f); 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; intersections->count = 0;
for (int i = 0; i < world->object_count; i++) { 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); 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, struct tuple color = lighting(computations->object->material,
world->light, world->light,
computations->point, computations->point,
computations->eyev, computations->eyev,
computations->normalv); computations->normalv,
is_shadowed);
return color; 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; struct intersections xs;
intersect_world(world, ray, &xs); world_intersect(world, ray, &xs);
struct intersection * i = hit(&xs); struct intersection * i = hit(&xs);
if (i != NULL) { if (i != NULL) {
struct computations computations = prepare_computations(*i, ray); struct computations computations = prepare_computations(*i, ray);
struct tuple color = shade_hit(world, &computations); struct tuple color = world_shade_hit(world, &computations);
return color; return color;
} else { } else {
return color(0.0f, 0.0f, 0.0f); return color(0.0f, 0.0f, 0.0f);