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 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);
}
}

View File

@ -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;
}

View File

@ -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
};
}

View File

@ -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);
}
}
}

View File

@ -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));

View File

@ -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);

View File

@ -2,6 +2,7 @@
#include <stdio.h>
#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);

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 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)

View File

@ -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)

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();
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);