├── .gitignore ├── AUTHORS ├── LICENSE ├── Makefile ├── baseline.ppm ├── idx_stack.h ├── main.c ├── math-toolkit.h ├── models.inc ├── objects.c ├── objects.h ├── primitives.h ├── raytracing.c ├── raytracing.h └── scripts ├── install-git-hooks └── pre-commit.hook /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.gch 3 | *.pch 4 | *.out 5 | raytracing 6 | use-models.h 7 | out.ppm 8 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | raytracing package is written by: 2 | Jim Huang 3 | Tim Hsu 4 | 5 | Copyrighted by: 6 | National Cheng Kung University, Taiwan 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | raytracing is freely redistributable under the two-clause BSD License: 2 | 3 | Copyright (C) 2016 National Cheng Kung University, Taiwan. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 | THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | EXEC = raytracing 2 | 3 | GIT_HOOKS := .git/hooks/pre-commit 4 | .PHONY: all 5 | all: $(GIT_HOOKS) $(EXEC) 6 | 7 | $(GIT_HOOKS): 8 | @scripts/install-git-hooks 9 | @echo 10 | 11 | CC ?= gcc 12 | CFLAGS = \ 13 | -std=gnu99 -Wall -O0 -g 14 | LDFLAGS = \ 15 | -lm 16 | 17 | ifeq ($(strip $(PROFILE)),1) 18 | PROF_FLAGS = -pg 19 | CFLAGS += $(PROF_FLAGS) 20 | LDFLAGS += $(PROF_FLAGS) 21 | endif 22 | 23 | OBJS := \ 24 | objects.o \ 25 | raytracing.o \ 26 | main.o 27 | 28 | %.o: %.c 29 | $(CC) $(CFLAGS) -c -o $@ $< 30 | 31 | 32 | $(EXEC): $(OBJS) 33 | $(CC) -o $@ $^ $(LDFLAGS) 34 | 35 | main.o: use-models.h 36 | use-models.h: models.inc Makefile 37 | @echo '#include "models.inc"' > use-models.h 38 | @egrep "^(light|sphere|rectangular) " models.inc | \ 39 | sed -e 's/^light /append_light/g' \ 40 | -e 's/light[0-9]/(\&&, \&lights);/g' \ 41 | -e 's/^sphere /append_sphere/g' \ 42 | -e 's/sphere[0-9]/(\&&, \&spheres);/g' \ 43 | -e 's/^rectangular /append_rectangular/g' \ 44 | -e 's/rectangular[0-9]/(\&&, \&rectangulars);/g' \ 45 | -e 's/ = {//g' >> use-models.h 46 | 47 | check: $(EXEC) 48 | @./$(EXEC) && diff -u baseline.ppm out.ppm || (echo Fail; exit) 49 | @echo "Verified OK" 50 | 51 | clean: 52 | $(RM) $(EXEC) $(OBJS) use-models.h \ 53 | out.ppm gmon.out 54 | -------------------------------------------------------------------------------- /baseline.ppm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sysprog21/raytracing/3165ec8995a75a3a88e86fce6761a92c33807499/baseline.ppm -------------------------------------------------------------------------------- /idx_stack.h: -------------------------------------------------------------------------------- 1 | #ifndef _RAY_IDX_STACK_H 2 | #define _RAY_IDX_STACK_H 3 | 4 | #define MAX_STACK_SIZE 16 5 | 6 | typedef struct { 7 | double idx; 8 | void *obj; 9 | } idx_stack_element; 10 | 11 | #define AIR_ELEMENT (idx_stack_element) { .obj = NULL, .idx = 1.0 } 12 | 13 | typedef struct { 14 | idx_stack_element data[MAX_STACK_SIZE]; 15 | int top; 16 | } idx_stack; 17 | 18 | static inline void idx_stack_init(idx_stack *stk) 19 | { 20 | stk->top = 0; 21 | } 22 | 23 | static inline void idx_stack_push(idx_stack *stk, idx_stack_element element) 24 | { 25 | if (stk->top < MAX_STACK_SIZE) 26 | stk->data[stk->top++] = element; 27 | } 28 | 29 | static inline int idx_stack_empty(idx_stack *stk) 30 | { 31 | return !stk->top; 32 | } 33 | 34 | static inline idx_stack_element idx_stack_pop(idx_stack *stk) 35 | { 36 | if (!idx_stack_empty(stk)) 37 | return stk->data[--stk->top]; 38 | return AIR_ELEMENT; 39 | } 40 | 41 | static inline idx_stack_element idx_stack_top(idx_stack *stk) 42 | { 43 | if (!idx_stack_empty(stk)) 44 | return stk->data[stk->top-1]; 45 | return AIR_ELEMENT; 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "primitives.h" 7 | #include "raytracing.h" 8 | 9 | #define OUT_FILENAME "out.ppm" 10 | 11 | #define ROWS 512 12 | #define COLS 512 13 | 14 | static void write_to_ppm(FILE *outfile, uint8_t *pixels, 15 | int width, int height) 16 | { 17 | fprintf(outfile, "P6\n%d %d\n%d\n", width, height, 255); 18 | fwrite(pixels, 1, height * width * 3, outfile); 19 | } 20 | 21 | static double diff_in_second(struct timespec t1, struct timespec t2) 22 | { 23 | struct timespec diff; 24 | if (t2.tv_nsec-t1.tv_nsec < 0) { 25 | diff.tv_sec = t2.tv_sec - t1.tv_sec - 1; 26 | diff.tv_nsec = t2.tv_nsec - t1.tv_nsec + 1000000000; 27 | } else { 28 | diff.tv_sec = t2.tv_sec - t1.tv_sec; 29 | diff.tv_nsec = t2.tv_nsec - t1.tv_nsec; 30 | } 31 | return (diff.tv_sec + diff.tv_nsec / 1000000000.0); 32 | } 33 | 34 | int main() 35 | { 36 | uint8_t *pixels; 37 | light_node lights = NULL; 38 | rectangular_node rectangulars = NULL; 39 | sphere_node spheres = NULL; 40 | color background = { 0.0, 0.1, 0.1 }; 41 | struct timespec start, end; 42 | 43 | #include "use-models.h" 44 | 45 | /* allocate by the given resolution */ 46 | pixels = malloc(sizeof(unsigned char) * ROWS * COLS * 3); 47 | if (!pixels) exit(-1); 48 | 49 | printf("# Rendering scene\n"); 50 | /* do the ray tracing with the given geometry */ 51 | clock_gettime(CLOCK_REALTIME, &start); 52 | raytracing(pixels, background, 53 | rectangulars, spheres, lights, &view, ROWS, COLS); 54 | clock_gettime(CLOCK_REALTIME, &end); 55 | { 56 | FILE *outfile = fopen(OUT_FILENAME, "wb"); 57 | write_to_ppm(outfile, pixels, ROWS, COLS); 58 | fclose(outfile); 59 | } 60 | 61 | delete_rectangular_list(&rectangulars); 62 | delete_sphere_list(&spheres); 63 | delete_light_list(&lights); 64 | free(pixels); 65 | printf("Done!\n"); 66 | printf("Execution time of raytracing() : %lf sec\n", diff_in_second(start, end)); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /math-toolkit.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAY_MATH_TOOLKIT_H 2 | #define __RAY_MATH_TOOLKIT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static inline 9 | void normalize(double *v) 10 | { 11 | double d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); 12 | assert(d != 0.0 && "Error calculating normal"); 13 | 14 | v[0] /= d; 15 | v[1] /= d; 16 | v[2] /= d; 17 | } 18 | 19 | static inline 20 | double length(const double *v) 21 | { 22 | return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); 23 | } 24 | 25 | static inline 26 | void add_vector(const double *a, const double *b, double *out) 27 | { 28 | for (int i = 0; i < 3; i++) 29 | out[i] = a[i] + b[i]; 30 | } 31 | 32 | static inline 33 | void subtract_vector(const double *a, const double *b, double *out) 34 | { 35 | for (int i = 0; i < 3; i++) 36 | out[i] = a[i] - b[i]; 37 | } 38 | 39 | static inline 40 | void multiply_vectors(const double *a, const double *b, double *out) 41 | { 42 | for (int i = 0; i < 3; i++) 43 | out[i] = a[i] * b[i]; 44 | } 45 | 46 | static inline 47 | void multiply_vector(const double *a, double b, double *out) 48 | { 49 | for (int i = 0; i < 3; i++) 50 | out[i] = a[i] * b; 51 | } 52 | 53 | static inline 54 | void cross_product(const double *v1, const double *v2, double *out) 55 | { 56 | out[0] = v1[1] * v2[2] - v1[2] * v2[1]; 57 | out[1] = v1[2] * v2[0] - v1[0] * v2[2]; 58 | out[2] = v1[0] * v2[1] - v1[1] * v2[0]; 59 | } 60 | 61 | static inline 62 | double dot_product(const double *v1, const double *v2) 63 | { 64 | double dp = 0.0; 65 | for (int i = 0; i < 3; i++) 66 | dp += v1[i] * v2[i]; 67 | return dp; 68 | } 69 | 70 | static inline 71 | void scalar_triple_product(const double *u, const double *v, const double *w, 72 | double *out) 73 | { 74 | cross_product(v, w, out); 75 | multiply_vectors(u, out, out); 76 | } 77 | 78 | static inline 79 | double scalar_triple(const double *u, const double *v, const double *w) 80 | { 81 | double tmp[3]; 82 | cross_product(w, u, tmp); 83 | return dot_product(v, tmp); 84 | } 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /models.inc: -------------------------------------------------------------------------------- 1 | static const 2 | light light1 = { 3 | .light_color = { 0.7, 0.5, 0.5 }, 4 | .position = { 5, 5, 20 }, 5 | .intensity = 200.0, 6 | }; 7 | 8 | static const 9 | light light2 = { 10 | .light_color = { 0.8, 0.8, 0.8 }, 11 | .position = { 10, 10, 20 }, 12 | .intensity = 200.0, 13 | }; 14 | 15 | static const 16 | sphere sphere1 = { 17 | .center = { 5, 0, 5 }, 18 | .radius = 3, 19 | .sphere_fill = { 20 | .fill_color = { 0.8, 0.8, 0.8 }, 21 | .Kd = 0.8, .Ks = 0.8, .T = 0.0, .R = 0.6, 22 | .index_of_refraction = 0.0, .phong_power = 30.0 23 | } 24 | }; 25 | 26 | static const 27 | sphere sphere2 = { 28 | .center = { 20, 15, 15 }, 29 | .radius = 3, 30 | .sphere_fill = { 31 | .fill_color = { 0.8, 0.6, 0.4 }, 32 | .Kd = 0.0, .Ks = 1.0, .T = 1.0, .R = 1.0, 33 | .index_of_refraction = 1.5, .phong_power = 30.0 34 | } 35 | }; 36 | 37 | static const 38 | sphere sphere3 = { 39 | .center = { 5, 10, 5 }, 40 | .radius = 3, 41 | .sphere_fill = { 42 | .fill_color = { 0.4, 0.8, 0.6 }, 43 | .Kd = 0.8, .Ks = 0.1, .T = 0.0, .R = 0.1, 44 | .index_of_refraction = 0.0, .phong_power = 30.0 45 | } 46 | }; 47 | 48 | static const 49 | viewpoint view = { 50 | .vrp = { 40.0, 40.0, 40.0 }, 51 | .vpn = { -1.0, -1.0, -1.0 }, 52 | .vup = { 0.0, 0.0, 1.0 } 53 | }; 54 | 55 | static const 56 | rectangular rectangular1 = { 57 | .vertices = { 58 | { 0.0, 0.0, 0.0 }, /* v0 */ 59 | { 0.0, 0.0, 20.0 }, /* v1 */ 60 | { 20.0, 0.0, 20.0 }, /* v3 */ 61 | { 20.0, 0.0, 0.0 }, /* v2 */ 62 | }, 63 | .normal = { 0.0, 1.0, 0.0 }, 64 | .rectangular_fill = { 65 | .fill_color = { 0.6, 0.6, 0.6 }, 66 | .Kd = 0.8, .Ks = 0.0, .T = 0.0, .R = 0.5, 67 | .index_of_refraction = 0.0, .phong_power = 5.0, 68 | }, 69 | }; 70 | 71 | static const 72 | rectangular rectangular2 = { 73 | .vertices = { 74 | { 0.0, 0.0, 0.0 }, /* v0 */ 75 | { 20.0, 0.0, 0.0 }, /* v1 */ 76 | { 20.0, 20.0, 0.0 }, /* v3 */ 77 | { 0.0, 20.0, 0.0 }, /* v2 */ 78 | }, 79 | .normal = { 0.0, 0.0, 1.0 }, 80 | .rectangular_fill = { 81 | .fill_color = { 0.6, 0.1, 0.1 }, 82 | .Kd = 0.8, .Ks = 0.0, .T = 0.0, .R = 0.5, 83 | .index_of_refraction = 0.0, .phong_power = 5.0, 84 | }, 85 | }; 86 | 87 | static const 88 | rectangular rectangular3 = { 89 | .vertices = { 90 | { 0.0, 0.0, 0.0 }, /* v0 */ 91 | { 0.0, 20.0, 0.0 }, /* v1 */ 92 | { 0.0, 20.0, 20.0 }, /* v3 */ 93 | { 0.0, 0.0, 20.0 }, /* v2 */ 94 | }, 95 | .normal = { 1.0, 0.0, 0.0 }, 96 | .rectangular_fill = { 97 | .fill_color = { 0.1, 0.1, 0.6 }, 98 | .Kd = 0.8, .Ks = 0.0, .T = 0.0, .R = 0.5, 99 | .index_of_refraction = 0.0, .phong_power = 5.0, 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /objects.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "primitives.h" 5 | #include "objects.h" 6 | 7 | #define FUNC_BEGIN(name) \ 8 | void append_##name (const name *X, name##_node *list) { \ 9 | name##_node newNode = malloc(sizeof(struct __##name##_node)); 10 | 11 | #define FUNC_END(name) \ 12 | newNode->next = NULL; \ 13 | if (!*list) { *list = newNode; } \ 14 | else { \ 15 | name##_node tmp; \ 16 | for (tmp = *list; tmp->next; tmp = tmp->next) ; \ 17 | tmp->next = newNode; \ 18 | } \ 19 | } \ 20 | void delete_##name##_list(name##_node *list) { \ 21 | name##_node pos = *list; \ 22 | while (pos) { \ 23 | name##_node tmp = pos->next; \ 24 | free(pos); pos = tmp; \ 25 | } \ 26 | *list = NULL; \ 27 | } 28 | 29 | // *INDENT-OFF* 30 | FUNC_BEGIN(light) 31 | COPY_POINT3(newNode->element.position, X->position); 32 | COPY_COLOR(newNode->element.light_color, X->light_color); 33 | newNode->element.intensity = X->intensity; 34 | FUNC_END(light) 35 | 36 | FUNC_BEGIN(rectangular) 37 | COPY_OBJECT_FILL(newNode->element.rectangular_fill, X->rectangular_fill); 38 | for (int i = 0; i < 4; i++) { 39 | COPY_POINT3(newNode->element.vertices[i], X->vertices[i]); 40 | COPY_POINT3(newNode->element.normal, X->normal); 41 | } 42 | FUNC_END(rectangular) 43 | 44 | FUNC_BEGIN(sphere) 45 | COPY_OBJECT_FILL(newNode->element.sphere_fill, X->sphere_fill); 46 | newNode->element.radius = X->radius; 47 | COPY_POINT3(newNode->element.center, X->center); 48 | FUNC_END(sphere) 49 | 50 | // *INDENT-ON* 51 | -------------------------------------------------------------------------------- /objects.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAY_OBJECTS_H 2 | #define __RAY_OBJECTS_H 3 | 4 | #define DECLARE_OBJECT(name) \ 5 | struct __##name##_node; \ 6 | typedef struct __##name##_node *name##_node; \ 7 | struct __##name##_node { \ 8 | name element; \ 9 | name##_node next; \ 10 | }; \ 11 | void append_##name(const name *X, name##_node *list); \ 12 | void delete_##name##_list(name##_node *list); 13 | 14 | DECLARE_OBJECT(light) 15 | DECLARE_OBJECT(rectangular) 16 | DECLARE_OBJECT(sphere) 17 | 18 | #undef DECLARE_OBJECT 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /primitives.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAY_PRIMITIVES_H 2 | #define __RAY_PRIMITIVES_H 3 | 4 | typedef double point3[3]; 5 | typedef double point4[3]; 6 | typedef double color[3]; 7 | 8 | typedef struct { 9 | color light_color; /**< scale (0,1) */ 10 | point3 position; 11 | double intensity; 12 | } light; 13 | 14 | typedef struct { 15 | color fill_color; /**< RGB is in terms of 0.0 to 1.0 */ 16 | double Kd; /**< the diffuse component */ 17 | double Ks; /**< the specular */ 18 | double T; /**< transmittance (fraction of light passed per unit) */ 19 | double R; /**< reflectance (effectiveness in reflecting)*/ 20 | double index_of_refraction; 21 | double phong_power; /**< the Phong cosine power for highlights */ 22 | } object_fill; 23 | 24 | typedef struct { 25 | point3 center; 26 | double radius; 27 | object_fill sphere_fill; 28 | } sphere; 29 | 30 | typedef struct { 31 | point3 vertices[4]; 32 | point3 normal; 33 | object_fill rectangular_fill; 34 | } rectangular; 35 | 36 | typedef struct { 37 | point3 vrp; 38 | point3 vpn; 39 | point3 vup; 40 | } viewpoint; 41 | 42 | typedef struct { 43 | point3 point; 44 | point3 normal; 45 | } intersection; 46 | 47 | #define COPY_OBJECT_FILL(a, b) { \ 48 | (a).fill_color[0] = (b).fill_color[0]; \ 49 | (a).fill_color[1] = (b).fill_color[1]; \ 50 | (a).fill_color[2] = (b).fill_color[2]; \ 51 | (a).Kd = (b).Kd; \ 52 | (a).Ks = (b).Ks; \ 53 | (a).R = (b).R; \ 54 | (a).phong_power = (b).phong_power; \ 55 | (a).T = (b).T; \ 56 | (a).index_of_refraction = (b).index_of_refraction; } 57 | 58 | #define COPY_POINT3(a, b) { \ 59 | (a)[0] = (b)[0]; \ 60 | (a)[1] = (b)[1]; \ 61 | (a)[2] = (b)[2]; } 62 | 63 | #define COPY_COLOR(a, b) { \ 64 | (a)[0] = (b)[0]; \ 65 | (a)[1] = (b)[1]; \ 66 | (a)[2] = (b)[2]; } 67 | 68 | #define SET_COLOR(r, R, G, B) { \ 69 | (r)[0] = (R); \ 70 | (r)[1] = (G); \ 71 | (r)[2] = (B); } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /raytracing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "math-toolkit.h" 5 | #include "primitives.h" 6 | #include "raytracing.h" 7 | #include "idx_stack.h" 8 | 9 | #define MAX_REFLECTION_BOUNCES 3 10 | #define MAX_DISTANCE 1000000000000.0 11 | #define MIN_DISTANCE 0.00001 12 | #define SAMPLES 4 13 | 14 | #define SQUARE(x) (x * x) 15 | #define MAX(a, b) (a > b ? a : b) 16 | 17 | /* @param t t distance 18 | * @return 1 means hit, otherwise 0 19 | */ 20 | static int raySphereIntersection(const point3 ray_e, 21 | const point3 ray_d, 22 | const sphere *sph, 23 | intersection *ip, double *t1) 24 | { 25 | point3 l; 26 | subtract_vector(sph->center, ray_e, l); 27 | double s = dot_product(l, ray_d); 28 | double l2 = dot_product(l, l); 29 | double r2 = sph->radius * sph->radius; 30 | 31 | if (s < 0 && l2 > r2) 32 | return 0; 33 | float m2 = l2 - s * s; 34 | if (m2 > r2) 35 | return 0; 36 | float q = sqrt(r2 - m2); 37 | *t1 = (l2 > r2) ? (s - q) : (s + q); 38 | /* p = e + t1 * d */ 39 | multiply_vector(ray_d, *t1, ip->point); 40 | add_vector(ray_e, ip->point, ip->point); 41 | 42 | subtract_vector(ip->point, sph->center, ip->normal); 43 | normalize(ip->normal); 44 | if (dot_product(ip->normal, ray_d) > 0.0) 45 | multiply_vector(ip->normal, -1, ip->normal); 46 | return 1; 47 | } 48 | 49 | /* @return 1 means hit, otherwise 0; */ 50 | static int rayRectangularIntersection(const point3 ray_e, 51 | const point3 ray_d, 52 | rectangular *rec, 53 | intersection *ip, double *t1) 54 | { 55 | point3 e01, e03, p; 56 | subtract_vector(rec->vertices[1], rec->vertices[0], e01); 57 | subtract_vector(rec->vertices[3], rec->vertices[0], e03); 58 | 59 | cross_product(ray_d, e03, p); 60 | 61 | double det = dot_product(e01, p); 62 | 63 | /* Reject rays orthagonal to the normal vector. 64 | * I.e. rays parallell to the plane. 65 | */ 66 | if (det < 1e-4) 67 | return 0; 68 | 69 | double inv_det = 1.0 / det; 70 | 71 | point3 s; 72 | subtract_vector(ray_e, rec->vertices[0], s); 73 | 74 | double alpha = inv_det * dot_product(s, p); 75 | 76 | if ((alpha > 1.0) || (alpha < 0.0)) 77 | return 0; 78 | 79 | point3 q; 80 | cross_product(s, e01, q); 81 | 82 | double beta = inv_det * dot_product(ray_d, q); 83 | if ((beta > 1.0) || (beta < 0.0)) 84 | return 0; 85 | 86 | *t1 = inv_det * dot_product(e03, q); 87 | 88 | if (alpha + beta > 1.0f) { 89 | /* for the second triangle */ 90 | point3 e23, e21; 91 | subtract_vector(rec->vertices[3], rec->vertices[2], e23); 92 | subtract_vector(rec->vertices[1], rec->vertices[2], e21); 93 | 94 | cross_product(ray_d, e21, p); 95 | 96 | det = dot_product(e23, p); 97 | 98 | if (det < 1e-4) 99 | return 0; 100 | 101 | inv_det = 1.0 / det; 102 | subtract_vector(ray_e, rec->vertices[2], s); 103 | 104 | alpha = inv_det * dot_product(s, p); 105 | if (alpha < 0.0) 106 | return 0; 107 | 108 | cross_product(s, e23, q); 109 | beta = inv_det * dot_product(ray_d, q); 110 | 111 | if ((beta < 0.0) || (beta + alpha > 1.0)) 112 | return 0; 113 | 114 | *t1 = inv_det * dot_product(e21, q); 115 | } 116 | 117 | if (*t1 < 1e-4) 118 | return 0; 119 | 120 | COPY_POINT3(ip->normal, rec->normal); 121 | if (dot_product(ip->normal, ray_d)>0.0) 122 | multiply_vector(ip->normal, -1, ip->normal); 123 | multiply_vector(ray_d, *t1, ip->point); 124 | add_vector(ray_e, ip->point, ip->point); 125 | 126 | return 1; 127 | } 128 | 129 | static void localColor(color local_color, 130 | const color light_color, double diffuse, 131 | double specular, const object_fill *fill) 132 | { 133 | color ambi = { 0.1, 0.1, 0.1 }; 134 | color diff, spec, lightCo, surface; 135 | 136 | /* Local Color = ambient * surface + 137 | * light * ( kd * surface * diffuse + ks * specular) 138 | */ 139 | 140 | COPY_COLOR(diff, fill->fill_color); 141 | multiply_vector(diff, fill->Kd, diff); 142 | multiply_vector(diff, diffuse, diff); 143 | COPY_COLOR(lightCo, light_color); 144 | multiply_vectors(diff, lightCo, diff); 145 | 146 | COPY_COLOR(spec, light_color); 147 | multiply_vector(spec, fill->Ks, spec); 148 | multiply_vector(spec, specular, spec); 149 | 150 | COPY_COLOR(surface, fill->fill_color); 151 | multiply_vectors(ambi,surface, ambi); 152 | add_vector(diff, ambi, diff); 153 | add_vector(diff, spec, diff); 154 | add_vector(local_color, diff, local_color); 155 | } 156 | 157 | /* @param d direction of the ray into intersection 158 | * @param l direction of intersection to light 159 | * @param n surface normal 160 | */ 161 | static void compute_specular_diffuse(double *diffuse, 162 | double *specular, 163 | const point3 d, const point3 l, 164 | const point3 n, double phong_pow) 165 | { 166 | point3 d_copy, l_copy, middle, r; 167 | 168 | /* Calculate vector to eye V */ 169 | COPY_POINT3(d_copy, d); 170 | multiply_vector(d_copy, -1, d_copy); 171 | normalize(d_copy); 172 | 173 | /* Calculate vector to light L */ 174 | COPY_POINT3(l_copy, l); 175 | multiply_vector(l_copy, -1, l_copy); 176 | normalize(l_copy); 177 | 178 | /* Calculate reflection direction R */ 179 | double tmp = dot_product(n, l_copy); 180 | multiply_vector(n, tmp, middle); 181 | multiply_vector(middle, 2, middle); 182 | subtract_vector(middle, l_copy, r); 183 | normalize(r); 184 | 185 | /* diffuse = max(0, dot_product(n, -l)) */ 186 | *diffuse = MAX(0, dot_product(n, l_copy)); 187 | 188 | /* specular = (dot_product(r, -d))^p */ 189 | *specular = pow(MAX(0, dot_product(r, d_copy)), phong_pow); 190 | } 191 | 192 | /* @param r direction of reflected ray 193 | * @param d direction of primary ray into intersection 194 | * @param n surface normal at intersection 195 | */ 196 | static void reflection(point3 r, const point3 d, const point3 n) 197 | { 198 | /* r = d - 2(d . n)n */ 199 | multiply_vector(n, -2.0 * dot_product(d, n), r); 200 | add_vector(r, d, r); 201 | } 202 | 203 | /* reference: https://www.opengl.org/sdk/docs/man/html/refract.xhtml */ 204 | static void refraction(point3 t, const point3 I, const point3 N, 205 | double n1, double n2) 206 | { 207 | double eta = n1 / n2; 208 | double dot_NI = dot_product(N,I); 209 | double k = 1.0 - eta * eta * (1.0 - dot_NI * dot_NI); 210 | if (k < 0.0 || n2 <= 0.0) 211 | t[0] = t[1] = t[2] = 0.0; 212 | else { 213 | point3 tmp; 214 | multiply_vector(I, eta, t); 215 | multiply_vector(N, eta * dot_NI + sqrt(k), tmp); 216 | subtract_vector(t, tmp, t); 217 | } 218 | } 219 | 220 | /* @param i direction of incoming ray, unit vector 221 | * @param r direction of refraction ray, unit vector 222 | * @param normal unit vector 223 | * @param n1 refraction index 224 | * @param n2 refraction index 225 | * 226 | * reference: http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf 227 | */ 228 | static double fresnel(const point3 r, const point3 l, 229 | const point3 normal, double n1, double n2) 230 | { 231 | /* TIR */ 232 | if (length(l) < 0.99) 233 | return 1.0; 234 | double cos_theta_i = -dot_product(r, normal); 235 | double cos_theta_t = -dot_product(l, normal); 236 | double r_vertical_root = (n1 * cos_theta_i - n2 * cos_theta_t) / 237 | (n1 * cos_theta_i + n2 * cos_theta_t); 238 | double r_parallel_root = (n2 * cos_theta_i - n1 * cos_theta_t) / 239 | (n2 * cos_theta_i + n1 * cos_theta_t); 240 | return (r_vertical_root * r_vertical_root + 241 | r_parallel_root * r_parallel_root) / 2.0; 242 | } 243 | 244 | /* @param t distance */ 245 | static intersection ray_hit_object(const point3 e, const point3 d, 246 | double t0, double t1, 247 | const rectangular_node rectangulars, 248 | rectangular_node *hit_rectangular, 249 | const sphere_node spheres, 250 | sphere_node *hit_sphere) 251 | { 252 | /* set these to not hit */ 253 | *hit_rectangular = NULL; 254 | *hit_sphere = NULL; 255 | 256 | point3 biased_e; 257 | multiply_vector(d, t0, biased_e); 258 | add_vector(biased_e, e, biased_e); 259 | 260 | double nearest = t1; 261 | intersection result, tmpresult; 262 | 263 | for (rectangular_node rec = rectangulars; rec; rec = rec->next) { 264 | if (rayRectangularIntersection(biased_e, d, &(rec->element), 265 | &tmpresult, &t1) && (t1 < nearest)) { 266 | /* hit is closest so far */ 267 | *hit_rectangular = rec; 268 | nearest = t1; 269 | result = tmpresult; 270 | } 271 | } 272 | 273 | /* check the spheres */ 274 | for (sphere_node sphere = spheres; sphere; sphere = sphere->next) { 275 | if (raySphereIntersection(biased_e, d, &(sphere->element), 276 | &tmpresult, &t1) && (t1 < nearest)) { 277 | *hit_sphere = sphere; 278 | *hit_rectangular = NULL; 279 | nearest = t1; 280 | result = tmpresult; 281 | } 282 | } 283 | 284 | return result; 285 | } 286 | 287 | /* @param d direction of ray 288 | * @param w basic vectors 289 | */ 290 | static void rayConstruction(point3 d, const point3 u, const point3 v, 291 | const point3 w, unsigned int i, unsigned int j, 292 | const viewpoint *view, unsigned int width, 293 | unsigned int height) 294 | { 295 | double xmin = -0.0175; 296 | double ymin = -0.0175; 297 | double xmax = 0.0175; 298 | double ymax = 0.0175; 299 | double focal = 0.05; 300 | 301 | point3 u_tmp, v_tmp, w_tmp, s; 302 | 303 | double w_s = focal; 304 | double u_s = xmin + ((xmax - xmin) * (float) i / (width - 1)); 305 | double v_s = ymax + ((ymin - ymax) * (float) j / (height - 1)); 306 | 307 | /* s = e + u_s * u + v_s * v + w_s * w */ 308 | multiply_vector(u, u_s, u_tmp); 309 | multiply_vector(v, v_s, v_tmp); 310 | multiply_vector(w, w_s, w_tmp); 311 | add_vector(view->vrp, u_tmp, s); 312 | add_vector(s, v_tmp, s); 313 | add_vector(s, w_tmp, s); 314 | 315 | /* p(t) = e + td = e + t(s - e) */ 316 | subtract_vector(s, view->vrp, d); 317 | normalize(d); 318 | } 319 | 320 | static void calculateBasisVectors(point3 u, point3 v, point3 w, 321 | const viewpoint *view) 322 | { 323 | /* w */ 324 | COPY_POINT3(w, view->vpn); 325 | normalize(w); 326 | 327 | /* u = (t x w) / (|t x w|) */ 328 | cross_product(w, view->vup, u); 329 | normalize(u); 330 | 331 | /* v = w x u */ 332 | cross_product(u, w, v); 333 | 334 | normalize(v); 335 | } 336 | 337 | /* @brief protect color value overflow */ 338 | static void protect_color_overflow(color c) 339 | { 340 | for (int i = 0; i < 3; i++) 341 | if (c[i] > 1.0) c[i] = 1.0; 342 | } 343 | 344 | static unsigned int ray_color(const point3 e, double t, 345 | const point3 d, 346 | idx_stack *stk, 347 | const rectangular_node rectangulars, 348 | const sphere_node spheres, 349 | const light_node lights, 350 | color object_color, int bounces_left) 351 | { 352 | rectangular_node hit_rec = NULL, light_hit_rec = NULL; 353 | sphere_node hit_sphere = NULL, light_hit_sphere = NULL; 354 | double diffuse, specular; 355 | point3 l, _l, r, rr; 356 | object_fill fill; 357 | 358 | color reflection_part; 359 | color refraction_part; 360 | /* might be a reflection ray, so check how many times we've bounced */ 361 | if (bounces_left == 0) { 362 | SET_COLOR(object_color, 0.0, 0.0, 0.0); 363 | return 0; 364 | } 365 | 366 | /* check for intersection with a sphere or a rectangular */ 367 | intersection ip= ray_hit_object(e, d, t, MAX_DISTANCE, rectangulars, 368 | &hit_rec, spheres, &hit_sphere); 369 | if (!hit_rec && !hit_sphere) 370 | return 0; 371 | 372 | /* pick the fill of the object that was hit */ 373 | fill = hit_rec ? 374 | hit_rec->element.rectangular_fill : 375 | hit_sphere->element.sphere_fill; 376 | 377 | void *hit_obj = hit_rec ? (void *) hit_rec : (void *) hit_sphere; 378 | 379 | /* assume it is a shadow */ 380 | SET_COLOR(object_color, 0.0, 0.0, 0.0); 381 | 382 | for (light_node light = lights; light; light = light->next) { 383 | /* calculate the intersection vector pointing at the light */ 384 | subtract_vector(ip.point, light->element.position, l); 385 | multiply_vector(l, -1, _l); 386 | normalize(_l); 387 | /* check for intersection with an object. use ignore_me 388 | * because we don't care about this normal 389 | */ 390 | ray_hit_object(ip.point, _l, MIN_DISTANCE, length(l), 391 | rectangulars, &light_hit_rec, 392 | spheres, &light_hit_sphere); 393 | /* the light was not block by itself(lit object) */ 394 | if (light_hit_rec || light_hit_sphere) 395 | continue; 396 | 397 | compute_specular_diffuse(&diffuse, &specular, d, l, 398 | ip.normal, fill.phong_power); 399 | 400 | localColor(object_color, light->element.light_color, 401 | diffuse, specular, &fill); 402 | } 403 | 404 | reflection(r, d, ip.normal); 405 | double idx = idx_stack_top(stk).idx, idx_pass = fill.index_of_refraction; 406 | if (idx_stack_top(stk).obj == hit_obj) { 407 | idx_stack_pop(stk); 408 | idx_pass = idx_stack_top(stk).idx; 409 | } else { 410 | idx_stack_element e = { .obj = hit_obj, 411 | .idx = fill.index_of_refraction 412 | }; 413 | idx_stack_push(stk, e); 414 | } 415 | 416 | refraction(rr, d, ip.normal, idx, idx_pass); 417 | double R = (fill.T > 0.1) ? 418 | fresnel(d, rr, ip.normal, idx, idx_pass) : 419 | 1.0; 420 | 421 | /* totalColor = localColor + 422 | mix((1-fill.Kd) * fill.R * reflection, T * refraction, R) 423 | */ 424 | if (fill.R > 0) { 425 | /* if we hit something, add the color */ 426 | int old_top = stk->top; 427 | if (ray_color(ip.point, MIN_DISTANCE, r, stk, rectangulars, spheres, 428 | lights, reflection_part, 429 | bounces_left - 1)) { 430 | multiply_vector(reflection_part, R * (1.0 - fill.Kd) * fill.R, 431 | reflection_part); 432 | add_vector(object_color, reflection_part, 433 | object_color); 434 | } 435 | stk->top = old_top; 436 | } 437 | /* calculate refraction ray */ 438 | if ((length(rr) > 0.0) && (fill.T > 0.0) && 439 | (fill.index_of_refraction > 0.0)) { 440 | normalize(rr); 441 | if (ray_color(ip.point, MIN_DISTANCE, rr, stk,rectangulars, spheres, 442 | lights, refraction_part, 443 | bounces_left - 1)) { 444 | multiply_vector(refraction_part, (1 - R) * fill.T, 445 | refraction_part); 446 | add_vector(object_color, refraction_part, 447 | object_color); 448 | } 449 | } 450 | 451 | protect_color_overflow(object_color); 452 | return 1; 453 | } 454 | 455 | /* @param background_color this is not ambient light */ 456 | void raytracing(uint8_t *pixels, color background_color, 457 | rectangular_node rectangulars, sphere_node spheres, 458 | light_node lights, const viewpoint *view, 459 | int width, int height) 460 | { 461 | point3 u, v, w, d; 462 | color object_color = { 0.0, 0.0, 0.0 }; 463 | 464 | /* calculate u, v, w */ 465 | calculateBasisVectors(u, v, w, view); 466 | 467 | idx_stack stk; 468 | 469 | int factor = sqrt(SAMPLES); 470 | for (int j = 0; j < height; j++) { 471 | for (int i = 0; i < width; i++) { 472 | double r = 0, g = 0, b = 0; 473 | /* MSAA */ 474 | for (int s = 0; s < SAMPLES; s++) { 475 | idx_stack_init(&stk); 476 | rayConstruction(d, u, v, w, 477 | i * factor + s / factor, 478 | j * factor + s % factor, 479 | view, 480 | width * factor, height * factor); 481 | if (ray_color(view->vrp, 0.0, d, &stk, rectangulars, spheres, 482 | lights, object_color, 483 | MAX_REFLECTION_BOUNCES)) { 484 | r += object_color[0]; 485 | g += object_color[1]; 486 | b += object_color[2]; 487 | } else { 488 | r += background_color[0]; 489 | g += background_color[1]; 490 | b += background_color[2]; 491 | } 492 | pixels[((i + (j * width)) * 3) + 0] = r * 255 / SAMPLES; 493 | pixels[((i + (j * width)) * 3) + 1] = g * 255 / SAMPLES; 494 | pixels[((i + (j * width)) * 3) + 2] = b * 255 / SAMPLES; 495 | } 496 | } 497 | } 498 | } 499 | -------------------------------------------------------------------------------- /raytracing.h: -------------------------------------------------------------------------------- 1 | #ifndef __RAYTRACING_H 2 | #define __RAYTRACING_H 3 | 4 | #include "objects.h" 5 | #include 6 | 7 | void raytracing(uint8_t *pixels, color background_color, 8 | rectangular_node rectangulars, sphere_node spheres, 9 | light_node lights, const viewpoint *view, 10 | int width, int height); 11 | #endif 12 | -------------------------------------------------------------------------------- /scripts/install-git-hooks: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! test -d .git; then 4 | echo "Execute scripts/install-git-hooks in the top-level directory." 5 | exit 1 6 | fi 7 | 8 | ln -sf ../../scripts/pre-commit.hook .git/hooks/pre-commit || exit 1 9 | echo 10 | echo "Git commit hooks are installed successfully." 11 | -------------------------------------------------------------------------------- /scripts/pre-commit.hook: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ASTYLE_OPTS="--style=kr --indent=spaces=4" 4 | CPPCHECK_OPTS="-I. --error-exitcode=1 ." 5 | 6 | RETURN=0 7 | ASTYLE=$(which astyle) 8 | if [ $? -ne 0 ]; then 9 | echo "[!] astyle not installed. Unable to check source file format policy." >&2 10 | exit 1 11 | fi 12 | 13 | CPPCHECK=$(which cppcheck) 14 | if [ $? -ne 0 ]; then 15 | echo "[!] cppecheck not installed. Unable to perform static analysis." >&2 16 | exit 1 17 | fi 18 | 19 | DIFF=$(which colordiff) 20 | if [ $? -ne 0 ]; then 21 | DIFF=diff 22 | fi 23 | 24 | FILES=`git diff --cached --name-only --diff-filter=ACMR | grep -E "\.(c|cpp|h)$"` 25 | for FILE in $FILES; do 26 | nf=`git checkout-index --temp $FILE | cut -f 1` 27 | newfile=`mktemp /tmp/${nf}.XXXXXX` || exit 1 28 | $ASTYLE $ASTYLE_OPTS < $nf > $newfile 2>> /dev/null 29 | $DIFF -u -p -B "${nf}" "${newfile}" 30 | r=$? 31 | rm "${newfile}" 32 | rm "${nf}" 33 | if [ $r != 0 ] ; then 34 | echo "[!] $FILE does not follow the consistent coding style." >&2 35 | RETURN=1 36 | fi 37 | done 38 | 39 | if [ $RETURN -eq 1 ]; then 40 | echo "" >&2 41 | echo "Make sure you have run astyle as the following:" >&2 42 | echo " astyle $ASTYLE_OPTS --suffix=none $FILE" >&2 43 | echo 44 | fi 45 | 46 | # static analysis 47 | $CPPCHECK $CPPCHECK_OPTS >/dev/null 48 | if [ $? -ne 0 ]; then 49 | RETURN=1 50 | echo "" >&2 51 | echo "Fail to pass static analysis." >&2 52 | echo 53 | fi 54 | 55 | exit $RETURN 56 | --------------------------------------------------------------------------------