chapter 6

This commit is contained in:
Zack Buhman 2024-08-05 21:25:15 -05:00
parent 70315ef0d8
commit 15ac0aa031
13 changed files with 390 additions and 9 deletions

6
.gitignore vendored
View File

@ -1,4 +1,5 @@
*.o
*.d
*.gch
test/test_tuples
test/test_canvas
@ -6,4 +7,9 @@ test/test_matrices
test/test_transformations
test/test_intersections
test/test_rays
test/test_lights
test/test_materials
test/test_spheres
raytracer
*.ppm
*.png

16
lights.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include "tuples.h"
struct light {
struct tuple position;
struct tuple intensity;
};
struct light point_light(struct tuple position, struct tuple intensity)
{
return (struct light){
position,
intensity
};
}

68
materials.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#include "tuples.h"
#include "lights.h"
#include "math.h"
struct material {
struct tuple color;
float ambient;
float diffuse;
float specular;
float shininess;
};
inline static struct material material()
{
return (struct material){
color(1.0f, 1.0f, 1.0f),
0.1f,
0.9f,
0.9f,
200.0f
};
}
inline static bool material_equal(struct material a, struct material b)
{
return
tuple_equal(a.color, b.color) &&
float_equal(a.ambient, b.ambient) &&
float_equal(a.diffuse, b.diffuse) &&
float_equal(a.specular, b.specular) &&
float_equal(a.shininess, b.shininess);
}
inline static struct tuple lighting(struct material material,
struct light light,
struct tuple point,
struct tuple eyev,
struct tuple normalv)
{
struct tuple effective_color = hadmard_product(material.color, light.intensity);
struct tuple lightv = tuple_normalize(tuple_sub(light.position, point));
struct tuple ambient = tuple_mul(effective_color, material.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
} else {
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
} else {
float factor = powf(reflect_dot_eye, material.shininess);
specular = tuple_mul(light.intensity, material.specular * factor);
}
}
return tuple_add(tuple_add(ambient, diffuse), specular);
}

1
math.h
View File

@ -4,3 +4,4 @@
#define fabsf __builtin_fabsf
#define cosf __builtin_cosf
#define sinf __builtin_sinf
#define powf __builtin_powf

View File

@ -4,6 +4,7 @@
#include "spheres.h"
#include "intersections.h"
#include "transformations.h"
#include "materials.h"
int main()
{
@ -15,12 +16,18 @@ int main()
float pixel_size = wall_size / (float)canvas_pixels;
float half = wall_size / 2.0f;
struct canvas c = canvas(canvas_pixels, canvas_pixels);
struct tuple red = color(1.0f, 0.0f, 0.0f);
//struct tuple red = color(1.0f, 0.0f, 0.0f);
struct sphere shape = sphere();
struct mat4x4 shear = shearing(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
struct mat4x4 scale = scaling(0.5f, 1.0f, 1.0f);
struct mat4x4 m = mat4x4_mul_m(&shear, &scale);
shape.transform = m;
shape.material = material();
shape.material.color = color(1.0f, 0.2f, 1.0f);
//struct mat4x4 shear = shearing(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
//struct mat4x4 scale = scaling(0.5f, 1.0f, 1.0f);
//struct mat4x4 m = mat4x4_mul_m(&shear, &scale);
//shape.transform = m;
struct tuple light_position = point(-10.0f, 10.0f, -10.0f);
struct tuple light_color = color(1.0f, 1.0f, 1.0f);
struct light light = point_light(light_position, light_color);
for (int y = 0; y < canvas_pixels; y++) {
float world_y = half - pixel_size * y;
@ -31,8 +38,14 @@ int main()
struct ray r = ray(ray_origin, tuple_normalize(tuple_sub(position, ray_origin)));
struct intersections2 xs = intersect(&shape, r);
struct intersection * h = hit((struct intersections *)&xs);
if (h != NULL) {
canvas_write_pixel(c, x, y, red);
struct tuple point = ray_position(r, h->t);
struct tuple normal = sphere_normal_at(h->object, point);
struct tuple eye = tuple_neg(r.direction);
struct tuple color = lighting(h->object->material, light, point, eye, normal);
canvas_write_pixel(c, x, y, color);
}
}
}

View File

@ -1,14 +1,28 @@
#pragma once
#include "matrices.h"
#include "materials.h"
struct sphere {
struct mat4x4 transform;
struct material material;
};
inline static struct sphere sphere()
{
return (struct sphere){
mat4x4_identity()
mat4x4_identity(),
material()
};
}
inline static struct tuple sphere_normal_at(struct sphere const * const s, struct tuple world_point)
{
struct mat4x4 inv = mat4x4_inverse(&s->transform);
struct tuple object_point = mat4x4_mul_t(&inv, &world_point);
struct tuple object_normal = tuple_sub(object_point, point(0.0f, 0.0f, 0.0f));
struct mat4x4 inv_t = mat4x4_transpose(&inv);
struct tuple world_normal = mat4x4_mul_t(&inv_t, &object_normal);
world_normal.w = 0.0f;
return tuple_normalize(world_normal);
}

View File

@ -2,7 +2,7 @@
set -eux
for name in tuples canvas matrices transformations rays intersections spheres; do
for name in tuples canvas matrices transformations rays intersections spheres lights materials; do
gcc -g -gdwarf-5 \
-Wall -Werror -Wfatal-errors \
-I. \

23
test/test_lights.c Normal file
View File

@ -0,0 +1,23 @@
#include <stdbool.h>
#include <stdio.h>
#include "lights.h"
#include "runner.h"
static bool lights_test_0(const char ** scenario)
{
*scenario = "A point light has a position and intensity";
struct tuple position = color(0.0f, 0.0f, 0.0f);
struct tuple intensity = color(1.0f, 1.0f, 1.0f);
struct light light = point_light(position, intensity);
return
tuple_equal(light.position, position) &&
tuple_equal(light.intensity, intensity);
}
test_t lights_tests[] = {
lights_test_0,
};
RUNNER(lights_tests)

105
test/test_materials.c Normal file
View File

@ -0,0 +1,105 @@
#include <stdbool.h>
#include <stdio.h>
#include "materials.h"
#include "runner.h"
#include "lights.h"
static bool materials_test_0(const char ** scenario)
{
*scenario = "The default material";
struct material m = material();
return
tuple_equal(m.color, color(1.0f, 1.0f, 1.0f)) &&
float_equal(m.ambient, 0.1f) &&
float_equal(m.diffuse, 0.9f) &&
float_equal(m.specular, 0.9f) &&
float_equal(m.shininess, 200.0f);
}
static bool materials_test_1(const char ** scenario)
{
*scenario = "Lighting with the eye between the light and the surface";
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));
struct tuple result = lighting(m, light, position, eyev, normalv);
return tuple_equal(result, color(1.9f, 1.9f, 1.9f));
}
static bool materials_test_2(const char ** scenario)
{
*scenario = "Lighting with the eye between the light and surface, eye offset 45 degrees";
struct material m = material();
struct tuple position = point(0.0f, 0.0f, 0.0f);
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);
return tuple_equal(result, color(1.0f, 1.0f, 1.0f));
}
static bool materials_test_3(const char ** scenario)
{
*scenario = "Lighting with eye opposite surface, light offset 45 degress";
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, 10.0f, -10.0f), color(1.0f, 1.0f, 1.0f));
struct tuple result = lighting(m, light, position, eyev, normalv);
return tuple_equal(result, color(0.7364f, 0.7364f, 0.7364f));
}
static bool materials_test_4(const char ** scenario)
{
*scenario = "Lighting with eye in the path of the reflection vector";
struct material m = material();
struct tuple position = point(0.0f, 0.0f, 0.0f);
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);
return tuple_equal(result, color(1.63639, 1.63639, 1.63639));
}
static bool materials_test_5(const char ** scenario)
{
*scenario = "Lighting with the light behind the surface";
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));
struct tuple result = lighting(m, light, position, eyev, normalv);
return tuple_equal(result, color(0.1f, 0.1f, 0.1f));
}
test_t materials_tests[] = {
materials_test_0,
materials_test_1,
materials_test_2,
materials_test_3,
materials_test_4,
materials_test_5,
};
RUNNER(materials_tests)

View File

@ -137,6 +137,103 @@ static bool spheres_test_9(const char ** scenario)
return xs.count == 0;
}
static bool spheres_test_10(const char ** scenario)
{
*scenario = "The normal on a sphere at a point on the x axis";
struct sphere s = sphere();
struct tuple n = sphere_normal_at(&s, point(1.0f, 0.0f, 0.0f));
return tuple_equal(n, vector(1.0f, 0.0f, 0.0f));
}
static bool spheres_test_11(const char ** scenario)
{
*scenario = "The normal on a sphere at a point on the y axis";
struct sphere s = sphere();
struct tuple n = sphere_normal_at(&s, point(0.0f, 1.0f, 0.0f));
return tuple_equal(n, vector(0.0f, 1.0f, 0.0f));
}
static bool spheres_test_12(const char ** scenario)
{
*scenario = "The normal on a sphere at a point on the z axis";
struct sphere s = sphere();
struct tuple n = sphere_normal_at(&s, point(0.0f, 0.0f, 1.0f));
return tuple_equal(n, vector(0.0f, 0.0f, 1.0f));
}
static bool spheres_test_13(const char ** scenario)
{
*scenario = "The normal on a sphere at a nonaxial point";
struct sphere s = sphere();
struct tuple n = sphere_normal_at(&s, point(0.5773502691896257,
0.5773502691896257,
0.5773502691896257));
return tuple_equal(n, vector(0.5773502691896257,
0.5773502691896257,
0.5773502691896257));
}
static bool spheres_test_14(const char ** scenario)
{
*scenario = "The normal is a normalized vector";
struct sphere s = sphere();
struct tuple n = sphere_normal_at(&s, point(0.5773502691896257,
0.5773502691896257,
0.5773502691896257));
return tuple_equal(n, tuple_normalize(n));
}
static bool spheres_test_15(const char ** scenario)
{
*scenario = "Computing the normal on a translated sphere";
struct sphere s = sphere();
s.transform = translation(0.0f, 1.0f, 0.0f);
struct tuple n = sphere_normal_at(&s, point(0.0f, 1.70711f, -0.70711));
return tuple_equal(n, vector(0.0f, 0.70711f, -0.70711f));
}
static bool spheres_test_16(const char ** scenario)
{
*scenario = "Computing the normal on a transformed sphere";
struct sphere s = sphere();
struct mat4x4 scale = scaling(1.0f, 0.5f, 1.0f);
struct mat4x4 rotate = rotation_z(pi / 5.0f);
struct mat4x4 m = mat4x4_mul_m(&scale, &rotate);
s.transform = m;
struct tuple n = sphere_normal_at(&s, point(0.0f, 0.7071067811865476, -0.7071067811865476));
return tuple_equal(n, vector(0.0f, 0.97014f, -0.24254f));
}
static bool spheres_test_17(const char ** scenario)
{
*scenario = "A sphere has a default material";
struct sphere s = sphere();
struct material m = s.material;
return material_equal(m, material());
}
static bool spheres_test_18(const char ** scenario)
{
*scenario = "A sphere may be assigned a material";
struct sphere s = sphere();
struct material m = material();
m.ambient = 1.0f;
s.material = m;
return material_equal(s.material, m);
}
test_t spheres_tests[] = {
spheres_test_0,
spheres_test_1,
@ -148,6 +245,15 @@ test_t spheres_tests[] = {
spheres_test_7,
spheres_test_8,
spheres_test_9,
spheres_test_10,
spheres_test_11,
spheres_test_12,
spheres_test_13,
spheres_test_14,
spheres_test_15,
spheres_test_16,
spheres_test_17,
spheres_test_18,
};
RUNNER(spheres_tests);

View File

@ -235,6 +235,26 @@ static bool tuple_test_27(const char ** scenario)
return tuple_equal(hadmard_product(c1, c2), color(0.9f, 0.2f, 0.04f));
}
static bool tuple_test_28(const char ** scenario)
{
*scenario = "Reflecting a vector approacing at 45 degrees";
struct tuple v = vector(1.0f, -1.0f, 0.0f);
struct tuple n = vector(0.0f, 1.0f, 0.0f);
struct tuple r = tuple_reflect(v, n);
return tuple_equal(r, vector(1.0f, 1.0f, 0.0f));
}
static bool tuple_test_29(const char ** scenario)
{
*scenario = "Reflecting a vector off a slanted surface";
struct tuple v = vector(0.0f, -1.0f, 0.0f);
struct tuple n = vector(0.7071067811865476f, 0.7071067811865476f, 0.0f);
struct tuple r = tuple_reflect(v, n);
return tuple_equal(r, vector(1.0f, 0.0f, 0.0f));
}
test_t tuple_tests[] = {
tuple_test_0,
tuple_test_1,
@ -264,6 +284,8 @@ test_t tuple_tests[] = {
tuple_test_25,
tuple_test_26,
tuple_test_27,
tuple_test_28,
tuple_test_29,
};
RUNNER(tuple_tests)

View File

@ -20,6 +20,7 @@ inline static struct mat4x4 scaling(float x, float y, float z)
}
static const float tau = 6.283185307179586;
static const float pi = tau / 2.0f;
inline static struct mat4x4 rotation_x(float r)
{

View File

@ -153,3 +153,9 @@ inline static struct tuple hadmard_product(struct tuple c1, struct tuple c2)
0.0f
);
}
inline static struct tuple tuple_reflect(struct tuple in, struct tuple normal)
{
float dot = tuple_dot(in, normal);
return tuple_sub(in, tuple_mul(tuple_mul(normal, 2.0f), dot));
}