Compare commits
2 Commits
9daf31ae4c
...
15ac0aa031
Author | SHA1 | Date | |
---|---|---|---|
15ac0aa031 | |||
70315ef0d8 |
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,7 +1,15 @@
|
||||
*.o
|
||||
*.d
|
||||
*.gch
|
||||
test/test_tuples
|
||||
test/test_canvas
|
||||
test/test_matrices
|
||||
test/test_transformations
|
||||
*.ppm
|
||||
test/test_intersections
|
||||
test/test_rays
|
||||
test/test_lights
|
||||
test/test_materials
|
||||
test/test_spheres
|
||||
raytracer
|
||||
*.ppm
|
||||
*.png
|
76
intersections.h
Normal file
76
intersections.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "spheres.h"
|
||||
#include "rays.h"
|
||||
|
||||
struct intersection {
|
||||
float t;
|
||||
struct sphere const * const object;
|
||||
};
|
||||
|
||||
struct intersection intersection(float t, struct sphere const * const object)
|
||||
{
|
||||
return (struct intersection){ t, object };
|
||||
}
|
||||
|
||||
struct intersections {
|
||||
int count;
|
||||
struct intersection i[];
|
||||
};
|
||||
|
||||
struct intersections2 {
|
||||
int count;
|
||||
struct intersection i[2];
|
||||
};
|
||||
|
||||
struct intersections4 {
|
||||
int count;
|
||||
struct intersection i[4];
|
||||
};
|
||||
|
||||
struct intersections2 intersections2(struct intersection a, struct intersection b)
|
||||
{
|
||||
return (struct intersections2){ 2, {a, b} };
|
||||
}
|
||||
|
||||
struct intersections4 intersections4(struct intersection a, struct intersection b, struct intersection c, struct intersection d)
|
||||
{
|
||||
return (struct intersections4){ 4, {a, b, c, d} };
|
||||
}
|
||||
|
||||
inline static struct intersections2 intersect(struct sphere const * const s, struct ray r)
|
||||
{
|
||||
struct mat4x4 m = mat4x4_inverse(&s->transform);
|
||||
struct ray r2 = ray_transform(r, &m);
|
||||
struct tuple sphere_to_ray = tuple_sub(r2.origin, point(0.0f, 0.0f, 0.0f));
|
||||
|
||||
float a = tuple_dot(r2.direction, r2.direction);
|
||||
float b = 2 * tuple_dot(r2.direction, sphere_to_ray);
|
||||
float c = tuple_dot(sphere_to_ray, sphere_to_ray) - 1;
|
||||
|
||||
float discriminant = b * b - 4 * a * c;
|
||||
|
||||
if (discriminant < 0) {
|
||||
return (struct intersections2) { 0 };
|
||||
} else {
|
||||
float root = sqrtf(discriminant);
|
||||
float t1 = (-b - root) / (2 * a);
|
||||
float t2 = (-b + root) / (2 * a);
|
||||
|
||||
return
|
||||
intersections2(intersection(t1, s),
|
||||
intersection(t2, s));
|
||||
}
|
||||
}
|
||||
|
||||
inline static struct intersection * hit(struct intersections * xs)
|
||||
{
|
||||
struct intersection * i = NULL;
|
||||
for (int n = 0; n < xs->count; n++) {
|
||||
if (xs->i[n].t >= 0) {
|
||||
if (i == NULL || i->t > xs->i[n].t)
|
||||
i = &xs->i[n];
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
16
lights.h
Normal file
16
lights.h
Normal 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
68
materials.h
Normal 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
1
math.h
@ -4,3 +4,4 @@
|
||||
#define fabsf __builtin_fabsf
|
||||
#define cosf __builtin_cosf
|
||||
#define sinf __builtin_sinf
|
||||
#define powf __builtin_powf
|
||||
|
26
rays.h
Normal file
26
rays.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "spheres.h"
|
||||
#include "tuples.h"
|
||||
#include "matrices.h"
|
||||
|
||||
struct ray {
|
||||
struct tuple origin;
|
||||
struct tuple direction;
|
||||
};
|
||||
|
||||
inline static struct ray ray(struct tuple origin, struct tuple direction)
|
||||
{
|
||||
return (struct ray){origin, direction};
|
||||
}
|
||||
|
||||
inline static struct tuple ray_position(struct ray ray, float t)
|
||||
{
|
||||
return tuple_add(ray.origin, tuple_mul(ray.direction, t));
|
||||
}
|
||||
|
||||
inline static struct ray ray_transform(struct ray r, struct mat4x4 const * const m)
|
||||
{
|
||||
return ray(mat4x4_mul_t(m, &r.origin),
|
||||
mat4x4_mul_t(m, &r.direction));
|
||||
}
|
54
raytracer.c
Normal file
54
raytracer.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "tuples.h"
|
||||
#include "canvas.h"
|
||||
#include "rays.h"
|
||||
#include "spheres.h"
|
||||
#include "intersections.h"
|
||||
#include "transformations.h"
|
||||
#include "materials.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
struct tuple ray_origin = point(0.0f, 0.0f, -5.0f);
|
||||
|
||||
float wall_z = 10.0f;
|
||||
float wall_size = 7.0f;
|
||||
int canvas_pixels = 100;
|
||||
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 sphere shape = sphere();
|
||||
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;
|
||||
for (int x = 0; x < canvas_pixels; x++) {
|
||||
float world_x = -half + pixel_size * x;
|
||||
|
||||
struct tuple position = point(world_x, world_y, wall_z);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvas_to_ppm(c, "test.ppm");
|
||||
}
|
28
spheres.h
Normal file
28
spheres.h
Normal file
@ -0,0 +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(),
|
||||
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);
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
set -eux
|
||||
|
||||
for name in tuples canvas matrices transformations; do
|
||||
for name in tuples canvas matrices transformations rays intersections spheres lights materials; do
|
||||
gcc -g -gdwarf-5 \
|
||||
-Wall -Werror -Wfatal-errors \
|
||||
-I. \
|
||||
|
@ -2,18 +2,26 @@
|
||||
|
||||
typedef bool (*test_t)(const char ** scenario);
|
||||
|
||||
#define ANSI_RED "\x1b[31m"
|
||||
#define ANSI_GREEN "\x1b[32m"
|
||||
#define ANSI_RESET "\x1b[0m"
|
||||
|
||||
#define RUNNER(tests) \
|
||||
int main() \
|
||||
{ \
|
||||
int fail_count = 0; \
|
||||
for (int i = 0; i < (sizeof (tests)) / (sizeof (test_t)); i++) { \
|
||||
const char * scenario = NULL; \
|
||||
const char * scenario = NULL; \
|
||||
bool result = tests[i](&scenario); \
|
||||
const char * result_s = result ? "ok" : "fail"; \
|
||||
const char * result_s = result ? "ok" : ANSI_RED "fail" ANSI_RESET; \
|
||||
fail_count += !result; \
|
||||
fprintf(stderr, "%s: %s\n", scenario, result_s); \
|
||||
} \
|
||||
fprintf(stderr, "\nfailed tests: %d\n", fail_count); \
|
||||
if (fail_count == 0) { \
|
||||
fprintf(stderr, ANSI_GREEN "failed tests: %d\n\n" ANSI_RESET, fail_count); \
|
||||
} else { \
|
||||
fprintf(stderr, ANSI_RED "failed tests: %d\n\n" ANSI_RESET, fail_count); \
|
||||
} \
|
||||
\
|
||||
return !(fail_count == 0); \
|
||||
}
|
||||
|
110
test/test_intersections.c
Normal file
110
test/test_intersections.c
Normal file
@ -0,0 +1,110 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "intersections.h"
|
||||
#include "runner.h"
|
||||
|
||||
static bool intersections_test_0(const char ** scenario)
|
||||
{
|
||||
*scenario = "An intersection encapsulates t and object";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct intersection i = intersection(3.5f, &s);
|
||||
|
||||
return
|
||||
float_equal(i.t, 3.5f) &&
|
||||
i.object == &s;
|
||||
}
|
||||
|
||||
static bool intersections_test_1(const char ** scenario)
|
||||
{
|
||||
*scenario = "Aggregating intersections2";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct intersection i1 = intersection(1.0f, &s);
|
||||
struct intersection i2 = intersection(2.0f, &s);
|
||||
struct intersections2 xs = intersections2(i1, i2);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
float_equal(xs.i[0].t, 1.0f) &&
|
||||
float_equal(xs.i[1].t, 2.0f);
|
||||
}
|
||||
|
||||
static bool intersections_test_2(const char ** scenario)
|
||||
{
|
||||
*scenario = "The hit, when all intersections2 have positive t";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct intersection i1 = intersection(1.0f, &s);
|
||||
struct intersection i2 = intersection(2.0f, &s);
|
||||
struct intersections2 xs = intersections2(i2, i1);
|
||||
|
||||
struct intersection * i = hit((struct intersections *)&xs);
|
||||
|
||||
return
|
||||
i != NULL &&
|
||||
i->t == i1.t &&
|
||||
i->object == i1.object;
|
||||
}
|
||||
|
||||
static bool intersections_test_3(const char ** scenario)
|
||||
{
|
||||
*scenario = "The hit, when some intersections2 have negative t";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct intersection i1 = intersection(-1.0f, &s);
|
||||
struct intersection i2 = intersection( 1.0f, &s);
|
||||
struct intersections2 xs = intersections2(i2, i1);
|
||||
|
||||
struct intersection * i = hit((struct intersections *)&xs);
|
||||
|
||||
return
|
||||
i != NULL &&
|
||||
i->t == i2.t &&
|
||||
i->object == i2.object;
|
||||
}
|
||||
|
||||
static bool intersections_test_4(const char ** scenario)
|
||||
{
|
||||
*scenario = "The hit, when all intersections2 have negative t";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct intersection i1 = intersection(-2.0f, &s);
|
||||
struct intersection i2 = intersection(-1.0f, &s);
|
||||
struct intersections2 xs = intersections2(i2, i1);
|
||||
|
||||
struct intersection * i = hit((struct intersections *)&xs);
|
||||
|
||||
return i == NULL;
|
||||
}
|
||||
|
||||
static bool intersections_test_5(const char ** scenario)
|
||||
{
|
||||
*scenario = "The hit is always the lowest nonnegative intersection";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct intersection i1 = intersection( 5.0f, &s);
|
||||
struct intersection i2 = intersection( 7.0f, &s);
|
||||
struct intersection i3 = intersection(-3.0f, &s);
|
||||
struct intersection i4 = intersection( 2.0f, &s);
|
||||
struct intersections4 xs = intersections4(i1, i2, i3, i4);
|
||||
|
||||
struct intersection * i = hit((struct intersections *)&xs);
|
||||
|
||||
return
|
||||
i != NULL &&
|
||||
i->t == i4.t &&
|
||||
i->object == i4.object;
|
||||
}
|
||||
|
||||
test_t intersections_tests[] = {
|
||||
intersections_test_0,
|
||||
intersections_test_1,
|
||||
intersections_test_2,
|
||||
intersections_test_3,
|
||||
intersections_test_4,
|
||||
intersections_test_5,
|
||||
};
|
||||
|
||||
RUNNER(intersections_tests);
|
23
test/test_lights.c
Normal file
23
test/test_lights.c
Normal 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
105
test/test_materials.c
Normal 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)
|
68
test/test_rays.c
Normal file
68
test/test_rays.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "rays.h"
|
||||
#include "runner.h"
|
||||
#include "transformations.h"
|
||||
|
||||
static bool rays_test_0(const char ** scenario)
|
||||
{
|
||||
*scenario = "Creating and querying a ray";
|
||||
|
||||
struct tuple origin = point(1.0f, 2.0f, 3.0f);
|
||||
struct tuple direction = vector(4.0f, 5.0f, 6.0f);
|
||||
struct ray r = ray(origin, direction);
|
||||
|
||||
return
|
||||
tuple_equal(r.origin, origin) &&
|
||||
tuple_equal(r.direction, direction);
|
||||
}
|
||||
|
||||
static bool rays_test_1(const char ** scenario)
|
||||
{
|
||||
*scenario = "Computing a point from a distance";
|
||||
|
||||
struct ray r = ray(point(2.0f, 3.0f, 4.0f), vector(1.0f, 0.0f, 0.0f));
|
||||
|
||||
return
|
||||
tuple_equal(ray_position(r, 0.0f), point(2.0f, 3.0f, 4.0f)) &&
|
||||
tuple_equal(ray_position(r, 1.0f), point(3.0f, 3.0f, 4.0f)) &&
|
||||
tuple_equal(ray_position(r, -1.0f), point(1.0f, 3.0f, 4.0f)) &&
|
||||
tuple_equal(ray_position(r, 2.5f), point(4.5f, 3.0f, 4.0f));
|
||||
}
|
||||
|
||||
|
||||
static bool rays_test_2(const char ** scenario)
|
||||
{
|
||||
*scenario = "Translating a ray";
|
||||
|
||||
struct ray r = ray(point(1.0f, 2.0f, 3.0f), vector(0.0f, 1.0f, 0.0f));
|
||||
struct mat4x4 m = translation(3.0f, 4.0f, 5.0f);
|
||||
struct ray r2 = ray_transform(r, &m);
|
||||
|
||||
return
|
||||
tuple_equal(r2.origin, point(4.0f, 6.0f, 8.0f)) &&
|
||||
tuple_equal(r2.direction, vector(0.0f, 1.0f, 0.0f));
|
||||
}
|
||||
|
||||
static bool rays_test_3(const char ** scenario)
|
||||
{
|
||||
*scenario = "Scaling a ray";
|
||||
|
||||
struct ray r = ray(point(1.0f, 2.0f, 3.0f), vector(0.0f, 1.0f, 0.0f));
|
||||
struct mat4x4 m = scaling(2.0f, 3.0f, 4.0f);
|
||||
struct ray r2 = ray_transform(r, &m);
|
||||
|
||||
return
|
||||
tuple_equal(r2.origin, point(2.0f, 6.0f, 12.0f)) &&
|
||||
tuple_equal(r2.direction, vector(0.0f, 3.0f, 0.0f));
|
||||
}
|
||||
|
||||
test_t rays_tests[] = {
|
||||
rays_test_0,
|
||||
rays_test_1,
|
||||
rays_test_2,
|
||||
rays_test_3,
|
||||
};
|
||||
|
||||
RUNNER(rays_tests);
|
259
test/test_spheres.c
Normal file
259
test/test_spheres.c
Normal file
@ -0,0 +1,259 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "rays.h"
|
||||
#include "spheres.h"
|
||||
#include "intersections.h"
|
||||
#include "runner.h"
|
||||
#include "matrices.h"
|
||||
#include "transformations.h"
|
||||
|
||||
static bool spheres_test_0(const char ** scenario)
|
||||
{
|
||||
*scenario = "A ray intersects a sphere at two points";
|
||||
|
||||
struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
float_equal(xs.i[0].t, 4.0f) &&
|
||||
float_equal(xs.i[1].t, 6.0f);
|
||||
}
|
||||
|
||||
static bool spheres_test_1(const char ** scenario)
|
||||
{
|
||||
*scenario = "A ray intersects a sphere at a tangent";
|
||||
|
||||
struct ray r = ray(point(0.0f, 1.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
float_equal(xs.i[0].t, 5.0f) &&
|
||||
float_equal(xs.i[1].t, 5.0f);
|
||||
}
|
||||
|
||||
static bool spheres_test_2(const char ** scenario)
|
||||
{
|
||||
*scenario = "A ray misses a sphere";
|
||||
|
||||
struct ray r = ray(point(0.0f, 2.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return xs.count == 0;
|
||||
}
|
||||
|
||||
static bool spheres_test_3(const char ** scenario)
|
||||
{
|
||||
*scenario = "A ray originates in a sphere";
|
||||
|
||||
struct ray r = ray(point(0.0f, 0.0f, 0.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
float_equal(xs.i[0].t, -1.0f) &&
|
||||
float_equal(xs.i[1].t, 1.0f);
|
||||
}
|
||||
|
||||
static bool spheres_test_4(const char ** scenario)
|
||||
{
|
||||
*scenario = "A sphere is behind a ray";
|
||||
|
||||
struct ray r = ray(point(0.0f, 0.0f, 5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
float_equal(xs.i[0].t, -6.0f) &&
|
||||
float_equal(xs.i[1].t, -4.0f);
|
||||
}
|
||||
|
||||
static bool spheres_test_5(const char ** scenario)
|
||||
{
|
||||
*scenario = "Intersect sets the object on the intersection";
|
||||
|
||||
struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
xs.i[0].object == &s &&
|
||||
xs.i[1].object == &s;
|
||||
}
|
||||
|
||||
static bool spheres_test_6(const char ** scenario)
|
||||
{
|
||||
*scenario = "A sphere's default transformation";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct mat4x4 identity = mat4x4_identity();
|
||||
|
||||
return mat4x4_equal(&s.transform, &identity);
|
||||
}
|
||||
|
||||
static bool spheres_test_7(const char ** scenario)
|
||||
{
|
||||
*scenario = "Changing a sphere's transformation";
|
||||
|
||||
struct sphere s = sphere();
|
||||
struct mat4x4 t = translation(2.0f, 3.0f, 4.0f);
|
||||
s.transform = t;
|
||||
|
||||
return mat4x4_equal(&s.transform, &t);
|
||||
}
|
||||
|
||||
static bool spheres_test_8(const char ** scenario)
|
||||
{
|
||||
*scenario = "Intersecting a scaled sphere with a ray";
|
||||
|
||||
struct ray r = ray(point(0.0f, 0.0f, -5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
s.transform = scaling(2.0f, 2.0f, 2.0f);
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
return
|
||||
xs.count == 2 &&
|
||||
float_equal(xs.i[0].t, 3.0f) &&
|
||||
float_equal(xs.i[1].t, 7.0f);
|
||||
}
|
||||
|
||||
static bool spheres_test_9(const char ** scenario)
|
||||
{
|
||||
*scenario = "Intersecting a translated sphere with a ray";
|
||||
|
||||
struct ray r = ray(point(0.0f, 0.0f, 5.0f), vector(0.0f, 0.0f, 1.0f));
|
||||
struct sphere s = sphere();
|
||||
s.transform = translation(5.0f, 0.0f, 0.0f);
|
||||
struct intersections2 xs = intersect(&s, r);
|
||||
|
||||
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,
|
||||
spheres_test_2,
|
||||
spheres_test_3,
|
||||
spheres_test_4,
|
||||
spheres_test_5,
|
||||
spheres_test_6,
|
||||
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);
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
6
tuples.h
6
tuples.h
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user