Add basic transforms

This change allows us to render a complete Cornell box.
This commit is contained in:
Jean-Michel Gorius 2022-11-13 19:41:48 +01:00
parent e78b539258
commit 990b36d9eb
5 changed files with 163 additions and 9 deletions

View File

@ -6,3 +6,4 @@ C implementation of the [*Ray tracing in one week-end*](https://raytracing.githu
![Ray tracing in one week-end book cover](examples/book-cover.png) ![Ray tracing in one week-end book cover](examples/book-cover.png)
![Earth](examples/earth.png) ![Earth](examples/earth.png)
![Cornell box](examples/cornell.png)

BIN
examples/cornell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 KiB

View File

@ -8,6 +8,7 @@
#include "vec3.h" #include "vec3.h"
#include <assert.h> #include <assert.h>
#include <float.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -194,6 +195,60 @@ Hittable *hittable_create_box(Point3 p0, Point3 p1, const Material *material,
return result; return result;
} }
Hittable *hittable_create_translation(Hittable *ptr, Vec3 offset,
Arena *arena) {
Hittable *result = arena_alloc(arena, sizeof(Hittable));
result->type = HITTABLE_TRANSLATION;
result->translation.ptr = ptr;
result->translation.offset = offset;
return result;
}
Hittable *hittable_create_y_rotation(Hittable *ptr, double angle,
Arena *arena) {
Hittable *result = arena_alloc(arena, sizeof(Hittable));
result->type = HITTABLE_Y_ROTATION;
result->y_rotation.ptr = ptr;
double radians = degrees_to_radians(angle);
double sin_theta = sin(radians);
double cos_theta = cos(radians);
result->y_rotation.sin_theta = sin_theta;
result->y_rotation.cos_theta = cos_theta;
result->y_rotation.has_box =
hittable_bounding_box(ptr, 0.0, 1.0, &result->y_rotation.bounding_box);
Point3 min = {DBL_MAX, DBL_MAX, DBL_MAX};
Point3 max = {-DBL_MAX, -DBL_MAX, -DBL_MAX};
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < 2; ++k) {
const AABB *bbox = &result->y_rotation.bounding_box;
double x = i * bbox->max.x + (1 - i) * bbox->min.x;
double y = j * bbox->max.y + (1 - j) * bbox->min.y;
double z = k * bbox->max.z + (1 - k) * bbox->min.z;
double new_x = cos_theta * x + sin_theta * z;
double new_z = -sin_theta * x + cos_theta * z;
min.x = fmin(min.x, new_x);
max.x = fmax(max.x, new_x);
min.y = fmin(min.y, y);
max.y = fmax(max.y, y);
min.z = fmin(min.z, new_z);
max.z = fmax(max.z, new_z);
}
}
}
result->y_rotation.bounding_box = (AABB){min, max};
return result;
}
/*===----------------------------------------------------------------------===*/
static bool hittable_list_hit(const HittableList *list, Ray r, double t_min, static bool hittable_list_hit(const HittableList *list, Ray r, double t_min,
double t_max, HitRecord *record) { double t_max, HitRecord *record) {
bool hit_anything = false; bool hit_anything = false;
@ -367,6 +422,53 @@ static bool box_hit(const Box *box, Ray r, double t_min, double t_max,
return hittable_list_hit(&box->sides, r, t_min, t_max, record); return hittable_list_hit(&box->sides, r, t_min, t_max, record);
} }
static bool translation_hit(const Translation *translation, Ray r, double t_min,
double t_max, HitRecord *record) {
Ray moved_r = {point3_add(r.origin, vec3_neg(translation->offset)),
r.direction, r.time};
if (!hittable_hit(translation->ptr, moved_r, t_min, t_max, record))
return false;
record->p = point3_add(record->p, translation->offset);
hit_record_set_face_normal(record, moved_r, record->normal);
return true;
}
static bool y_rotation_hit(const YRotation *rotation, Ray r, double t_min,
double t_max, HitRecord *record) {
Point3 origin = {
rotation->cos_theta * r.origin.x - rotation->sin_theta * r.origin.z,
r.origin.y,
rotation->sin_theta * r.origin.x + rotation->cos_theta * r.origin.z,
};
Vec3 direction = {
rotation->cos_theta * r.direction.x - rotation->sin_theta * r.direction.z,
r.direction.y,
rotation->sin_theta * r.direction.x + rotation->cos_theta * r.direction.z,
};
Ray rotated_r = {origin, direction};
if (!hittable_hit(rotation->ptr, rotated_r, t_min, t_max, record))
return false;
record->p = (Point3){
rotation->cos_theta * record->p.x + rotation->sin_theta * record->p.z,
record->p.y,
-rotation->sin_theta * record->p.x + rotation->cos_theta * record->p.z,
};
Vec3 normal = {
rotation->cos_theta * record->normal.x +
rotation->sin_theta * record->normal.z,
record->normal.y,
-rotation->sin_theta * record->normal.x +
rotation->cos_theta * record->normal.z,
};
hit_record_set_face_normal(record, rotated_r, normal);
return true;
}
bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
HitRecord *record) { HitRecord *record) {
switch (hittable->type) { switch (hittable->type) {
@ -386,10 +488,16 @@ bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
return yz_rectangle_hit(&hittable->yz_rectangle, r, t_min, t_max, record); return yz_rectangle_hit(&hittable->yz_rectangle, r, t_min, t_max, record);
case HITTABLE_BOX: case HITTABLE_BOX:
return box_hit(&hittable->box, r, t_min, t_max, record); return box_hit(&hittable->box, r, t_min, t_max, record);
case HITTABLE_TRANSLATION:
return translation_hit(&hittable->translation, r, t_min, t_max, record);
case HITTABLE_Y_ROTATION:
return y_rotation_hit(&hittable->y_rotation, r, t_min, t_max, record);
} }
return false; return false;
} }
/*===----------------------------------------------------------------------===*/
static bool hittable_list_bounding_box(const HittableList *list, static bool hittable_list_bounding_box(const HittableList *list,
double time_start, double time_end, double time_start, double time_end,
AABB *bounding_box) { AABB *bounding_box) {
@ -482,6 +590,24 @@ static bool box_bounding_box(const Box *box, AABB *bounding_box) {
return true; return true;
} }
static bool translation_bounding_box(const Translation *translation,
double time_start, double time_end,
AABB *bounding_box) {
if (!hittable_bounding_box(translation->ptr, time_start, time_end,
bounding_box))
return false;
*bounding_box = (AABB){point3_add(bounding_box->min, translation->offset),
point3_add(bounding_box->max, translation->offset)};
return true;
}
static bool y_rotation_bounding_box(const YRotation *rotation,
AABB *bounding_box) {
*bounding_box = rotation->bounding_box;
return true;
}
bool hittable_bounding_box(const Hittable *hittable, double time_start, bool hittable_bounding_box(const Hittable *hittable, double time_start,
double time_end, AABB *bounding_box) { double time_end, AABB *bounding_box) {
switch (hittable->type) { switch (hittable->type) {
@ -503,6 +629,11 @@ bool hittable_bounding_box(const Hittable *hittable, double time_start,
return yz_rectangle_bounding_box(&hittable->yz_rectangle, bounding_box); return yz_rectangle_bounding_box(&hittable->yz_rectangle, bounding_box);
case HITTABLE_BOX: case HITTABLE_BOX:
return box_bounding_box(&hittable->box, bounding_box); return box_bounding_box(&hittable->box, bounding_box);
case HITTABLE_TRANSLATION:
return translation_bounding_box(&hittable->translation, time_start,
time_end, bounding_box);
case HITTABLE_Y_ROTATION:
return y_rotation_bounding_box(&hittable->y_rotation, bounding_box);
} }
return false; return false;
} }

View File

@ -31,6 +31,8 @@ typedef enum HittableType {
HITTABLE_XZ_RECTANGLE, HITTABLE_XZ_RECTANGLE,
HITTABLE_YZ_RECTANGLE, HITTABLE_YZ_RECTANGLE,
HITTABLE_BOX, HITTABLE_BOX,
HITTABLE_TRANSLATION,
HITTABLE_Y_ROTATION,
} HittableType; } HittableType;
typedef struct Hittable Hittable; typedef struct Hittable Hittable;
@ -81,6 +83,19 @@ typedef struct Box {
Point3 max; Point3 max;
} Box; } Box;
typedef struct Translation {
const Hittable *ptr;
Vec3 offset;
} Translation;
typedef struct YRotation {
const Hittable *ptr;
double sin_theta;
double cos_theta;
bool has_box;
AABB bounding_box;
} YRotation;
struct Hittable { struct Hittable {
HittableType type; HittableType type;
union { union {
@ -92,6 +107,8 @@ struct Hittable {
XZRectangle xz_rectangle; XZRectangle xz_rectangle;
YZRectangle yz_rectangle; YZRectangle yz_rectangle;
Box box; Box box;
Translation translation;
YRotation y_rotation;
}; };
}; };
@ -115,6 +132,8 @@ Hittable *hittable_create_yz_rectangle(double y0, double y1, double z0,
const Material *material, Arena *arena); const Material *material, Arena *arena);
Hittable *hittable_create_box(Point3 p0, Point3 p1, const Material *material, Hittable *hittable_create_box(Point3 p0, Point3 p1, const Material *material,
Arena *arena); Arena *arena);
Hittable *hittable_create_translation(Hittable *ptr, Vec3 offset, Arena *arena);
Hittable *hittable_create_y_rotation(Hittable *ptr, double angle, Arena *arena);
bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
HitRecord *record); HitRecord *record);

21
main.c
View File

@ -219,14 +219,17 @@ static Hittable *cornell_box(Arena *arena) {
&world->list, &world->list,
hittable_create_xy_rectangle(0, 555, 0, 555, 555, white, arena), arena); hittable_create_xy_rectangle(0, 555, 0, 555, 555, white, arena), arena);
hittable_list_add(&world->list, Hittable *box1 = hittable_create_box((Point3){0, 0, 0},
hittable_create_box((Point3){130, 0, 65}, (Point3){165, 330, 165}, white, arena);
(Point3){295, 165, 230}, white, arena), box1 = hittable_create_y_rotation(box1, 15, arena);
arena); box1 = hittable_create_translation(box1, (Vec3){265, 0, 295}, arena);
hittable_list_add(&world->list, hittable_list_add(&world->list, box1, arena);
hittable_create_box((Point3){265, 0, 295},
(Point3){430, 330, 460}, white, arena), Hittable *box2 = hittable_create_box((Point3){0, 0, 0},
arena); (Point3){165, 165, 165}, white, arena);
box2 = hittable_create_y_rotation(box2, -18, arena);
box2 = hittable_create_translation(box2, (Vec3){130, 0, 65}, arena);
hittable_list_add(&world->list, box2, arena);
Hittable *bvh_root = hittable_create_bvh_node( Hittable *bvh_root = hittable_create_bvh_node(
world->list.objects, 0, world->list.size, 0.0, 1.0, arena); world->list.objects, 0, world->list.size, 0.0, 1.0, arena);
@ -310,7 +313,7 @@ int main(int argc, char *argv[]) {
world = cornell_box(&arena); world = cornell_box(&arena);
aspect_ratio = 1.0; aspect_ratio = 1.0;
image_width = 600; image_width = 600;
samples_per_pixel = 200; samples_per_pixel = 1000;
background_color = (Color){0.0, 0.0, 0.0}; background_color = (Color){0.0, 0.0, 0.0};
look_from = (Point3){278.0, 278.0, -800.0}; look_from = (Point3){278.0, 278.0, -800.0};
look_at = (Point3){278.0, 278.0, 0.0}; look_at = (Point3){278.0, 278.0, 0.0};