Add hittable objects
This commit is contained in:
parent
6352cbf0fd
commit
ec322b7c16
2
build.sh
2
build.sh
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
CC=${CC:-"gcc"}
|
||||
CFLAGS=${CFLAGS:-"-Wall -Wextra -std=c18 -g"}
|
||||
CFLAGS=${CFLAGS:-"-Wall -Wextra -std=gnu18 -g -fsanitize=address"}
|
||||
LDFLAGS=${LDFLAGS:-"-lm"}
|
||||
MAIN_FILE=${MAIN_FILE:-"main.c"}
|
||||
|
||||
|
||||
6
color.c
6
color.c
@ -1,15 +1,15 @@
|
||||
#include "color.h"
|
||||
#include <stdio.h>
|
||||
|
||||
color color_lerp(color c1, color c2, double t) {
|
||||
return (color){
|
||||
Color color_lerp(Color c1, Color c2, double t) {
|
||||
return (Color){
|
||||
(1.0 - t) * c1.r + t * c2.r,
|
||||
(1.0 - t) * c1.g + t * c2.g,
|
||||
(1.0 - t) * c1.b + t * c2.b,
|
||||
};
|
||||
}
|
||||
|
||||
void color_write(FILE *out, color c) {
|
||||
void color_write(FILE *out, Color c) {
|
||||
fprintf(out, "%d %d %d\n", (int)(255.999 * c.r), (int)(255.999 * c.g),
|
||||
(int)(255.999 * c.b));
|
||||
}
|
||||
|
||||
8
color.h
8
color.h
@ -3,12 +3,12 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct color {
|
||||
typedef struct Color {
|
||||
double r, g, b;
|
||||
} color;
|
||||
} Color;
|
||||
|
||||
color color_lerp(color c1, color c2, double t);
|
||||
Color color_lerp(Color c1, Color c2, double t);
|
||||
|
||||
void color_write(FILE *out, color c);
|
||||
void color_write(FILE *out, Color c);
|
||||
|
||||
#endif /* INCLUDED_COLOR_H */
|
||||
|
||||
98
hittable.c
Normal file
98
hittable.c
Normal file
@ -0,0 +1,98 @@
|
||||
#include "hittable.h"
|
||||
#include "point3.h"
|
||||
#include "vec3.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void hit_record_set_face_normal(HitRecord *record, Ray r, Vec3 outward_normal) {
|
||||
record->front_face = vec3_dot(r.direction, outward_normal) < 0;
|
||||
record->normal =
|
||||
record->front_face ? outward_normal : vec3_neg(outward_normal);
|
||||
}
|
||||
|
||||
bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
|
||||
HitRecord *record) {
|
||||
switch (hittable->type) {
|
||||
case HITTABLE_LIST:
|
||||
return hittable_list_hit(hittable->data, r, t_min, t_max, record);
|
||||
case HITTABLE_SPHERE:
|
||||
return sphere_hit(hittable->data, r, t_min, t_max, record);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Hittable make_hittable_list(const HittableList *list) {
|
||||
return (Hittable){.type = HITTABLE_LIST, .data = list};
|
||||
}
|
||||
|
||||
static void hittable_list_grow(HittableList *list, size_t n) {
|
||||
if (list->objects) {
|
||||
list->objects = realloc(list->objects, n);
|
||||
if (!list->objects)
|
||||
abort();
|
||||
list->capacity = n;
|
||||
} else {
|
||||
list->objects = malloc(n * sizeof(Hittable));
|
||||
if (!list->objects)
|
||||
abort();
|
||||
list->capacity = n;
|
||||
list->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void hittable_list_add(HittableList *list, Hittable hittable) {
|
||||
if (list->capacity == list->size)
|
||||
hittable_list_grow(list, list->capacity == 0 ? 16 : list->capacity);
|
||||
list->objects[list->size++] = hittable;
|
||||
}
|
||||
|
||||
bool hittable_list_hit(const HittableList *list, Ray r, double t_min,
|
||||
double t_max, HitRecord *record) {
|
||||
bool hit_anything = false;
|
||||
double closest_so_far = t_max;
|
||||
|
||||
for (size_t i = 0; i < list->size; ++i) {
|
||||
if (hittable_hit(&list->objects[i], r, t_min, closest_so_far, record)) {
|
||||
hit_anything = true;
|
||||
closest_so_far = record->t;
|
||||
}
|
||||
}
|
||||
|
||||
return hit_anything;
|
||||
}
|
||||
|
||||
void hittable_list_free(HittableList *list) {
|
||||
free(list->objects);
|
||||
list->objects = 0;
|
||||
}
|
||||
|
||||
Hittable make_hittable_sphere(const Sphere *sphere) {
|
||||
return (Hittable){.type = HITTABLE_SPHERE, sphere};
|
||||
}
|
||||
|
||||
bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
|
||||
HitRecord *record) {
|
||||
Vec3 oc = point3_sub(r.origin, sphere->center);
|
||||
double a = vec3_length2(r.direction);
|
||||
double half_b = vec3_dot(oc, r.direction);
|
||||
double c = vec3_length2(oc) - sphere->radius * sphere->radius;
|
||||
double discriminant = half_b * half_b - a * c;
|
||||
if (discriminant < 0)
|
||||
return false;
|
||||
|
||||
double square_root = sqrt(discriminant);
|
||||
double root = (-half_b - square_root) / a;
|
||||
if (root < t_min || t_max < root) {
|
||||
root = (-half_b + square_root) / a;
|
||||
if (root < t_min || t_max < root)
|
||||
return false;
|
||||
}
|
||||
|
||||
record->t = root;
|
||||
record->p = ray_at(r, root);
|
||||
Vec3 outward_normal =
|
||||
vec3_div(point3_sub(record->p, sphere->center), sphere->radius);
|
||||
hit_record_set_face_normal(record, r, outward_normal);
|
||||
return true;
|
||||
}
|
||||
56
hittable.h
Normal file
56
hittable.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef INCLUDED_HITTABLE_H
|
||||
#define INCLUDED_HITTABLE_H
|
||||
|
||||
#include "point3.h"
|
||||
#include "ray.h"
|
||||
#include "vec3.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct HitRecord {
|
||||
Point3 p;
|
||||
Vec3 normal;
|
||||
double t;
|
||||
bool front_face;
|
||||
} HitRecord;
|
||||
|
||||
void hit_record_set_face_normal(HitRecord *record, Ray r, Vec3 outward_normal);
|
||||
|
||||
typedef enum HittableType {
|
||||
HITTABLE_LIST,
|
||||
HITTABLE_SPHERE,
|
||||
} HittableType;
|
||||
|
||||
typedef struct Hittable {
|
||||
HittableType type;
|
||||
const void *data;
|
||||
} Hittable;
|
||||
|
||||
bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
|
||||
HitRecord *record);
|
||||
|
||||
typedef struct HittableList {
|
||||
Hittable *objects;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} HittableList;
|
||||
|
||||
Hittable make_hittable_list(const HittableList *list);
|
||||
|
||||
void hittable_list_add(HittableList *list, Hittable hittable);
|
||||
bool hittable_list_hit(const HittableList *list, Ray r, double t_min,
|
||||
double t_max, HitRecord *record);
|
||||
void hittable_list_free(HittableList *list);
|
||||
|
||||
typedef struct Sphere {
|
||||
Point3 center;
|
||||
double radius;
|
||||
} Sphere;
|
||||
|
||||
Hittable make_hittable_sphere(const Sphere *sphere);
|
||||
|
||||
bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
|
||||
HitRecord *record);
|
||||
|
||||
#endif /* INCLUDED_HITTABLE_H */
|
||||
67
main.c
67
main.c
@ -1,34 +1,24 @@
|
||||
#include <float.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "hittable.h"
|
||||
#include "point3.h"
|
||||
#include "ray.h"
|
||||
#include "vec3.h"
|
||||
|
||||
double hit_sphere(point3 center, double radius, ray r) {
|
||||
vec3 oc = point3_sub(r.origin, center);
|
||||
double a = vec3_length2(r.direction);
|
||||
double half_b = vec3_dot(oc, r.direction);
|
||||
double c = vec3_length2(oc) - radius * radius;
|
||||
double discriminant = half_b * half_b - a * c;
|
||||
if (discriminant < 0)
|
||||
return -1.0;
|
||||
else
|
||||
return (-half_b - sqrt(discriminant)) / a;
|
||||
}
|
||||
|
||||
color ray_color(ray r) {
|
||||
double t = hit_sphere((point3){0, 0, -1}, 0.5, r);
|
||||
if (t > 0.0) {
|
||||
vec3 n = vec3_normalize(point3_sub(ray_at(r, t), (point3){0, 0, -1}));
|
||||
return (color){0.5 * (n.x + 1), 0.5 * (n.y + 1), 0.5 * (n.z + 1)};
|
||||
Color ray_color(Ray r, Hittable world) {
|
||||
HitRecord record;
|
||||
if (hittable_hit(&world, r, 0, DBL_MAX, &record)) {
|
||||
return (Color){0.5 * (record.normal.x + 1), 0.5 * (record.normal.y + 1),
|
||||
0.5 * (record.normal.z + 1)};
|
||||
}
|
||||
vec3 unit_direction = vec3_normalize(r.direction);
|
||||
t = 0.5 * (unit_direction.y + 1.0);
|
||||
color gradient1 = {1.0, 1.0, 1.0};
|
||||
color gradient2 = {0.5, 0.7, 1.0};
|
||||
Vec3 unit_direction = vec3_normalize(r.direction);
|
||||
double t = 0.5 * (unit_direction.y + 1.0);
|
||||
Color gradient1 = {1.0, 1.0, 1.0};
|
||||
Color gradient2 = {0.5, 0.7, 1.0};
|
||||
return color_lerp(gradient1, gradient2, t);
|
||||
}
|
||||
|
||||
@ -38,17 +28,28 @@ int main(void) {
|
||||
const int image_width = 256;
|
||||
const int image_height = (int)(image_width / aspect_ratio);
|
||||
|
||||
/* World */
|
||||
HittableList world = {0};
|
||||
hittable_list_add(&world, make_hittable_sphere(&(Sphere){
|
||||
.center = (Point3){0, 0, -1},
|
||||
.radius = 0.5,
|
||||
}));
|
||||
hittable_list_add(&world, make_hittable_sphere(&(Sphere){
|
||||
.center = (Point3){0, -100.5, -1},
|
||||
.radius = 100,
|
||||
}));
|
||||
|
||||
/* Camera parameters */
|
||||
double viewport_height = 2;
|
||||
double viewport_width = aspect_ratio * viewport_height;
|
||||
double focal_length = 1.0;
|
||||
|
||||
point3 origin = {0};
|
||||
vec3 horizontal = {viewport_width, 0, 0};
|
||||
vec3 vertical = {0, viewport_height, 0};
|
||||
vec3 offset = vec3_add(vec3_div(horizontal, 2), vec3_div(vertical, 2));
|
||||
offset = vec3_add(offset, (vec3){0, 0, focal_length});
|
||||
point3 lower_left_corner = point3_add(origin, vec3_neg(offset));
|
||||
Point3 origin = {0};
|
||||
Vec3 horizontal = {viewport_width, 0, 0};
|
||||
Vec3 vertical = {0, viewport_height, 0};
|
||||
Vec3 offset = vec3_add(vec3_div(horizontal, 2), vec3_div(vertical, 2));
|
||||
offset = vec3_add(offset, (Vec3){0, 0, focal_length});
|
||||
Point3 lower_left_corner = point3_add(origin, vec3_neg(offset));
|
||||
|
||||
printf("P3\n%u %u\n255\n", image_width, image_height);
|
||||
|
||||
@ -57,22 +58,26 @@ int main(void) {
|
||||
for (int i = 0; i < image_width; ++i) {
|
||||
double u = (double)i / (image_width - 1);
|
||||
double v = (double)j / (image_height - 1);
|
||||
point3 screen_point =
|
||||
Point3 screen_point =
|
||||
point3_add(lower_left_corner,
|
||||
vec3_add(vec3_mul(u, horizontal), vec3_mul(v, vertical)));
|
||||
vec3 direction = point3_sub(screen_point, origin);
|
||||
ray r = {origin, direction};
|
||||
color pixel_color = ray_color(r);
|
||||
Vec3 direction = point3_sub(screen_point, origin);
|
||||
Ray r = {origin, direction};
|
||||
Color pixel_color = ray_color(r, make_hittable_list(&world));
|
||||
color_write(stdout, pixel_color);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\nDone.\n");
|
||||
|
||||
hittable_list_free(&world);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "color.c"
|
||||
#include "hittable.c"
|
||||
#include "point3.c"
|
||||
#include "ray.c"
|
||||
#include "utils.c"
|
||||
#include "vec3.c"
|
||||
|
||||
8
point3.c
8
point3.c
@ -1,9 +1,9 @@
|
||||
#include "point3.h"
|
||||
|
||||
point3 point3_add(point3 p, vec3 v) {
|
||||
return (point3){p.x + v.x, p.y + v.y, p.z + v.z};
|
||||
Point3 point3_add(Point3 p, Vec3 v) {
|
||||
return (Point3){p.x + v.x, p.y + v.y, p.z + v.z};
|
||||
}
|
||||
|
||||
vec3 point3_sub(point3 p1, point3 p2) {
|
||||
return (vec3){p1.x - p2.x, p1.y - p2.y, p1.z - p2.z};
|
||||
Vec3 point3_sub(Point3 p1, Point3 p2) {
|
||||
return (Vec3){p1.x - p2.x, p1.y - p2.y, p1.z - p2.z};
|
||||
}
|
||||
|
||||
8
point3.h
8
point3.h
@ -3,11 +3,11 @@
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
typedef struct point3 {
|
||||
typedef struct Point3 {
|
||||
double x, y, z;
|
||||
} point3;
|
||||
} Point3;
|
||||
|
||||
point3 point3_add(point3 p, vec3 v);
|
||||
vec3 point3_sub(point3 p1, point3 p2);
|
||||
Point3 point3_add(Point3 p, Vec3 v);
|
||||
Vec3 point3_sub(Point3 p1, Point3 p2);
|
||||
|
||||
#endif /* INCLUDED_POINT3_H */
|
||||
|
||||
2
ray.c
2
ray.c
@ -1,6 +1,6 @@
|
||||
#include "ray.h"
|
||||
#include "point3.h"
|
||||
|
||||
point3 ray_at(ray r, double t) {
|
||||
Point3 ray_at(Ray r, double t) {
|
||||
return point3_add(r.origin, vec3_mul(t, r.direction));
|
||||
}
|
||||
|
||||
10
ray.h
10
ray.h
@ -4,11 +4,11 @@
|
||||
#include "point3.h"
|
||||
#include "vec3.h"
|
||||
|
||||
typedef struct ray {
|
||||
point3 origin;
|
||||
vec3 direction;
|
||||
} ray;
|
||||
typedef struct Ray {
|
||||
Point3 origin;
|
||||
Vec3 direction;
|
||||
} Ray;
|
||||
|
||||
point3 ray_at(ray r, double t);
|
||||
Point3 ray_at(Ray r, double t);
|
||||
|
||||
#endif /* INCLUDED_RAY_H */
|
||||
|
||||
7
utils.c
Normal file
7
utils.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
double degrees_to_radians(double degrees) {
|
||||
return degrees * M_PI / 180.0;
|
||||
}
|
||||
6
utils.h
Normal file
6
utils.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef INCLUDED_UTILS_H
|
||||
#define INCLUDED_UTILS_H
|
||||
|
||||
double degrees_to_radians(double degrees);
|
||||
|
||||
#endif /* INCLUDED_UTILS_H */
|
||||
26
vec3.c
26
vec3.c
@ -2,32 +2,32 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
vec3 vec3_neg(vec3 v) { return (vec3){-v.x, -v.y, -v.z}; }
|
||||
Vec3 vec3_neg(Vec3 v) { return (Vec3){-v.x, -v.y, -v.z}; }
|
||||
|
||||
vec3 vec3_add(vec3 v1, vec3 v2) {
|
||||
return (vec3){v1.x + v2.x, v1.y + v2.y, v1.z + v2.z};
|
||||
Vec3 vec3_add(Vec3 v1, Vec3 v2) {
|
||||
return (Vec3){v1.x + v2.x, v1.y + v2.y, v1.z + v2.z};
|
||||
}
|
||||
|
||||
vec3 vec3_sub(vec3 v1, vec3 v2) {
|
||||
return (vec3){v1.x - v2.x, v1.y - v2.y, v1.z - v2.z};
|
||||
Vec3 vec3_sub(Vec3 v1, Vec3 v2) {
|
||||
return (Vec3){v1.x - v2.x, v1.y - v2.y, v1.z - v2.z};
|
||||
}
|
||||
|
||||
vec3 vec3_mul(double c, vec3 v) { return (vec3){c * v.x, c * v.y, c * v.z}; }
|
||||
Vec3 vec3_mul(double c, Vec3 v) { return (Vec3){c * v.x, c * v.y, c * v.z}; }
|
||||
|
||||
vec3 vec3_div(vec3 v, double c) { return (vec3){v.x / c, v.y / c, v.z / c}; }
|
||||
Vec3 vec3_div(Vec3 v, double c) { return (Vec3){v.x / c, v.y / c, v.z / c}; }
|
||||
|
||||
double vec3_length(vec3 v) { return sqrt(vec3_length2(v)); }
|
||||
double vec3_length(Vec3 v) { return sqrt(vec3_length2(v)); }
|
||||
|
||||
double vec3_length2(vec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
|
||||
double vec3_length2(Vec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
|
||||
|
||||
vec3 vec3_normalize(vec3 v) { return vec3_div(v, vec3_length(v)); }
|
||||
Vec3 vec3_normalize(Vec3 v) { return vec3_div(v, vec3_length(v)); }
|
||||
|
||||
double vec3_dot(vec3 v1, vec3 v2) {
|
||||
double vec3_dot(Vec3 v1, Vec3 v2) {
|
||||
return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
|
||||
}
|
||||
|
||||
vec3 vec3_cross(vec3 v1, vec3 v2) {
|
||||
return (vec3){
|
||||
Vec3 vec3_cross(Vec3 v1, Vec3 v2) {
|
||||
return (Vec3){
|
||||
v1.y * v2.z - v1.z * v2.y,
|
||||
v1.z * v2.x - v1.x * v2.z,
|
||||
v1.x * v2.y - v1.y * v2.x,
|
||||
|
||||
24
vec3.h
24
vec3.h
@ -1,22 +1,22 @@
|
||||
#ifndef INCLUDED_VEC3_H
|
||||
#define INCLUDED_VEC3_H
|
||||
|
||||
typedef struct vec3 {
|
||||
typedef struct Vec3 {
|
||||
double x, y, z;
|
||||
} vec3;
|
||||
} Vec3;
|
||||
|
||||
vec3 vec3_neg(vec3 v);
|
||||
vec3 vec3_add(vec3 v1, vec3 v2);
|
||||
vec3 vec3_sub(vec3 v1, vec3 v2);
|
||||
vec3 vec3_mul(double c, vec3 v);
|
||||
vec3 vec3_div(vec3 v, double c);
|
||||
Vec3 vec3_neg(Vec3 v);
|
||||
Vec3 vec3_add(Vec3 v1, Vec3 v2);
|
||||
Vec3 vec3_sub(Vec3 v1, Vec3 v2);
|
||||
Vec3 vec3_mul(double c, Vec3 v);
|
||||
Vec3 vec3_div(Vec3 v, double c);
|
||||
|
||||
double vec3_length(vec3 v);
|
||||
double vec3_length2(vec3 v);
|
||||
double vec3_length(Vec3 v);
|
||||
double vec3_length2(Vec3 v);
|
||||
|
||||
vec3 vec3_normalize(vec3 v);
|
||||
Vec3 vec3_normalize(Vec3 v);
|
||||
|
||||
double vec3_dot(vec3 v1, vec3 v2);
|
||||
vec3 vec3_cross(vec3 v1, vec3 v2);
|
||||
double vec3_dot(Vec3 v1, Vec3 v2);
|
||||
Vec3 vec3_cross(Vec3 v1, Vec3 v2);
|
||||
|
||||
#endif /* INCLUDED_VEC3_H */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user