├── .gitignore ├── README.md ├── benchmark.c ├── demo ├── demo_gl.c ├── makefile ├── models │ ├── README │ ├── airboat.obj │ ├── al.obj │ ├── ateneal.obj │ ├── ateneav.obj │ ├── bench.obj │ ├── car.obj │ ├── cessna.obj │ ├── chair.obj │ ├── cow.obj │ ├── elephal.obj │ ├── ladybird.obj │ ├── lamp.obj │ ├── man.obj │ ├── minicooper.obj │ ├── new_csie_b1.obj │ ├── skybox │ │ ├── negx.png │ │ ├── negy.png │ │ ├── negz.png │ │ ├── posx.png │ │ ├── posy.png │ │ ├── posz.png │ │ └── readme.txt │ ├── skyscraper.obj │ ├── teapot.obj │ ├── teddy.obj │ ├── tetrahedron.obj │ ├── venusl.obj │ ├── venusv.obj │ └── wall.png ├── scene.c └── scene.h ├── example.c ├── img ├── demo_scenes.jpg └── example.png ├── makefile └── render ├── include ├── canvas.h ├── color.h ├── kdtree.h ├── obj_loader.h ├── queue.h ├── render.h └── utils.h ├── makefile └── src ├── canvas.c ├── fog.c ├── kdtree.c ├── obj_loader.c ├── render.c ├── scene.c ├── sphere.c ├── tracer.c └── triangle.c /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOS files 2 | .DS_Store 3 | 4 | # Object files 5 | *.o 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | raytracing-render 2 | ================= 3 | 4 | Developing [ray tracing](http://en.wikipedia.org/wiki/Ray_tracing_%28graphics%29) render from scratch. 5 | 6 | 7 | ![Demo scenes](https://raw.github.com/lagodiuk/raytracing-render/master/img/demo_scenes.jpg) 8 | 9 | ### Key Features ### 10 | * Using [k-d tree](http://en.wikipedia.org/wiki/K-d_tree) for fast traversal of 3D scene objects 11 | * Using [Surface Area Heuristic](http://stackoverflow.com/a/4633332/653511) for building optimal k-d tree 12 | * Rendering entire scene in parallel (using [OpenMP](http://en.wikipedia.org/wiki/OpenMP)) 13 | * Texture mapping (using [libpng](http://en.wikipedia.org/wiki/Libpng)) 14 | * Saving rendered image to file (using [libpng](http://en.wikipedia.org/wiki/Libpng)) 15 | * Loading 3D models from [*.obj format](http://en.wikipedia.org/wiki/Wavefront_.obj_file) 16 | * [Phong shading](http://en.wikipedia.org/wiki/Phong_shading) 17 | * Antialiasing: throwing 4 rays per each pixel, which belongs to edge (using [Sobel operator](http://en.wikipedia.org/wiki/Sobel_operator) to detect edges) 18 | * [Phong reflection model](http://en.wikipedia.org/wiki/Phong_reflection_model) 19 | * Two types of primitives: triangle and sphere 20 | * Reflections, shadows, fog effect, multiple light sources 21 | 22 | ### Requirements ### 23 | Requires [libpng](http://www.libpng.org/pub/png/) to be installed.
24 | Tested on Mac OS 10.8 with gcc 4.2, gcc 4.7, gcc 4.9 and gcc 5 (as far as OpenMP is required - currently, Clang can't be 25 | used). 26 | 27 | ### Demo with GLUT front-end ### 28 | All rendering routines are performing by this render, not OpenGL. 29 | Just using GLUT to display rendered image. 30 | ```bash 31 | make run_demo_gl 32 | ``` 33 | or something like this (in case if Homebrew installed GCC 5 - under its own alias): 34 | ```bash 35 | make CC=gcc-5 run_demo_gl 36 | ``` 37 | * Use controls ← ↑ → ↓ to rotate camera 38 | * Use CTRL + ↑ or CTRL + ↓ to move camera forward or backward 39 | * Use SHIFT + ↑ or SHIFT + ↓ to move camera up or down 40 | * Use SHIFT + ← or SHIFT + → to move camera left or right 41 | * Use ALT + ↑ or ALT + ↓ to change focus of camera 42 | * Use ESC to exit 43 | 44 | ### Create demo from scratch ### 45 | 46 | Lets create example application from scratch :) 47 | You will get following image: 48 | 49 | ![Sphere-triangle-and-cow](https://raw.github.com/lagodiuk/raytracing-render/master/img/example.png) 50 | 51 | ```c 52 | #include 53 | 54 | #include 55 | #include 56 | #include 57 | 58 | #define CANVAS_W 400 59 | #define CANVAS_H 400 60 | 61 | // Boost by rendering in parallel 62 | #define THREADS_NUM 4 63 | 64 | #define BACKGROUND_COLOR rgb(255, 255, 255) 65 | 66 | #define MAX_OBJECTS_NUMBER 10000 67 | #define MAX_LIGHT_SOURCES_NUMBER 5 68 | 69 | int 70 | main(void) { 71 | // Allocating scene 72 | Scene * scene = new_scene(MAX_OBJECTS_NUMBER, 73 | MAX_LIGHT_SOURCES_NUMBER, 74 | BACKGROUND_COLOR); 75 | 76 | // Allocating new sphere 77 | Float radius = 100; 78 | Point3d center = point3d(0, 0, 0); 79 | Color sphere_color = rgb(250, 30, 30); 80 | Material sphere_material = material(1, 5, 5, 10, 0, 10); 81 | Object3d * sphere = new_sphere(center, 82 | radius, 83 | sphere_color, 84 | sphere_material); 85 | 86 | // Adding sphere to the scene 87 | add_object(scene, 88 | sphere); 89 | 90 | // Allocating new triangle 91 | Object3d * triangle = new_triangle(point3d(-700, -700, -130), // vertex 1 92 | point3d( 700, -700, -130), // vertex 2 93 | point3d( 0, 400, -130), // vertex 3 94 | rgb(100, 255, 30), // color 95 | material(1, 6, 0, 2, 0, 0) // surface params 96 | ); 97 | 98 | // Adding triangle to the scene 99 | add_object(scene, 100 | triangle); 101 | 102 | // Loading 3D model of cow from *.obj file 103 | // defining transformations and parameters of 3D model 104 | // TODO: must be refactored... 105 | SceneFaceHandlerParams load_params = 106 | new_scene_face_handler_params(scene, 107 | // scale: 108 | 40, 109 | // move dx, dy, dz: 110 | -150, -100, 30, 111 | // rotate around axises x, y, z: 112 | 0, 0, 0, 113 | // color 114 | rgb(200, 200, 50), 115 | // surface params 116 | material(2, 3, 0, 0, 0, 0) 117 | ); 118 | 119 | load_obj("./demo/models/cow.obj", 120 | // default handler which adding polygons of 3D model to scene: 121 | scene_face_handler, 122 | &load_params); 123 | 124 | // This function is requried (bulding k-d tree of entire scene) 125 | prepare_scene(scene); 126 | 127 | printf("\nNumber of polygons: %i\n", scene->last_object_index + 1); 128 | 129 | // Allocating new light source 130 | Color light_source_color = rgb(255, 255, 255); 131 | Point3d light_source_location = point3d(-300, 300, 300); 132 | LightSource3d * light_source = new_light_source(light_source_location, 133 | light_source_color); 134 | // Adding light source to the scene 135 | add_light_source(scene, 136 | light_source); 137 | 138 | // Adding fog 139 | Float density = 0.002; 140 | set_exponential_fog(scene, density); 141 | 142 | // Allocating camera 143 | // TODO: It's a pity, but quaternions are not implemented yet :( 144 | Point3d camera_location = point3d(0, 500, 0); 145 | Float focus = 320; 146 | Float x_angle = -1.57; 147 | Float y_angle = 0; 148 | Float z_angle = 3.14; 149 | Camera * camera = new_camera(camera_location, 150 | x_angle, 151 | y_angle, 152 | z_angle, 153 | focus); 154 | 155 | // Rotate camera if needed 156 | // rotate_camera(camera, d_x_angle, d_y_angle, d_z_angle); 157 | 158 | // Move camera if needed 159 | // move_camera(camera, vector3df(d_x, d_y, d_z)); 160 | 161 | // Alocate new canvas, to render scene on it 162 | Canvas * canvas = new_canvas(CANVAS_W, 163 | CANVAS_H); 164 | 165 | render_scene(scene, 166 | camera, 167 | canvas, 168 | THREADS_NUM); 169 | 170 | // Saving rendered image in PNG format 171 | write_png("example.png", 172 | canvas); 173 | 174 | release_canvas(canvas); 175 | release_scene(scene); 176 | release_camera(camera); 177 | 178 | return 0; 179 | } 180 | ``` 181 | Launch it 182 | ```bash 183 | make example && ./example 184 | ``` 185 | 186 | ### Average number of intersections per pixel ### 187 | Define different values of maximal depth of Kd-tree and track average number of ray intersections per pixel: 188 | ```bash 189 | make DEF="-DRAY_INTERSECTIONS_STAT -DMAX_TREE_DEPTH=25" run_demo_gl 190 | ``` 191 | ```bash 192 | make DEF="-DRAY_INTERSECTIONS_STAT -DMAX_TREE_DEPTH=25" example && ./example 193 | ``` 194 | 195 | ### Benchamrks ### 196 | Illustration of kd-tree boosting: 197 | 198 | 1) Without kd-tree, and without bounding box: 199 | ```bash 200 | make clean > /dev/null 201 | make DEF="-DRAY_INTERSECTIONS_STAT -DNO_BOUNDING_BOX -DMAX_TREE_DEPTH=0" benchmark > /dev/null && 202 | ./benchmark 203 | make clean > /dev/null 204 | ``` 205 | 206 | 2) Without kd-tree, but with bounding box: 207 | ```bash 208 | make clean > /dev/null 209 | make DEF="-DRAY_INTERSECTIONS_STAT -DMAX_TREE_DEPTH=0" benchmark > /dev/null && 210 | ./benchmark 211 | make clean > /dev/null 212 | ``` 213 | 214 | 3) With kd-tree, but without bounding box: 215 | ```bash 216 | make clean > /dev/null 217 | make DEF="-DRAY_INTERSECTIONS_STAT -DNO_BOUNDING_BOX -DMAX_TREE_DEPTH=20" benchmark > /dev/null && 218 | ./benchmark 219 | make clean > /dev/null 220 | ``` 221 | 222 | 4) With kd-tree, and with bounding box: 223 | ```bash 224 | make clean > /dev/null 225 | make DEF="-DRAY_INTERSECTIONS_STAT -DMAX_TREE_DEPTH=20" benchmark > /dev/null && 226 | ./benchmark 227 | make clean > /dev/null 228 | ``` 229 | -------------------------------------------------------------------------------- /benchmark.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define CANVAS_W 400 10 | #define CANVAS_H 400 11 | 12 | #define THREADS_NUM 1 13 | 14 | #define BACKGROUND_COLOR rgb(255, 255, 255) 15 | 16 | #define MAX_OBJECTS_NUMBER 10000 17 | 18 | #define MAX_LIGHT_SOURCES_NUMBER 5 19 | 20 | #define MAX_SERPINSKY_PYRAMID_LEVEL 6 21 | 22 | Camera * 23 | create_camera(void); 24 | 25 | void 26 | create_serpinsky_pyramid(Scene * scene, 27 | int level); 28 | 29 | void add_serpinsky_pyramid(Scene * scene, 30 | int depth, 31 | Point3d p1, 32 | Point3d p2, 33 | Point3d p3, 34 | Point3d p4, 35 | Material material, 36 | Color color); 37 | 38 | void 39 | add_pyramid(Scene * scene, 40 | Point3d p1, 41 | Point3d p2, 42 | Point3d p3, 43 | Point3d p4, 44 | Material material, 45 | Color color); 46 | 47 | void 48 | generate_random_spheres(Scene * scene, 49 | int count); 50 | 51 | int 52 | main(void) { 53 | Camera * camera = create_camera(); 54 | 55 | Canvas * canvas = new_canvas(CANVAS_W, 56 | CANVAS_H); 57 | 58 | int i; 59 | for(i = 0; i < MAX_SERPINSKY_PYRAMID_LEVEL; i++) { 60 | Scene * scene = new_scene(MAX_OBJECTS_NUMBER, 61 | MAX_LIGHT_SOURCES_NUMBER, 62 | BACKGROUND_COLOR); 63 | 64 | create_serpinsky_pyramid(scene, i); 65 | 66 | // Randomness causes unreproducible results 67 | // But in general - results are similar to Serpinsky pyramid 68 | //generate_random_spheres(scene, i * 400); 69 | 70 | prepare_scene(scene); 71 | 72 | printf("Number of polygons: %i. ", scene->last_object_index + 1); 73 | 74 | render_scene(scene, 75 | camera, 76 | canvas, 77 | THREADS_NUM); 78 | 79 | release_scene(scene); 80 | } 81 | 82 | release_canvas(canvas); 83 | release_camera(camera); 84 | 85 | return 0; 86 | } 87 | 88 | Camera * 89 | create_camera(void) { 90 | Point3d camera_location = point3d(-70, 200, 50); 91 | Float focus = 320; 92 | Float x_angle = -1.57; 93 | Float y_angle = 0; 94 | Float z_angle = 3.14; 95 | Camera * camera = new_camera(camera_location, 96 | x_angle, 97 | y_angle, 98 | z_angle, 99 | focus); 100 | return camera; 101 | } 102 | 103 | void 104 | create_serpinsky_pyramid(Scene * scene, 105 | int level) { 106 | Float pyramid_edge = 200; 107 | Float dx = -100; 108 | Float dy = -40; 109 | 110 | add_serpinsky_pyramid(scene, 111 | level, 112 | point3d(-pyramid_edge/2 + dx, -pyramid_edge * 0.87 / 2 + dy, 0), 113 | point3d(pyramid_edge/2 + dx, -pyramid_edge * 0.87 / 2 + dy, 0), 114 | point3d(dx, pyramid_edge * 0.87 / 2 + dy, 0), 115 | point3d(dx, dy, pyramid_edge * 0.87), 116 | material(1, 5, 0, 0, 0, 0), rgb(240, 210, 40)); 117 | } 118 | 119 | void add_serpinsky_pyramid(Scene * scene, 120 | int depth, 121 | Point3d p1, 122 | Point3d p2, 123 | Point3d p3, 124 | Point3d p4, 125 | Material material, 126 | Color color) { 127 | if(depth) { 128 | Point3d p1p2 = point3d((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2); 129 | Point3d p1p3 = point3d((p1.x + p3.x) / 2, (p1.y + p3.y) / 2, (p1.z + p3.z) / 2); 130 | Point3d p1p4 = point3d((p1.x + p4.x) / 2, (p1.y + p4.y) / 2, (p1.z + p4.z) / 2); 131 | Point3d p2p3 = point3d((p2.x + p3.x) / 2, (p2.y + p3.y) / 2, (p2.z + p3.z) / 2); 132 | Point3d p2p4 = point3d((p2.x + p4.x) / 2, (p2.y + p4.y) / 2, (p2.z + p4.z) / 2); 133 | Point3d p3p4 = point3d((p3.x + p4.x) / 2, (p3.y + p4.y) / 2, (p3.z + p4.z) / 2); 134 | 135 | add_serpinsky_pyramid(scene, depth - 1, p1, p1p2, p1p3, p1p4, material, color); 136 | add_serpinsky_pyramid(scene, depth - 1, p2, p1p2, p2p3, p2p4, material, color); 137 | add_serpinsky_pyramid(scene, depth - 1, p3, p1p3, p2p3, p3p4, material, color); 138 | add_serpinsky_pyramid(scene, depth - 1, p4, p1p4, p2p4, p3p4, material, color); 139 | } else { 140 | add_pyramid(scene, p1, p2, p3, p4, material, color); 141 | } 142 | } 143 | 144 | void 145 | add_pyramid(Scene * scene, 146 | Point3d p1, 147 | Point3d p2, 148 | Point3d p3, 149 | Point3d p4, 150 | Material material, 151 | Color color) { 152 | add_object(scene, new_triangle(p1, p2, p3, color, material)); 153 | add_object(scene, new_triangle(p1, p2, p4, color, material)); 154 | add_object(scene, new_triangle(p2, p3, p4, color, material)); 155 | add_object(scene, new_triangle(p3, p1, p4, color, material)); 156 | } 157 | 158 | void 159 | generate_random_spheres(Scene * scene, 160 | int count) { 161 | srand(time(NULL)); 162 | int i; 163 | for(i = 0; i < count; i++) { 164 | int x = (rand() % 200) - (rand() % 200); 165 | int y = (rand() % 200) - (rand() % 200); 166 | int z = (rand() % 200) - (rand() % 200); 167 | int r = (rand() % 40); 168 | add_object(scene, new_sphere(point3d(x, y, z), r, rgb(255, 0, 0), material(1, 5, 0, 0, 0, 0))); 169 | } 170 | } -------------------------------------------------------------------------------- /demo/demo_gl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This demo use GLUT as front-end to ray tracing render: 4 | * - displaying rendered scene in window 5 | * - handling keyboard controls for moving and rotating the camera 6 | * 7 | * TODO: maybe need some refactoring, as I am totally newbie to OpenGL and GLUT 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // Mac OS X 17 | #ifdef DARWIN 18 | #include 19 | #include 20 | #endif 21 | // The Rest of the World 22 | #ifdef POSIX 23 | #include 24 | #include 25 | #endif 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | /* collapse is a feature from OpenMP 3 (2008) */ 32 | #if _OPENMP < 200805 33 | #define collapse(x) 34 | #endif 35 | #define CHUNK 50 36 | 37 | #include "scene.h" 38 | 39 | #define DX 10 40 | #define DY 10 41 | #define DZ 10 42 | #define D_FOCUS 5 43 | #define D_ANGLE 0.05 44 | 45 | #define TEX_WIDTH 256 46 | #define TEX_HEIGHT 256 47 | 48 | Scene * scene = NULL; 49 | Camera * camera = NULL; 50 | Canvas * canv = NULL; 51 | 52 | Boolean camera_state_changed = False; 53 | 54 | int threads_num = 0; 55 | 56 | typedef 57 | struct { 58 | uint8_t r; 59 | uint8_t g; 60 | uint8_t b; 61 | } 62 | pixel_t; 63 | 64 | GLint win_width = 512; 65 | GLint win_height = 512; 66 | 67 | GLuint tex; 68 | pixel_t canvas[TEX_WIDTH][TEX_HEIGHT]; 69 | 70 | void 71 | processControls(int key, 72 | int x, 73 | int y); 74 | 75 | void 76 | processExit(unsigned char key, 77 | int x, 78 | int y); 79 | 80 | void 81 | init_scene_and_camera(void); 82 | 83 | void 84 | glut_routines(void); 85 | 86 | void 87 | display(void); 88 | 89 | void 90 | animate(void); 91 | 92 | void 93 | render_seq(void); 94 | 95 | int 96 | main(int argc, 97 | char *argv[]) { 98 | 99 | init_scene_and_camera(); 100 | threads_num = (argc > 1) ? atoi(argv[1]) : 1; 101 | 102 | glutInit(&argc, argv); 103 | glut_routines(); 104 | glutMainLoop(); 105 | 106 | return 0; 107 | } 108 | 109 | void 110 | init_scene_and_camera(void) { 111 | 112 | scene = makeScene(); 113 | 114 | Float focus = 200; 115 | Float x_angle = -M_PI / 2; 116 | Float y_angle = 0; 117 | Float z_angle = M_PI; 118 | 119 | camera = new_camera(point3d(-80, 250, 50), 120 | x_angle, 121 | y_angle, 122 | z_angle, 123 | focus); 124 | 125 | camera_state_changed = True; 126 | 127 | canv = new_canvas(TEX_WIDTH, 128 | TEX_HEIGHT); 129 | } 130 | 131 | void 132 | glut_routines(void) { 133 | glutInitWindowSize(win_width, win_height); 134 | glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); 135 | 136 | glutCreateWindow("Raytracing"); 137 | 138 | glutKeyboardFunc(processExit); 139 | glutSpecialFunc(processControls); 140 | 141 | glClearColor(0.0, 1.0, 0.0, 1.0); 142 | glViewport(0, 0, win_width, win_height); 143 | glGenTextures(1, &tex); 144 | glBindTexture(GL_TEXTURE_2D, tex); 145 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 146 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 147 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 148 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 149 | 150 | glutDisplayFunc(display); 151 | glutIdleFunc(animate); 152 | 153 | render_seq(); 154 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_WIDTH, TEX_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, canvas); 155 | } 156 | 157 | void 158 | animate(void) { 159 | 160 | render_seq(); 161 | 162 | glEnable(GL_TEXTURE_2D); 163 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, canvas); 164 | glDisable(GL_TEXTURE_2D); 165 | glutPostRedisplay(); 166 | } 167 | 168 | void 169 | render_seq(void) { 170 | 171 | if(camera_state_changed) { 172 | render_scene(scene, 173 | camera, 174 | canv, 175 | threads_num); 176 | 177 | pixel_t px; 178 | GLint i; 179 | GLint j; 180 | Color c; 181 | 182 | /* Copying rendered image from Canvas * canv to pixel_t canvas[TEX_WIDTH][TEX_HEIGHT]*/ 183 | // TODO: memcpy entire arrays 184 | omp_set_num_threads(threads_num); 185 | #pragma omp parallel private(i, j, px, c) 186 | #pragma omp for collapse(2) schedule(dynamic, CHUNK) 187 | for (j = 0; j < TEX_HEIGHT; ++j) { 188 | for (i = 0; i < TEX_WIDTH; ++i) { 189 | c = get_pixel(i, j, canv); 190 | memcpy(&px, &c, sizeof(pixel_t)); 191 | canvas[j][i] = px; 192 | } 193 | } 194 | 195 | camera_state_changed = False; 196 | } 197 | } 198 | 199 | void 200 | display(void) { 201 | 202 | glClear(GL_COLOR_BUFFER_BIT); 203 | glLoadIdentity(); 204 | 205 | glEnable(GL_TEXTURE_2D); 206 | glBegin(GL_QUADS); 207 | glTexCoord2f(0.0, 0.0); 208 | glVertex2f( 1.0, 1.0); 209 | glTexCoord2f(1.0, 0.0); 210 | glVertex2f(-1.0, 1.0); 211 | glTexCoord2f(1.0, 1.0); 212 | glVertex2f(-1.0, -1.0); 213 | glTexCoord2f(0.0, 1.0); 214 | glVertex2f( 1.0, -1.0); 215 | glEnd(); 216 | glDisable(GL_TEXTURE_2D); 217 | 218 | glFlush(); 219 | } 220 | 221 | void 222 | processControls(int key, 223 | int x, 224 | int y) { 225 | 226 | int modifiers = glutGetModifiers(); 227 | switch(key) { 228 | 229 | case GLUT_KEY_UP : 230 | switch(modifiers) { 231 | case GLUT_ACTIVE_CTRL : 232 | move_camera(camera, 233 | vector3df(0, 0, DZ)); 234 | break; 235 | case GLUT_ACTIVE_ALT : 236 | camera->proj_plane_dist += D_FOCUS; 237 | break; 238 | case GLUT_ACTIVE_SHIFT : 239 | move_camera(camera, 240 | vector3df(0, -DY, 0)); 241 | break; 242 | default : 243 | rotate_camera(camera, 244 | D_ANGLE, 0, 0); 245 | break; 246 | } 247 | camera_state_changed = True; 248 | break; 249 | 250 | case GLUT_KEY_DOWN : 251 | switch(modifiers) { 252 | case GLUT_ACTIVE_CTRL : 253 | move_camera(camera, 254 | vector3df(0, 0, -DZ)); 255 | break; 256 | case GLUT_ACTIVE_ALT : 257 | camera->proj_plane_dist -= D_FOCUS; 258 | break; 259 | case GLUT_ACTIVE_SHIFT : 260 | move_camera(camera, 261 | vector3df(0, DY, 0)); 262 | break; 263 | default : 264 | rotate_camera(camera, 265 | -D_ANGLE, 0, 0); 266 | break; 267 | } 268 | camera_state_changed = True; 269 | break; 270 | 271 | case GLUT_KEY_LEFT : 272 | switch(modifiers) { 273 | case GLUT_ACTIVE_SHIFT : 274 | move_camera(camera, 275 | vector3df(DX, 0, 0)); 276 | break; 277 | case GLUT_ACTIVE_ALT : 278 | rotate_camera(camera, 279 | 0, -D_ANGLE, 0); 280 | break; 281 | default : 282 | rotate_camera(camera, 283 | 0, 0, -D_ANGLE); 284 | break; 285 | } 286 | camera_state_changed = True; 287 | break; 288 | 289 | case GLUT_KEY_RIGHT : 290 | switch(modifiers) { 291 | case GLUT_ACTIVE_SHIFT : 292 | move_camera(camera, 293 | vector3df(-DX, 0, 0)); 294 | break; 295 | case GLUT_ACTIVE_ALT : 296 | rotate_camera(camera, 297 | 0, D_ANGLE, 0); 298 | break; 299 | default : 300 | rotate_camera(camera, 301 | 0, 0, D_ANGLE); 302 | break; 303 | } 304 | camera_state_changed = True; 305 | break; 306 | } 307 | } 308 | 309 | void 310 | processExit(unsigned char key, 311 | int x, 312 | int y) { 313 | 314 | if (key == 27) { 315 | exit(0); 316 | } 317 | } -------------------------------------------------------------------------------- /demo/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | LIBPATH = -L../render/lib 4 | INCLUDES = -I../render/include 5 | LIBS = -lrender -lm -lpng -fopenmp 6 | 7 | render = ../render/lib/librender.a 8 | 9 | CC_OPTS = -Wall 10 | CC_OPTS += -std=gnu89 -O2 11 | 12 | ifeq ($(shell uname), Darwin) 13 | OPEN_GL_OPTS = -framework GLUT -framework OpenGL -DDARWIN 14 | # for libpng 15 | LIBPATH += -L/usr/X11/lib 16 | INCLUDES += -I/usr/X11/include 17 | else 18 | OPEN_GL_OPTS = -lGL -lglut -DPOSIX 19 | endif 20 | 21 | THREADS_NUM = 4 22 | 23 | run_demo_gl: demo_gl 24 | ./$< $(THREADS_NUM) 25 | 26 | demo_gl: $(render) scene.o scene.h demo_gl.c 27 | $(CC) $(CC_OPTS) -fopenmp demo_gl.c scene.o $(OPEN_GL_OPTS) $(LIBPATH) $(INCLUDES) $(LIBS) -o $@ 28 | 29 | scene.o: scene.c 30 | $(CC) $(INCLUDES) $(CC_OPTS) -c $< -o $@ 31 | 32 | $(render): 33 | (cd ../render && make DEF="$(DEF)" render) 34 | 35 | .PHONY: clean 36 | clean: 37 | (cd ../render && make clean) && \ 38 | rm -f *.o; \ 39 | rm -f ./demo_gl; 40 | -------------------------------------------------------------------------------- /demo/models/README: -------------------------------------------------------------------------------- 1 | These models and textures are used just for demonstration of render capabilities. 2 | 3 | All of these 3D models are not made by me, and been downloaded from variety of free sources. 4 | 5 | The following list describes models, and web sources, from which I have downloaded them. 6 | 7 | 1) 8 | Source: 9 | http://people.sc.fsu.edu/~jburkardt/data/obj/obj.html 10 | 11 | Models: 12 | airboat.obj 13 | al.obj 14 | cessna.obj 15 | lamp.obj 16 | skyscraper.obj 17 | teapot.obj 18 | minicooper.obj 19 | tetrahedron.obj 20 | 21 | 22 | 2) 23 | Source: 24 | http://groups.csail.mit.edu/graphics/classes/6.837/F03/models/ 25 | 26 | Models: 27 | teddy.obj 28 | cow.obj 29 | 30 | 31 | 3) 32 | Source: 33 | http://graphics.im.ntu.edu.tw/~robin/courses/cg03/model/ 34 | 35 | Models: 36 | ateneal.obj 37 | ateneav.obj 38 | elephal.obj 39 | venusl.obj 40 | venusv.obj 41 | new_csie_b1.obj 42 | 43 | 44 | 4) 45 | Source: 46 | http://o2c3ds.ru/3d_modeli/332-kachestvennye-3d-modeli-klassicheskih-kresel.html 47 | 48 | Models: 49 | chair.obj 50 | 51 | 52 | 5) 53 | Source: 54 | http://www.oyonale.com/modeles.php?lang=en&format=OBJ 55 | 56 | Models: 57 | ladybird.obj 58 | 59 | 60 | 6) 61 | Source: 62 | http://o2c3ds.ru/3d_modeli/315-3d-modeli-lyudi-v-dvizhenii-axyz.html 63 | 64 | Models: 65 | man.obj 66 | 67 | 68 | 7) 69 | car.obj - I have found somewhere in the web, but I have not stored the url of actual site. I am sorry. 70 | 71 | 72 | 8) 73 | Skybox textures are from this wonderful site: http://www.humus.name/index.php?page=Textures&&start=0 74 | 75 | 76 | 77 | If you have copyrights for any of these models or textures - please, contact me, and I will remove them by request. -------------------------------------------------------------------------------- /demo/models/skybox/negx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/skybox/negx.png -------------------------------------------------------------------------------- /demo/models/skybox/negy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/skybox/negy.png -------------------------------------------------------------------------------- /demo/models/skybox/negz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/skybox/negz.png -------------------------------------------------------------------------------- /demo/models/skybox/posx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/skybox/posx.png -------------------------------------------------------------------------------- /demo/models/skybox/posy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/skybox/posy.png -------------------------------------------------------------------------------- /demo/models/skybox/posz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/skybox/posz.png -------------------------------------------------------------------------------- /demo/models/skybox/readme.txt: -------------------------------------------------------------------------------- 1 | Author 2 | ====== 3 | 4 | This is the work of Emil Persson, aka Humus. 5 | http://www.humus.name 6 | 7 | 8 | 9 | License 10 | ======= 11 | 12 | This work is licensed under a Creative Commons Attribution 3.0 Unported License. 13 | http://creativecommons.org/licenses/by/3.0/ 14 | -------------------------------------------------------------------------------- /demo/models/tetrahedron.obj: -------------------------------------------------------------------------------- 1 | # tetrahedron.obj created by hand. 2 | # 3 | 4 | g tetrahedron 5 | 6 | v 1.00 1.00 1.00 7 | v 2.00 1.00 1.00 8 | v 1.00 2.00 1.00 9 | v 1.00 1.00 2.00 10 | 11 | f 1 3 2 12 | f 1 4 3 13 | f 1 2 4 14 | f 2 3 4 -------------------------------------------------------------------------------- /demo/models/wall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/demo/models/wall.png -------------------------------------------------------------------------------- /demo/scene.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "scene.h" 8 | 9 | #define BACKGROUND_COLOR rgb(255, 255, 255) 10 | 11 | #define MAX_POLYGONS_NUMBER 150000 12 | 13 | #define MAX_LIGHT_SOURCES_NUMBER 5 14 | 15 | #define SERPINSKY_PYRAMID_LEVEL 5 16 | 17 | void add_cube(Scene * scene, 18 | Point3d base, 19 | Float a, 20 | Material material); 21 | 22 | void add_serpinsky_pyramid(Scene * scene, 23 | int depth, 24 | Point3d p1, 25 | Point3d p2, 26 | Point3d p3, 27 | Point3d p4, 28 | Material material, 29 | Color color); 30 | 31 | void add_pyramid(Scene * scene, 32 | Point3d p1, 33 | Point3d p2, 34 | Point3d p3, 35 | Point3d p4, 36 | Material material, 37 | Color color); 38 | 39 | void create_serpinsky_pyramid(Scene * scene); 40 | 41 | void create_cube(Scene * scene); 42 | 43 | void create_sphere(Scene * scene); 44 | 45 | void create_floor_with_texture(Scene * scene); 46 | 47 | void create_floor_without_texture(Scene * scene); 48 | 49 | void load_lamp(Scene * scene); 50 | 51 | void load_teapot(Scene * scene); 52 | 53 | void load_man(Scene * scene); 54 | 55 | void load_atenea(Scene * scene); 56 | 57 | void load_venus(Scene * scene); 58 | 59 | void load_elephant(Scene * scene); 60 | 61 | void load_car(Scene * scene); 62 | 63 | void load_minicooper(Scene * scene); 64 | 65 | void 66 | add_skybox(Scene * scene, 67 | Point3d base, 68 | Float a); 69 | 70 | Scene * makeScene(void) { 71 | Scene * scene = new_scene(MAX_POLYGONS_NUMBER, MAX_LIGHT_SOURCES_NUMBER, BACKGROUND_COLOR); 72 | 73 | add_light_source(scene, new_light_source(point3d(-300, 300, 300), rgb(255, 255, 255))); 74 | 75 | //set_exponential_fog(scene, 0.001); 76 | 77 | //create_cube(scene); 78 | 79 | //create_serpinsky_pyramid(scene); 80 | 81 | create_floor_with_texture(scene); 82 | 83 | //create_floor_without_texture(scene); 84 | 85 | create_sphere(scene); 86 | 87 | //load_lamp(scene); 88 | 89 | //load_teapot(scene); 90 | 91 | //load_man(scene); 92 | 93 | // 10k triangles 94 | load_atenea(scene); 95 | 96 | // 11k triangles 97 | load_venus(scene); 98 | 99 | // 10k triangles 100 | load_elephant(scene); 101 | 102 | // 97k triangles! 103 | //load_car(scene); 104 | 105 | // 79k triangles! 106 | //load_minicooper(scene); 107 | 108 | //add_skybox(scene, point3d(-2000, -2000, -2000), 4000); 109 | 110 | printf("\nNumber of polygons: %i\n", scene->last_object_index + 1); 111 | printf("\nBuilding Kd-Tree. Wait, please...\n"); 112 | prepare_scene(scene); 113 | printf("\nScene created\n"); 114 | 115 | return scene; 116 | } 117 | 118 | void load_lamp(Scene * scene) { 119 | SceneFaceHandlerParams load_params = 120 | new_scene_face_handler_params(scene, 121 | 33, 30, -100, 30, 0, 0, 0, 122 | rgb(20, 250, 100), 123 | material(1, 3, 5, 0, 0, 10)); 124 | load_obj("./models/lamp.obj", 125 | scene_face_handler, 126 | &load_params); 127 | } 128 | 129 | void load_teapot(Scene * scene) { 130 | SceneFaceHandlerParams load_params = 131 | new_scene_face_handler_params(scene, 132 | 25, 100, 100, 32, 0, 0, 0, 133 | rgb(250, 200, 50), 134 | material(1, 3, 4, 7, 0, 10)); 135 | load_obj("./models/teapot.obj", 136 | scene_face_handler, 137 | &load_params); 138 | } 139 | 140 | void load_man(Scene * scene) { 141 | SceneFaceHandlerParams load_params = 142 | new_scene_face_handler_params(scene, 143 | 110, 100, -100, -80, 0, 0, 0, 144 | rgb(120, 120, 250), 145 | material(1, 5, 0, 0, 0, 10)); 146 | load_obj("./models/man.obj", 147 | scene_face_handler, 148 | &load_params); 149 | } 150 | 151 | void load_atenea(Scene * scene) { 152 | SceneFaceHandlerParams load_params = 153 | new_scene_face_handler_params(scene, 154 | 0.05, -100, -100, -30, 0, 0, 0, 155 | rgb(250, 200, 50), 156 | //reflective surface 157 | material(2, 3, 7, 3, 0, 10)); 158 | //material(4, 3, 7, 0, 0, 10)); 159 | load_obj("./models/ateneal.obj", 160 | scene_face_handler, 161 | &load_params); 162 | } 163 | 164 | void load_venus(Scene * scene) { 165 | SceneFaceHandlerParams load_params = 166 | new_scene_face_handler_params(scene, 167 | 0.05, 100, -100, -80, 0, 0, 1.3, 168 | rgb(200, 200, 150), 169 | material(2, 3, 0, 0, 0, 0)); 170 | load_obj("./models/venusl.obj", 171 | scene_face_handler, 172 | &load_params); 173 | } 174 | 175 | void load_elephant(Scene * scene) { 176 | SceneFaceHandlerParams load_params = 177 | new_scene_face_handler_params(scene, 178 | 0.3, -350, -150, -100, 0, 0, 0, 179 | rgb(50, 150, 250), 180 | material(2, 3, 0, 0, 0, 10)); 181 | load_obj("./models/elephal.obj", 182 | scene_face_handler, 183 | &load_params); 184 | } 185 | 186 | void load_car(Scene * scene) { 187 | SceneFaceHandlerParams load_params = 188 | new_scene_face_handler_params(scene, 189 | 3, 200, -100, -100, M_PI / 4, M_PI / 2, 0, 190 | rgb(190, 190, 220), 191 | material(3, 3, 7, 5, 0, 10)); 192 | load_obj("./models/car.obj", 193 | scene_face_handler, 194 | &load_params); 195 | } 196 | 197 | void load_minicooper(Scene * scene) { 198 | SceneFaceHandlerParams load_params = 199 | new_scene_face_handler_params(scene, 200 | 3, -100, -350, -100, 0, M_PI / 2, 0, 201 | rgb(220, 220, 220), 202 | material(2, 3, 7, 5, 0, 10)); 203 | load_obj("./models/minicooper.obj", 204 | scene_face_handler, 205 | &load_params); 206 | } 207 | 208 | void create_serpinsky_pyramid(Scene * scene) { 209 | Float pyramid_edge = 250; 210 | Float dx = 40; 211 | Float dy = 40; 212 | 213 | add_serpinsky_pyramid(scene, SERPINSKY_PYRAMID_LEVEL, 214 | point3d(-pyramid_edge/2 + dx, -pyramid_edge * 0.87 / 2 + dy, 0), 215 | point3d(pyramid_edge/2 + dx, -pyramid_edge * 0.87 / 2 + dy, 0), 216 | point3d(dx, pyramid_edge * 0.87 / 2 + dy, 0), 217 | point3d(dx, dy, pyramid_edge * 0.87), 218 | material(1, 5, 0, 0, 0, 0), rgb(240, 210, 40)); 219 | } 220 | 221 | void create_cube(Scene * scene) { 222 | add_cube(scene, point3d(60, 60, -60), 90, material(3, 7, 0, 0, 0, 0)); 223 | } 224 | 225 | void create_sphere(Scene * scene) { 226 | add_object(scene, new_sphere(point3d(130, 100, -30), 227 | 50.0, 228 | rgb(250, 30, 30), 229 | material(1, 5, 5, 10, 0, 10))); 230 | } 231 | 232 | void create_floor_with_texture(Scene * scene) { 233 | Canvas * tex = read_png("./models/wall.png"); 234 | 235 | add_object(scene, new_triangle_with_texture( 236 | point3d(-300, -300, -120), 237 | point3d(300, -300, -120), 238 | point3d(300, 300, -120), 239 | point2d(5, 0), 240 | point2d(0, 0), 241 | point2d(0, 5), 242 | tex, 243 | rgb(55, 255, 55), 244 | material(1, 6, 0, 2, 0, 0))); 245 | add_object(scene, new_triangle_with_texture( 246 | point3d(-300, -300, -120), 247 | point3d(-300, 300, -120), 248 | point3d(300, 300, -120), 249 | point2d(5, 0), 250 | point2d(5, 5), 251 | point2d(0, 5), 252 | tex, 253 | rgb(55, 255, 55), 254 | material(1, 6, 0, 2, 0, 0))); 255 | } 256 | 257 | void create_floor_without_texture(Scene * scene) { 258 | add_object(scene, new_triangle( 259 | point3d(-500, -500, -120), 260 | point3d(500, -500, -120), 261 | point3d(500, 500, -120), 262 | rgb(55, 200, 155), 263 | material(1, 6, 0, 2, 0, 0))); 264 | 265 | add_object(scene, new_triangle( 266 | point3d(-500, -500, -120), 267 | point3d(-500, 500, -120), 268 | point3d(500, 500, -120), 269 | rgb(55, 200, 155), 270 | material(1, 6, 0, 2, 0, 0))); 271 | } 272 | 273 | void add_cube(Scene * scene, Point3d base, Float a, Material material) { 274 | add_object(scene, new_triangle( 275 | point3d(base.x, base.y, base.z), 276 | point3d(base.x + a, base.y, base.z), 277 | point3d(base.x, base.y + a, base.z), 278 | rgb(255, 0, 0), 279 | material)); 280 | add_object(scene, new_triangle( 281 | point3d(base.x + a, base.y + a, base.z), 282 | point3d(base.x + a, base.y, base.z), 283 | point3d(base.x, base.y + a, base.z), 284 | rgb(255, 0, 0), 285 | material)); 286 | 287 | 288 | add_object(scene, new_triangle( 289 | point3d(base.x, base.y, base.z + a), 290 | point3d(base.x + a, base.y, base.z + a), 291 | point3d(base.x, base.y + a, base.z + a), 292 | rgb(0, 255, 0), 293 | material)); 294 | add_object(scene, new_triangle( 295 | point3d(base.x + a, base.y + a, base.z + a), 296 | point3d(base.x + a, base.y, base.z + a), 297 | point3d(base.x, base.y + a, base.z + a), 298 | rgb(0, 255, 0), 299 | material)); 300 | 301 | add_object(scene, new_triangle( 302 | point3d(base.x, base.y, base.z), 303 | point3d(base.x, base.y + a, base.z), 304 | point3d(base.x, base.y + a, base.z + a), 305 | rgb(0, 0, 255), 306 | material)); 307 | add_object(scene, new_triangle( 308 | point3d(base.x, base.y, base.z), 309 | point3d(base.x, base.y, base.z + a), 310 | point3d(base.x, base.y + a, base.z + a), 311 | rgb(0, 0, 255), 312 | material)); 313 | 314 | add_object(scene, new_triangle( 315 | point3d(base.x + a, base.y, base.z), 316 | point3d(base.x + a, base.y + a, base.z), 317 | point3d(base.x + a, base.y + a, base.z + a), 318 | rgb(255, 255, 0), 319 | material)); 320 | add_object(scene, new_triangle( 321 | point3d(base.x + a, base.y, base.z), 322 | point3d(base.x + a, base.y, base.z + a), 323 | point3d(base.x + a, base.y + a, base.z + a), 324 | rgb(255, 255, 0), 325 | material)); 326 | 327 | add_object(scene, new_triangle( 328 | point3d(base.x, base.y, base.z), 329 | point3d(base.x, base.y, base.z + a), 330 | point3d(base.x + a, base.y, base.z), 331 | rgb(255, 0, 255), 332 | material)); 333 | add_object(scene, new_triangle( 334 | point3d(base.x, base.y, base.z + a), 335 | point3d(base.x + a, base.y, base.z), 336 | point3d(base.x + a, base.y, base.z + a), 337 | rgb(255, 0, 255), 338 | material)); 339 | 340 | add_object(scene, new_triangle( 341 | point3d(base.x, base.y + a, base.z), 342 | point3d(base.x, base.y + a, base.z + a), 343 | point3d(base.x + a, base.y + a, base.z), 344 | rgb(0, 255, 255), 345 | material)); 346 | add_object(scene, new_triangle( 347 | point3d(base.x, base.y + a, base.z + a), 348 | point3d(base.x + a, base.y + a, base.z), 349 | point3d(base.x + a, base.y + a, base.z + a), 350 | rgb(0, 255, 255), 351 | material)); 352 | } 353 | 354 | void add_pyramid(Scene * scene, Point3d p1, Point3d p2, Point3d p3, Point3d p4, Material material, Color color) { 355 | add_object(scene, new_triangle(p1, p2, p3, color, material)); 356 | add_object(scene, new_triangle(p1, p2, p4, color, material)); 357 | add_object(scene, new_triangle(p2, p3, p4, color, material)); 358 | add_object(scene, new_triangle(p3, p1, p4, color, material)); 359 | } 360 | 361 | void add_serpinsky_pyramid(Scene * scene, int depth, 362 | Point3d p1, Point3d p2, Point3d p3, Point3d p4, 363 | Material material, Color color) { 364 | if(depth) { 365 | Point3d p1p2 = point3d((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2); 366 | Point3d p1p3 = point3d((p1.x + p3.x) / 2, (p1.y + p3.y) / 2, (p1.z + p3.z) / 2); 367 | Point3d p1p4 = point3d((p1.x + p4.x) / 2, (p1.y + p4.y) / 2, (p1.z + p4.z) / 2); 368 | Point3d p2p3 = point3d((p2.x + p3.x) / 2, (p2.y + p3.y) / 2, (p2.z + p3.z) / 2); 369 | Point3d p2p4 = point3d((p2.x + p4.x) / 2, (p2.y + p4.y) / 2, (p2.z + p4.z) / 2); 370 | Point3d p3p4 = point3d((p3.x + p4.x) / 2, (p3.y + p4.y) / 2, (p3.z + p4.z) / 2); 371 | 372 | add_serpinsky_pyramid(scene, depth - 1, p1, p1p2, p1p3, p1p4, material, color); 373 | add_serpinsky_pyramid(scene, depth - 1, p2, p1p2, p2p3, p2p4, material, color); 374 | add_serpinsky_pyramid(scene, depth - 1, p3, p1p3, p2p3, p3p4, material, color); 375 | add_serpinsky_pyramid(scene, depth - 1, p4, p1p4, p2p4, p3p4, material, color); 376 | } else { 377 | add_pyramid(scene, p1, p2, p3, p4, material, color); 378 | } 379 | } 380 | 381 | 382 | 383 | 384 | void 385 | add_skybox(Scene * scene, 386 | Point3d base, 387 | Float a) { 388 | 389 | Material m = material(1, 0, 0, 0, 0, 0); 390 | 391 | 392 | Canvas * negz = read_png("./models/skybox/negy.png"); 393 | add_object(scene, new_triangle_with_texture( 394 | point3d(base.x, base.y, base.z), 395 | point3d(base.x + a, base.y, base.z), 396 | point3d(base.x, base.y + a, base.z), 397 | point2d(0, 0), 398 | point2d(1, 0), 399 | point2d(0, 1), 400 | negz, 401 | rgb(255, 0, 0), 402 | m)); 403 | add_object(scene, new_triangle_with_texture( 404 | point3d(base.x + a, base.y + a, base.z), 405 | point3d(base.x + a, base.y, base.z), 406 | point3d(base.x, base.y + a, base.z), 407 | point2d(1, 1), 408 | point2d(1, 0), 409 | point2d(0, 1), 410 | negz, 411 | rgb(255, 0, 0), 412 | m)); 413 | 414 | 415 | Canvas * posz = read_png("./models/skybox/posy.png"); 416 | add_object(scene, new_triangle_with_texture( 417 | point3d(base.x, base.y, base.z + a), 418 | point3d(base.x + a, base.y, base.z + a), 419 | point3d(base.x, base.y + a, base.z + a), 420 | point2d(0, 0), 421 | point2d(1, 0), 422 | point2d(0, -1), 423 | posz, 424 | rgb(0, 255, 0), 425 | m)); 426 | add_object(scene, new_triangle_with_texture( 427 | point3d(base.x + a, base.y + a, base.z + a), 428 | point3d(base.x + a, base.y, base.z + a), 429 | point3d(base.x, base.y + a, base.z + a), 430 | point2d(1, -1), 431 | point2d(1, 0), 432 | point2d(0, -1), 433 | posz, 434 | rgb(0, 255, 0), 435 | m)); 436 | 437 | 438 | Canvas * negx = read_png("./models/skybox/negx.png"); 439 | add_object(scene, new_triangle_with_texture( 440 | point3d(base.x, base.y, base.z), 441 | point3d(base.x, base.y + a, base.z), 442 | point3d(base.x, base.y + a, base.z + a), 443 | point2d(0, 0), 444 | point2d(-1, 0), 445 | point2d(-1, -1), 446 | negx, 447 | rgb(0, 0, 255), 448 | m)); 449 | add_object(scene, new_triangle_with_texture( 450 | point3d(base.x, base.y, base.z), 451 | point3d(base.x, base.y, base.z + a), 452 | point3d(base.x, base.y + a, base.z + a), 453 | point2d(0, 0), 454 | point2d(0, -1), 455 | point2d(-1, -1), 456 | negx, 457 | rgb(0, 0, 255), 458 | m)); 459 | 460 | 461 | Canvas * posx = read_png("./models/skybox/posx.png"); 462 | add_object(scene, new_triangle_with_texture( 463 | point3d(base.x + a, base.y, base.z), 464 | point3d(base.x + a, base.y + a, base.z), 465 | point3d(base.x + a, base.y + a, base.z + a), 466 | point2d(0, 0), 467 | point2d(1, 0), 468 | point2d(1, -1), 469 | posx, 470 | rgb(255, 255, 0), 471 | m)); 472 | add_object(scene, new_triangle_with_texture( 473 | point3d(base.x + a, base.y, base.z), 474 | point3d(base.x + a, base.y, base.z + a), 475 | point3d(base.x + a, base.y + a, base.z + a), 476 | point2d(0, 0), 477 | point2d(0, -1), 478 | point2d(1, -1), 479 | posx, 480 | rgb(255, 255, 0), 481 | m)); 482 | 483 | 484 | Canvas * negy = read_png("./models/skybox/posz.png"); 485 | add_object(scene, new_triangle_with_texture( 486 | point3d(base.x, base.y, base.z), 487 | point3d(base.x, base.y, base.z + a), 488 | point3d(base.x + a, base.y, base.z), 489 | point2d(0, 0), 490 | point2d(0, -1), 491 | point2d(1, 0), 492 | negy, 493 | rgb(255, 0, 255), 494 | m)); 495 | add_object(scene, new_triangle_with_texture( 496 | point3d(base.x, base.y, base.z + a), 497 | point3d(base.x + a, base.y, base.z), 498 | point3d(base.x + a, base.y, base.z + a), 499 | point2d(0, -1), 500 | point2d(1, 0), 501 | point2d(1, -1), 502 | negy, 503 | rgb(255, 0, 255), 504 | m)); 505 | 506 | Canvas * posy = read_png("./models/skybox/negz.png"); 507 | add_object(scene, new_triangle_with_texture( 508 | point3d(base.x, base.y + a, base.z), 509 | point3d(base.x, base.y + a, base.z + a), 510 | point3d(base.x + a, base.y + a, base.z), 511 | point2d(0, 0), 512 | point2d(0, -1), 513 | point2d(-1, 0), 514 | posy, 515 | rgb(0, 255, 255), 516 | m)); 517 | add_object(scene, new_triangle_with_texture( 518 | point3d(base.x, base.y + a, base.z + a), 519 | point3d(base.x + a, base.y + a, base.z), 520 | point3d(base.x + a, base.y + a, base.z + a), 521 | point2d(0, -1), 522 | point2d(-1, 0), 523 | point2d(-1, -1), 524 | posy, 525 | rgb(0, 255, 255), 526 | m)); 527 | } -------------------------------------------------------------------------------- /demo/scene.h: -------------------------------------------------------------------------------- 1 | #ifndef __SCENE_H__ 2 | #define __SCENE_H__ 3 | 4 | Scene *makeScene(void); 5 | 6 | #endif //__SCENE_H__ 7 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define CANVAS_W 400 8 | #define CANVAS_H 400 9 | 10 | // Boost by rendering in parallel 11 | #define THREADS_NUM 4 12 | 13 | #define BACKGROUND_COLOR rgb(255, 255, 255) 14 | 15 | #define MAX_OBJECTS_NUMBER 10000 16 | #define MAX_LIGHT_SOURCES_NUMBER 5 17 | 18 | int 19 | main(void) { 20 | // Allocating scene 21 | Scene * scene = new_scene(MAX_OBJECTS_NUMBER, 22 | MAX_LIGHT_SOURCES_NUMBER, 23 | BACKGROUND_COLOR); 24 | 25 | // Allocating new sphere 26 | Float radius = 100; 27 | Point3d center = point3d(0, 0, 0); 28 | Color sphere_color = rgb(250, 30, 30); 29 | Material sphere_material = material(1, 5, 5, 10, 0, 10); 30 | Object3d * sphere = new_sphere(center, 31 | radius, 32 | sphere_color, 33 | sphere_material); 34 | 35 | // Adding sphere to the scene 36 | add_object(scene, 37 | sphere); 38 | 39 | // Allocating new triangle 40 | Object3d * triangle = new_triangle(point3d(-700, -700, -130), // vertex 1 41 | point3d( 700, -700, -130), // vertex 2 42 | point3d( 0, 400, -130), // vertex 3 43 | rgb(100, 255, 30), // color 44 | material(1, 6, 0, 2, 0, 0) // surface params 45 | ); 46 | 47 | // Adding triangle to the scene 48 | add_object(scene, 49 | triangle); 50 | 51 | // Loading 3D model of cow from *.obj file 52 | // defining transformations and parameters of 3D model 53 | // TODO: must be refactored... 54 | SceneFaceHandlerParams load_params = 55 | new_scene_face_handler_params(scene, 56 | // scale: 57 | 40, 58 | // move dx, dy, dz: 59 | -150, -100, 30, 60 | // rotate around axises x, y, z: 61 | 0, 0, 0, 62 | // color 63 | rgb(200, 200, 50), 64 | // surface params 65 | material(2, 3, 0, 0, 0, 0) 66 | ); 67 | 68 | load_obj("./demo/models/cow.obj", 69 | // default handler which adding polygons of 3D model to scene: 70 | scene_face_handler, 71 | &load_params); 72 | 73 | // This function is requried (bulding k-d tree of entire scene) 74 | prepare_scene(scene); 75 | 76 | printf("\nNumber of polygons: %i\n", scene->last_object_index + 1); 77 | 78 | // Allocating new light source 79 | Color light_source_color = rgb(255, 255, 255); 80 | Point3d light_source_location = point3d(-300, 300, 300); 81 | LightSource3d * light_source = new_light_source(light_source_location, 82 | light_source_color); 83 | // Adding light source to the scene 84 | add_light_source(scene, 85 | light_source); 86 | 87 | // Adding fog 88 | Float density = 0.002; 89 | set_exponential_fog(scene, density); 90 | 91 | // Allocating camera 92 | // TODO: It's a pity, but quaternions are not implemented yet :( 93 | Point3d camera_location = point3d(0, 500, 0); 94 | Float focus = 320; 95 | Float x_angle = -1.57; 96 | Float y_angle = 0; 97 | Float z_angle = 3.14; 98 | Camera * camera = new_camera(camera_location, 99 | x_angle, 100 | y_angle, 101 | z_angle, 102 | focus); 103 | 104 | // Rotate camera if needed 105 | // rotate_camera(camera, d_x_angle, d_y_angle, d_z_angle); 106 | 107 | // Move camera if needed 108 | // move_camera(camera, vector3df(d_x, d_y, d_z)); 109 | 110 | // Alocate new canvas, to render scene on it 111 | Canvas * canvas = new_canvas(CANVAS_W, 112 | CANVAS_H); 113 | 114 | render_scene(scene, 115 | camera, 116 | canvas, 117 | THREADS_NUM); 118 | 119 | // Saving rendered image in PNG format 120 | write_png("example.png", 121 | canvas); 122 | 123 | Canvas * grayscaled_canvas = grayscale_canvas(canvas, 124 | THREADS_NUM); 125 | write_png("gray_example.png", 126 | grayscaled_canvas); 127 | 128 | Canvas * edges_canvas = detect_edges_canvas(canvas, 129 | THREADS_NUM); 130 | write_png("edges_example.png", 131 | edges_canvas); 132 | 133 | release_canvas(canvas); 134 | release_canvas(grayscaled_canvas); 135 | release_canvas(edges_canvas); 136 | release_scene(scene); 137 | release_camera(camera); 138 | 139 | return 0; 140 | } -------------------------------------------------------------------------------- /img/demo_scenes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/img/demo_scenes.jpg -------------------------------------------------------------------------------- /img/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lagodiuk/raytracing-render/86bb967f6ec4cd1cd4c676912720dd193d7ed903/img/example.png -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | LIBPATH = -Lrender/lib 2 | INCLUDES = -Irender/include 3 | LIBS = -lrender -lm -lpng -fopenmp 4 | 5 | CC = gcc 6 | CC_OPTS = -std=gnu89 -Wall -O2 7 | 8 | render = render/lib/librender.a 9 | 10 | benchmark: $(render) benchmark.c 11 | $(CC) $(CC_OPTS) -fopenmp benchmark.c $(LIBPATH) $(INCLUDES) $(LIBS) -o $@ 12 | 13 | example: $(render) example.c 14 | $(CC) $(CC_OPTS) -fopenmp example.c $(LIBPATH) $(INCLUDES) $(LIBS) -o $@ 15 | 16 | run_demo_gl: $(render) 17 | (cd demo && make DEF="$(DEF)" run_demo_gl) 18 | 19 | $(render): 20 | (cd render && make DEF="$(DEF)" render) 21 | 22 | .PHONY: clean 23 | clean: 24 | (cd render && make clean) && \ 25 | (cd demo && make clean) && \ 26 | rm -f ./example ./benchmark; \ 27 | rm -f *.png 28 | -------------------------------------------------------------------------------- /render/include/canvas.h: -------------------------------------------------------------------------------- 1 | #ifndef __CANVAS_H__ 2 | #define __CANVAS_H__ 3 | 4 | #include 5 | 6 | typedef 7 | struct { 8 | int w; 9 | int h; 10 | Color * data; 11 | } 12 | Canvas; 13 | 14 | Canvas * 15 | new_canvas(int width, 16 | int height); 17 | 18 | Canvas * 19 | grayscale_canvas(Canvas * base, 20 | int num_threads); 21 | 22 | Canvas * 23 | detect_edges_canvas(Canvas * base, 24 | int num_threads); 25 | 26 | void 27 | release_canvas(Canvas * c); 28 | 29 | void 30 | clear_canvas(Canvas * canv); 31 | 32 | static inline void 33 | set_pixel(int x, 34 | int y, 35 | Color c, 36 | Canvas * canv) { 37 | 38 | const int offs = y * canv->w + x; 39 | canv->data[offs] = c; 40 | } 41 | 42 | static inline Color 43 | get_pixel(int x, 44 | int y, 45 | Canvas * canv) { 46 | 47 | const int offs = y * canv->w + x; 48 | return canv->data[offs]; 49 | } 50 | 51 | Canvas * 52 | read_png(char * file_name); 53 | 54 | void write_png(char file_name[], 55 | Canvas * canv); 56 | 57 | #endif //__CANVAS_H__ 58 | -------------------------------------------------------------------------------- /render/include/color.h: -------------------------------------------------------------------------------- 1 | #ifndef COLOR_H_INCLUDED 2 | #define COLOR_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_R 255 8 | #define MAX_G 255 9 | #define MAX_B 255 10 | 11 | typedef 12 | uint8_t 13 | Byte; 14 | 15 | typedef 16 | struct { 17 | Byte r; 18 | Byte g; 19 | Byte b; 20 | } 21 | Color; 22 | 23 | static inline Color 24 | rgb(Byte r, 25 | Byte g, 26 | Byte b) { 27 | 28 | Color c; 29 | c.r = r; 30 | c.g = g; 31 | c.b = b; 32 | return c; 33 | } 34 | 35 | static inline Color 36 | add_colors(Color c1, 37 | Color c2) { 38 | 39 | int r = (int) c1.r + c2.r; 40 | int g = (int) c1.g + c2.g; 41 | int b = (int) c1.b + c2.b; 42 | r = (r < MAX_R) ? r : MAX_R; 43 | g = (g < MAX_G) ? g : MAX_G; 44 | b = (b < MAX_B) ? b : MAX_B; 45 | return rgb((Byte) r, (Byte) g, (Byte) b); 46 | } 47 | 48 | static inline Color 49 | mix_colors(Color c1, 50 | Color c2) { 51 | 52 | /* 53 | uint16_t r = ((uint16_t) c1.r + c2.r) >> 1; 54 | uint16_t g = ((uint16_t) c1.g + c2.g) >> 1; 55 | uint16_t b = ((uint16_t) c1.b + c2.b) >> 1; 56 | */ 57 | 58 | uint16_t r = (c1.r * c2.r) >> 8; 59 | uint16_t g = (c1.g * c2.g) >> 8; 60 | uint16_t b = (c1.b * c2.b) >> 8; 61 | 62 | /* 63 | uint16_t r = sqrt(c1.r * c2.r); 64 | uint16_t g = sqrt(c1.g * c2.g); 65 | uint16_t b = sqrt(c1.b * c2.b); 66 | */ 67 | return rgb((Byte) r, (Byte) g, (Byte) b); 68 | } 69 | 70 | static inline Color 71 | mul_color(Color c, 72 | double k) { 73 | 74 | return rgb((Byte) (c.r * k), (Byte) (c.g * k), (Byte) (c.b * k)); 75 | } 76 | 77 | static inline Color 78 | grayscale(Color c) { 79 | // https://en.wikipedia.org/wiki/Grayscale 80 | // const Byte gray = (Byte)(c.r * 0.299 + c.g * 0.587 + c.b * 0.114); 81 | const Byte gray = (Byte)(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722); 82 | return rgb(gray, gray, gray); 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /render/include/kdtree.h: -------------------------------------------------------------------------------- 1 | #ifndef __KD_TREEE_H__ 2 | #define __KD_TREEE_H__ 3 | 4 | #include 5 | 6 | KDTree * 7 | build_kd_tree(Object3d ** objects, 8 | int objects_count); 9 | 10 | void 11 | release_kd_tree(KDTree * tree); 12 | 13 | Boolean 14 | find_intersection_tree(KDTree * const tree, 15 | const Point3d vector_start, 16 | const Vector3d vector, 17 | Object3d ** const nearest_obj_ptr, 18 | Point3d * const nearest_intersection_point_ptr, 19 | Float * const nearest_intersection_point_dist_ptr); 20 | 21 | #endif -------------------------------------------------------------------------------- /render/include/obj_loader.h: -------------------------------------------------------------------------------- 1 | #ifndef _OBJ_LOADER_H_ 2 | #define _OBJ_LOADER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef 10 | struct { 11 | Float scale; 12 | 13 | Float dx; 14 | Float dy; 15 | Float dz; 16 | 17 | Scene * scene; 18 | 19 | Float sin_al_x; 20 | Float cos_al_x; 21 | 22 | Float sin_al_y; 23 | Float cos_al_y; 24 | 25 | Float sin_al_z; 26 | Float cos_al_z; 27 | 28 | Color default_color; 29 | Material default_material; 30 | } 31 | SceneFaceHandlerParams; 32 | 33 | 34 | void 35 | load_obj(const char * filename, 36 | void (* face_handler)(Queue * vertexes, 37 | Queue * norm_vectors, 38 | void * args), 39 | void * args); 40 | 41 | //---------------------------------------------------- 42 | 43 | void 44 | scene_face_handler(Queue * vertexes, 45 | Queue * norm_vectors, 46 | void * arg); 47 | 48 | static inline SceneFaceHandlerParams 49 | new_scene_face_handler_params(Scene * scene, 50 | Float scale, 51 | Float dx, 52 | Float dy, 53 | Float dz, 54 | Float al_x, 55 | Float al_y, 56 | Float al_z, 57 | Color default_color, 58 | Material default_material) { 59 | 60 | SceneFaceHandlerParams params = 61 | {.scene = scene, 62 | .scale = scale, 63 | .dx = dx, 64 | .dy = dy, 65 | .dz = dz, 66 | .sin_al_x = sin(al_x), 67 | .cos_al_x = cos(al_x), 68 | .sin_al_y = sin(al_y), 69 | .cos_al_y = cos(al_y), 70 | .sin_al_z = sin(al_z), 71 | .cos_al_z = cos(al_z), 72 | .default_color = default_color, 73 | .default_material = default_material}; 74 | return params; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /render/include/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __QUEUE_H__ 2 | #define __QUEUE_H__ 3 | 4 | #include 5 | 6 | typedef 7 | struct Elem { 8 | void * obj; 9 | struct Elem * prev; 10 | } 11 | Elem; 12 | 13 | typedef 14 | struct { 15 | Elem * head; 16 | Elem * tail; 17 | volatile int size; 18 | } 19 | Queue; 20 | 21 | static inline Queue * 22 | new_queue(); 23 | 24 | static inline void 25 | release_queue(Queue * q); 26 | 27 | static inline void 28 | add(void * obj, 29 | Queue * q); 30 | 31 | static inline void * 32 | get(Queue * q); 33 | 34 | static inline int 35 | get_size(Queue * q); 36 | 37 | static inline int 38 | is_empty(Queue * q); 39 | 40 | static inline Queue * 41 | new_queue() { 42 | Queue * q = malloc(sizeof(Queue)); 43 | q->head = NULL; 44 | q->tail = NULL; 45 | q->size = 0; 46 | return q; 47 | } 48 | 49 | static inline void 50 | release_queue(Queue * q) { 51 | while(q->size) // not empty 52 | get(q); 53 | 54 | free(q); 55 | } 56 | 57 | static inline int 58 | get_size(Queue * q) { 59 | return q->size; 60 | } 61 | 62 | static inline int 63 | is_empty(Queue * q) { 64 | return !q->size; 65 | } 66 | 67 | static inline void 68 | add(void * obj, 69 | Queue * q) { 70 | 71 | Elem * e = malloc(sizeof(Elem)); 72 | e->obj = obj; 73 | e->prev = NULL; 74 | 75 | if(q->size) 76 | q->head->prev = e; 77 | else 78 | q->tail = e; 79 | 80 | q->head = e; 81 | 82 | q->size++; 83 | } 84 | 85 | static inline void * 86 | get(Queue * q) { 87 | 88 | if(!q->size) // is empty 89 | return NULL; 90 | 91 | Elem * t = q->tail; 92 | 93 | void * obj = t->obj; 94 | q->tail = t->prev; 95 | 96 | free(t); 97 | 98 | q->size--; 99 | 100 | if(!q->size) { 101 | q->head = NULL; 102 | q->tail = NULL; 103 | } 104 | 105 | return obj; 106 | } 107 | 108 | #endif -------------------------------------------------------------------------------- /render/include/render.h: -------------------------------------------------------------------------------- 1 | #ifndef __RENDER_H__ 2 | #define __RENDER_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef RAY_INTERSECTIONS_STAT 11 | long 12 | intersections_per_ray; 13 | #endif // RAY_INTERSECTIONS_STAT 14 | 15 | typedef 16 | int 17 | Boolean; 18 | 19 | #define True 1 20 | #define False 0 21 | 22 | // Using double for satisfactory accuracy 23 | typedef 24 | double 25 | Float; 26 | 27 | #define EPSILON 1e-5 28 | 29 | #define FLOAT_MAX DBL_MAX 30 | 31 | typedef 32 | struct { 33 | Float x; 34 | Float y; 35 | } 36 | Point2d; 37 | 38 | typedef 39 | struct { 40 | Float x; 41 | Float y; 42 | Float z; 43 | } 44 | Point3d; 45 | 46 | typedef 47 | struct { 48 | Float x; 49 | Float y; 50 | Float z; 51 | } 52 | Vector3d; 53 | 54 | typedef 55 | struct { 56 | // Absolute location 57 | Point3d location_world; 58 | // Location on projection 59 | Point3d location; 60 | Color color; 61 | } 62 | LightSource3d; 63 | 64 | typedef 65 | struct { 66 | // Required: 67 | // Ka + Kd + Ks + Kr + Kt = 1.0 68 | 69 | // Ambient 70 | Float Ka; 71 | // Diffuse 72 | Float Kd; 73 | // Specular 74 | Float Ks; 75 | // Reflection 76 | Float Kr; 77 | // Transparency 78 | Float Kt; 79 | 80 | // Ks * light_source_color * ((cos(..))^p) 81 | Float p; 82 | } 83 | Material; 84 | 85 | typedef 86 | struct { 87 | void * data; 88 | 89 | Boolean (*intersect)(const void * data, 90 | const Point3d vector_start, 91 | const Vector3d vector, 92 | Point3d * const intersection_point); 93 | 94 | Color (*get_color)(const void * data, 95 | const Point3d intersection_point); 96 | 97 | Vector3d (*get_normal_vector)(const void * data, 98 | const Point3d intersection_point); 99 | 100 | Material (*get_material)(const void * data, 101 | const Point3d intersection_point); 102 | 103 | Point3d (*get_min_boundary_point)(const void * data); 104 | 105 | Point3d (*get_max_boundary_point)(const void * data); 106 | 107 | void (*release_data)(void * data); 108 | } 109 | Object3d; 110 | 111 | // KD Tree 112 | enum Plane {XY, XZ, YZ, NONE}; 113 | 114 | typedef 115 | union { 116 | Float x; 117 | Float y; 118 | Float z; 119 | } 120 | Coord; 121 | 122 | typedef 123 | struct { 124 | Float x_min; 125 | Float y_min; 126 | Float z_min; 127 | 128 | Float x_max; 129 | Float y_max; 130 | Float z_max; 131 | } 132 | Voxel; 133 | 134 | typedef 135 | struct KDNode { 136 | enum Plane plane; 137 | Coord coord; 138 | 139 | Object3d ** objects; 140 | int objects_count; 141 | 142 | struct KDNode * l; 143 | struct KDNode * r; 144 | } 145 | KDNode; 146 | 147 | typedef 148 | struct { 149 | KDNode * root; 150 | Voxel bounding_box; 151 | } 152 | KDTree; 153 | // 154 | 155 | typedef 156 | struct { 157 | // Array of pointers to 3d objects of scene 158 | Object3d ** objects; 159 | int objects_count; 160 | int last_object_index; 161 | 162 | KDTree * kd_tree; 163 | 164 | // Array of pointers to light sources 165 | LightSource3d ** light_sources; 166 | int light_sources_count; 167 | int last_light_source_index; 168 | 169 | Color background_color; 170 | 171 | // Required to return value from interval [0..1] 172 | Float (*fog_density)(const Float distance, 173 | const void * fog_parameters); 174 | void * fog_parameters; 175 | } 176 | Scene; 177 | 178 | typedef 179 | struct { 180 | Point3d camera_position; 181 | 182 | Float al_x; 183 | Float sin_al_x; 184 | Float cos_al_x; 185 | 186 | Float al_y; 187 | Float sin_al_y; 188 | Float cos_al_y; 189 | 190 | Float al_z; 191 | Float sin_al_z; 192 | Float cos_al_z; 193 | 194 | Float proj_plane_dist; 195 | } 196 | Camera; 197 | 198 | /*************************************************** 199 | * Render * 200 | ***************************************************/ 201 | 202 | void 203 | render_scene(const Scene * const scene, 204 | const Camera * const camera, 205 | Canvas * canvas, 206 | const int num_threads); 207 | 208 | /*************************************************** 209 | * Scene * 210 | ***************************************************/ 211 | 212 | Scene * 213 | new_scene(const int objects_count, 214 | const int light_sources_count, 215 | const Color background_color); 216 | 217 | void 218 | release_scene(Scene * scene); 219 | 220 | void 221 | add_object(Scene * const scene, 222 | Object3d * const object); 223 | 224 | void 225 | prepare_scene(Scene * const scene); 226 | 227 | void 228 | set_exponential_fog(Scene * const scene, 229 | const Float k); 230 | 231 | void 232 | set_no_fog(Scene * const scene); 233 | 234 | Color 235 | trace(const Scene * const scene, 236 | const Camera * const camera, 237 | Vector3d vector); 238 | 239 | void 240 | add_light_source(Scene * const scene, 241 | LightSource3d * const light_source); 242 | 243 | /*************************************************** 244 | * 3D objects * 245 | ***************************************************/ 246 | 247 | Object3d * 248 | new_triangle(const Point3d p1, 249 | const Point3d p2, 250 | const Point3d p3, 251 | const Color color, 252 | const Material material); 253 | 254 | Object3d * 255 | new_triangle_with_norms(const Point3d p1, 256 | const Point3d p2, 257 | const Point3d p3, 258 | const Vector3d n1, 259 | const Vector3d n2, 260 | const Vector3d n3, 261 | const Color color, 262 | const Material material); 263 | 264 | Object3d * 265 | new_triangle_with_texture(const Point3d p1, 266 | const Point3d p2, 267 | const Point3d p3, 268 | const Point2d t1, 269 | const Point2d t2, 270 | const Point2d t3, 271 | Canvas * texture, 272 | const Color color, 273 | const Material material); 274 | 275 | Object3d * 276 | new_sphere(const Point3d center, 277 | const Float radius, 278 | const Color color, 279 | const Material material); 280 | 281 | void 282 | release_object3d(Object3d * obj); 283 | 284 | LightSource3d * 285 | new_light_source(const Point3d location, 286 | const Color color); 287 | 288 | Material 289 | material(const Float Ka, 290 | const Float Kd, 291 | const Float Ks, 292 | const Float Kr, 293 | const Float Kt, 294 | const Float p); 295 | 296 | /*************************************************** 297 | * Camera * 298 | ***************************************************/ 299 | 300 | Camera * 301 | new_camera(const Point3d camera_position, 302 | const Float al_x, 303 | const Float al_y, 304 | const Float al_z, 305 | const Float proj_plane_dist); 306 | 307 | void 308 | release_camera(Camera * const cam); 309 | 310 | void 311 | rotate_camera(Camera * const cam, 312 | const Float al_x, 313 | const Float al_y, 314 | const Float al_z); 315 | 316 | void 317 | move_camera(Camera * const camera, 318 | const Vector3d vector); 319 | 320 | /*************************************************** 321 | * Point and vectors * 322 | ***************************************************/ 323 | 324 | static inline Point2d 325 | point2d(const Float x, 326 | const Float y); 327 | 328 | static inline Point3d 329 | point3d(const Float x, 330 | const Float y, 331 | const Float z); 332 | 333 | static inline Vector3d 334 | vector3dp(const Point3d start_point, 335 | const Point3d end_point); 336 | 337 | static inline Vector3d 338 | vector3df(const Float x, 339 | const Float y, 340 | const Float z); 341 | 342 | static inline Point2d 343 | point2d(const Float x, 344 | const Float y) { 345 | 346 | const Point2d p = {.x = x, .y = y}; 347 | return p; 348 | } 349 | 350 | static inline Point3d 351 | point3d(const Float x, 352 | const Float y, 353 | const Float z) { 354 | 355 | const Point3d p = {.x = x, .y = y, .z = z}; 356 | return p; 357 | } 358 | 359 | static inline Vector3d 360 | vector3dp(const Point3d start_point, 361 | const Point3d end_point) { 362 | 363 | const Vector3d v = {.x = (end_point.x - start_point.x), 364 | .y = (end_point.y - start_point.y), 365 | .z = (end_point.z - start_point.z)}; 366 | return v; 367 | } 368 | 369 | static inline Vector3d 370 | vector3df(const Float x, 371 | const Float y, 372 | const Float z) { 373 | 374 | const Vector3d v = {.x = x, .y = y, .z = z}; 375 | return v; 376 | } 377 | #endif //__RENDER_H__ 378 | -------------------------------------------------------------------------------- /render/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_3D_H 2 | #define UTILS_3D_H 3 | 4 | #include 5 | #include 6 | 7 | static inline Vector3d 8 | cross_product(const Vector3d a, 9 | const Vector3d b) { 10 | 11 | return vector3df(a.z * b.y - a.y * b.z, 12 | a.x * b.z - a.z * b.x, 13 | a.y * b.x - a.x * b.y); 14 | } 15 | 16 | static inline Float 17 | dot_product(const Vector3d v1, 18 | const Vector3d v2) { 19 | 20 | return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; 21 | } 22 | 23 | static inline Float 24 | sqr_module_vector(const Vector3d v) { 25 | return dot_product(v, v); 26 | } 27 | 28 | static inline Float 29 | module_vector(const Vector3d v) { 30 | return sqrt(sqr_module_vector(v)); 31 | } 32 | 33 | static inline Float 34 | cos_vectors(const Vector3d v1, 35 | const Vector3d v2) { 36 | 37 | return dot_product(v1, v2) / sqrt(sqr_module_vector(v1) * sqr_module_vector(v2)); 38 | } 39 | 40 | static inline void 41 | normalize_vector(Vector3d * const v) { 42 | const Float module = module_vector(*v); 43 | v->x = v->x / module; 44 | v->y = v->y / module; 45 | v->z = v->z / module; 46 | } 47 | 48 | static inline Vector3d 49 | reflect_ray(const Vector3d incident_ray, 50 | const Vector3d norm_v) { 51 | 52 | const Float k = 2 * dot_product(incident_ray, norm_v) / sqr_module_vector(norm_v); 53 | 54 | const Float x = incident_ray.x - norm_v.x * k; 55 | const Float y = incident_ray.y - norm_v.y * k; 56 | const Float z = incident_ray.z - norm_v.z * k; 57 | 58 | return vector3df(x, y, z); 59 | } 60 | 61 | // TODO: use matrixes 62 | static inline Point3d 63 | rotate_point_x(const Point3d p, 64 | const Float sin_al, 65 | const Float cos_al) { 66 | 67 | const Float y = p.y * cos_al - p.z * sin_al; 68 | const Float z = p.y * sin_al + p.z * cos_al; 69 | 70 | return point3d(p.x, y, z); 71 | } 72 | 73 | static inline Point3d 74 | rotate_point_y(const Point3d p, 75 | const Float sin_al, 76 | const Float cos_al) { 77 | 78 | const Float x = p.x * cos_al - p.z * sin_al; 79 | const Float z = p.x * sin_al + p.z * cos_al; 80 | 81 | return point3d(x, p.y, z); 82 | } 83 | 84 | static inline Point3d 85 | rotate_point_z(const Point3d p, 86 | const Float sin_al, 87 | const Float cos_al) { 88 | 89 | const Float x = p.x * cos_al - p.y * sin_al; 90 | const Float y = p.x * sin_al + p.y * cos_al; 91 | 92 | return point3d(x, y, p.z); 93 | } 94 | 95 | // TODO: use matrixes 96 | static inline Vector3d 97 | rotate_vector_x(const Vector3d p, 98 | const Float sin_al, 99 | const Float cos_al) { 100 | 101 | const Float y = p.y * cos_al - p.z * sin_al; 102 | const Float z = p.y * sin_al + p.z * cos_al; 103 | 104 | return vector3df(p.x, y, z); 105 | } 106 | 107 | static inline Vector3d 108 | rotate_vector_y(const Vector3d p, 109 | const Float sin_al, 110 | const Float cos_al) { 111 | 112 | const Float x = p.x * cos_al - p.z * sin_al; 113 | const Float z = p.x * sin_al + p.z * cos_al; 114 | 115 | return vector3df(x, p.y, z); 116 | } 117 | 118 | static inline Vector3d 119 | rotate_vector_z(const Vector3d p, 120 | const Float sin_al, 121 | const Float cos_al) { 122 | 123 | const Float x = p.x * cos_al - p.y * sin_al; 124 | const Float y = p.x * sin_al + p.y * cos_al; 125 | 126 | return vector3df(x, y, p.z); 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /render/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | 3 | INCLUDES = $(addprefix -I, ./include) 4 | 5 | ifeq ($(shell uname),Darwin) 6 | INCLUDES += -I/usr/X11/include 7 | INCLUDES += -I/usr/local/include 8 | endif 9 | 10 | CFLAGS = $(DEF) -Wall 11 | CFLAGS += -std=gnu89 -O2 12 | # 13 | # http://www.insidepro.com/kk/231/231r.shtml 14 | # Boosting render performance a bit :) 15 | #CFLAGS += -O3 -ffast-math 16 | 17 | lib_dir = ./lib 18 | 19 | render_lib = $(lib_dir)/librender.a 20 | 21 | $(lib_dir): 22 | mkdir -p $@ 23 | 24 | $(lib_dir)/obj_loader.o: ./src/obj_loader.c ./include/obj_loader.h ./include/queue.h $(lib_dir) 25 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/obj_loader.c -o $@ 26 | 27 | $(lib_dir)/canvas.o: ./src/canvas.c ./include/canvas.h ./include/color.h $(lib_dir) 28 | $(CC) $(CFLAGS) $(INCLUDES) -fopenmp -c ./src/canvas.c -o $@ 29 | 30 | $(lib_dir)/scene.o: ./src/scene.c ./include/render.h ./include/color.h $(lib_dir) 31 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/scene.c -o $@ 32 | 33 | $(lib_dir)/fog.o: ./src/fog.c ./include/render.h $(lib_dir) 34 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/fog.c -o $@ 35 | 36 | $(lib_dir)/tracer.o: ./src/tracer.c ./include/render.h ./include/color.h $(lib_dir) 37 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/tracer.c -o $@ 38 | 39 | $(lib_dir)/render.o: ./src/render.c ./include/render.h ./include/color.h $(lib_dir) 40 | $(CC) $(CFLAGS) $(INCLUDES) -fopenmp -c ./src/render.c -o $@ 41 | 42 | $(lib_dir)/triangle.o: ./src/triangle.c ./include/render.h ./include/color.h $(lib_dir) 43 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/triangle.c -o $@ 44 | 45 | $(lib_dir)/sphere.o: ./src/sphere.c ./include/render.h ./include/color.h $(lib_dir) 46 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/sphere.c -o $@ 47 | 48 | $(lib_dir)/kdtree.o: ./src/kdtree.c ./include/kdtree.h ./include/render.h $(lib_dir) 49 | $(CC) $(CFLAGS) $(INCLUDES) -c ./src/kdtree.c -o $@ 50 | 51 | render: $(lib_dir)/tracer.o $(lib_dir)/render.o $(lib_dir)/triangle.o $(lib_dir)/sphere.o $(lib_dir)/kdtree.o $(lib_dir)/scene.o $(lib_dir)/fog.o $(lib_dir)/canvas.o $(lib_dir)/obj_loader.o 52 | ar -rcs $(render_lib) $^ 53 | 54 | .PHONY: clean 55 | clean: 56 | rm -rf $(lib_dir) 57 | -------------------------------------------------------------------------------- /render/src/canvas.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #define PNG_DEBUG 3 12 | #include 13 | 14 | #include 15 | 16 | #define IMG_CHUNK 10 17 | 18 | /* collapse is a feature from OpenMP 3 (2008) */ 19 | #if _OPENMP < 200805 20 | #define collapse(x) 21 | #endif 22 | 23 | #include 24 | 25 | 26 | Canvas * 27 | new_canvas(int width, 28 | int height) { 29 | 30 | Canvas * c = (Canvas *) malloc(sizeof(Canvas)); 31 | c->w = width; 32 | c->h = height; 33 | c->data = (Color *) calloc(width * height + 1, sizeof(Color)); 34 | return c; 35 | } 36 | 37 | void 38 | release_canvas(Canvas * c) { 39 | free(c->data); 40 | free(c); 41 | } 42 | 43 | void 44 | clear_canvas(Canvas * canv) { 45 | memset(canv->data, 0, canv->w * canv->h * sizeof(Color)); 46 | } 47 | 48 | // Just adapted from http://zarb.org/~gc/html/libpng.html 49 | // TODO: refactoring 50 | 51 | void 52 | abort_(const char * s, 53 | ...) { 54 | 55 | va_list args; 56 | va_start(args, s); 57 | vfprintf(stderr, s, args); 58 | fprintf(stderr, "\n"); 59 | va_end(args); 60 | abort(); 61 | } 62 | 63 | void 64 | write_png(char file_name[], 65 | Canvas * canv) { 66 | 67 | // create file 68 | FILE *fp = fopen(file_name, "wb"); 69 | if (!fp) 70 | abort_("[write_png_file] File %s could not be opened for writing", file_name); 71 | 72 | // initialize stuff 73 | png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 74 | if (!png_ptr) 75 | abort_("[write_png_file] png_create_write_struct failed"); 76 | 77 | png_infop info_ptr = png_create_info_struct(png_ptr); 78 | if (!info_ptr) 79 | abort_("[write_png_file] png_create_info_struct failed"); 80 | 81 | if (setjmp(png_jmpbuf(png_ptr))) 82 | abort_("[write_png_file] Error during init_io"); 83 | 84 | png_init_io(png_ptr, fp); 85 | 86 | 87 | // write header 88 | if (setjmp(png_jmpbuf(png_ptr))) 89 | abort_("[write_png_file] Error during writing header"); 90 | 91 | png_byte bit_depth = 8; 92 | png_byte color_type = PNG_COLOR_TYPE_RGB; 93 | 94 | png_set_IHDR(png_ptr, info_ptr, canv->w, canv->h, 95 | bit_depth, color_type, PNG_INTERLACE_NONE, 96 | PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 97 | 98 | png_write_info(png_ptr, info_ptr); 99 | 100 | 101 | png_bytep * row_pointers = (png_bytep *) malloc(sizeof(png_bytep) * canv->h); 102 | int y; 103 | int x; 104 | for (y=0; y < canv->h; y++) { 105 | row_pointers[y] = (png_byte*) malloc(canv->w * 3 * bit_depth); 106 | png_byte* row = row_pointers[y]; 107 | for(x = 0; x < canv->w; x++) { 108 | Color c = get_pixel(x, y, canv); 109 | png_byte * ptr = &(row[x * 3]); 110 | ptr[0] = c.r; 111 | ptr[1] = c.g; 112 | ptr[2] = c.b; 113 | } 114 | } 115 | 116 | 117 | // write bytes 118 | if (setjmp(png_jmpbuf(png_ptr))) 119 | abort_("[write_png_file] Error during writing bytes"); 120 | 121 | png_write_image(png_ptr, row_pointers); 122 | 123 | 124 | // end write 125 | if (setjmp(png_jmpbuf(png_ptr))) 126 | abort_("[write_png_file] Error during end of write"); 127 | 128 | png_write_end(png_ptr, NULL); 129 | 130 | // cleanup heap allocation 131 | for (y=0; y < canv->h; y++) 132 | free(row_pointers[y]); 133 | free(row_pointers); 134 | 135 | fclose(fp); 136 | } 137 | 138 | Canvas * 139 | read_png(char * file_name) { 140 | 141 | unsigned char header[8]; // 8 is the maximum size that can be checked 142 | 143 | // open file and test for it being a png 144 | FILE *fp = fopen(file_name, "rb"); 145 | if (!fp) 146 | abort_("[read_png_file] File %s could not be opened for reading", file_name); 147 | fread(header, 1, 8, fp); 148 | if (png_sig_cmp(header, 0, 8)) 149 | abort_("[read_png_file] File %s is not recognized as a PNG file", file_name); 150 | 151 | 152 | // initialize stuff 153 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 154 | 155 | if (!png_ptr) 156 | abort_("[read_png_file] png_create_read_struct failed"); 157 | 158 | png_infop info_ptr = png_create_info_struct(png_ptr); 159 | if (!info_ptr) 160 | abort_("[read_png_file] png_create_info_struct failed"); 161 | 162 | if (setjmp(png_jmpbuf(png_ptr))) 163 | abort_("[read_png_file] Error during init_io"); 164 | 165 | png_init_io(png_ptr, fp); 166 | png_set_sig_bytes(png_ptr, 8); 167 | 168 | png_read_info(png_ptr, info_ptr); 169 | 170 | int width = png_get_image_width(png_ptr, info_ptr); 171 | int height = png_get_image_height(png_ptr, info_ptr); 172 | 173 | png_set_interlace_handling(png_ptr); 174 | png_read_update_info(png_ptr, info_ptr); 175 | 176 | 177 | // read file 178 | if (setjmp(png_jmpbuf(png_ptr))) 179 | abort_("[read_png_file] Error during read_image"); 180 | 181 | png_bytep * row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); 182 | int y; 183 | for (y = 0; y < height; y++) 184 | row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr)); 185 | 186 | png_read_image(png_ptr, row_pointers); 187 | 188 | Canvas * canvas = new_canvas(width, height); 189 | int x; 190 | for (y=0; y < canvas->h; y++) { 191 | png_byte * row = row_pointers[y]; 192 | for(x = 0; x < canvas->w; x++) { 193 | png_byte * ptr = &(row[x * 3]); 194 | 195 | set_pixel(x, y, rgb(ptr[0], ptr[1], ptr[2]), canvas); 196 | } 197 | } 198 | 199 | 200 | // cleanup heap allocation 201 | for (y=0; y < canvas->h; y++) 202 | free(row_pointers[y]); 203 | free(row_pointers); 204 | 205 | fclose(fp); 206 | 207 | return canvas; 208 | } 209 | 210 | Canvas * 211 | grayscale_canvas(Canvas * base, 212 | int num_threads) { 213 | const int w = base->w; 214 | const int h = base->h; 215 | Canvas * ret = new_canvas(w, h); 216 | 217 | omp_set_num_threads((num_threads < 2) ? 1 : num_threads); 218 | 219 | int x; 220 | int y; 221 | #pragma omp parallel private(x, y) 222 | #pragma omp for collapse(2) schedule(dynamic, IMG_CHUNK) 223 | for(x = 0; x < w; ++x) { 224 | for(y = 0; y < h; ++y) { 225 | const Color c = get_pixel(x, y, base); 226 | const Color gray = grayscale(c); 227 | set_pixel(x, y, gray, ret); 228 | } 229 | } 230 | return ret; 231 | } 232 | 233 | // Edges detection 234 | // See: http://en.wikipedia.org/wiki/Sobel_operator 235 | 236 | int mattrix_x[3][3] = 237 | {{-1, 0, 1}, 238 | {-2, 0, 2}, 239 | {-1, 0, 1}}; 240 | 241 | int mattrix_y[3][3] = 242 | {{-1, -2, -1}, 243 | { 0, 0, 0}, 244 | { 1, 2, 1}}; 245 | 246 | Canvas * 247 | detect_edges_canvas(Canvas * base, 248 | int num_threads) { 249 | 250 | Canvas * grayscaled_canv = grayscale_canvas(base, num_threads); 251 | 252 | const int w = base->w; 253 | const int h = base->h; 254 | Canvas * grad_canv = new_canvas(w, h); 255 | 256 | omp_set_num_threads((num_threads < 2) ? 1 : num_threads); 257 | 258 | int x; 259 | int y; 260 | #pragma omp parallel private(x, y) 261 | #pragma omp for collapse(2) schedule(dynamic, IMG_CHUNK) 262 | for(x = 1; x < w - 1; ++x) { 263 | for(y = 1; y < h - 1; ++y) { 264 | int i; 265 | int j; 266 | 267 | int gx = 0; 268 | for(i = -1; i < 2; ++i) { 269 | for(j = -1; j < 2; ++j) { 270 | gx += mattrix_x[i + 1][j + 1] * get_pixel(x + i, y + j, grayscaled_canv).r; 271 | } 272 | } 273 | 274 | int gy = 0; 275 | for(i = -1; i < 2; ++i) { 276 | for(j = -1; j < 2; ++j) { 277 | gy += mattrix_y[i + 1][j + 1] * get_pixel(x + i, y + j, grayscaled_canv).r; 278 | } 279 | } 280 | 281 | Byte grad = (Byte) sqrt(gx * gx + gy * gy); 282 | set_pixel(x, y, rgb(grad, grad, grad), grad_canv); 283 | } 284 | } 285 | 286 | release_canvas(grayscaled_canv); 287 | return grad_canv; 288 | } -------------------------------------------------------------------------------- /render/src/fog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | static inline Float 8 | exponential_fog_density(const Float distance, 9 | const void * fog_data); 10 | 11 | void 12 | set_no_fog(Scene * const scene) { 13 | if(scene->fog_parameters) { 14 | free(scene->fog_parameters); 15 | } 16 | scene->fog_density = NULL; 17 | } 18 | 19 | void 20 | set_exponential_fog(Scene * const scene, 21 | const Float k) { 22 | scene->fog_density = exponential_fog_density; 23 | 24 | Float * k_p = malloc(sizeof(Float)); 25 | *k_p = k; 26 | 27 | if(scene->fog_parameters) { 28 | free(scene->fog_parameters); 29 | } 30 | scene->fog_parameters = k_p; 31 | } 32 | 33 | static inline Float 34 | exponential_fog_density(const Float distance, 35 | const void * fog_data) { 36 | 37 | Float * k = (Float *) fog_data; 38 | return 1 - exp(- (*k) * distance); 39 | } -------------------------------------------------------------------------------- /render/src/kdtree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef MAX_TREE_DEPTH 12 | #define MAX_TREE_DEPTH 20 13 | #endif // MAX_TREE_DEPTH 14 | 15 | #define OBJECTS_IN_LEAF 1 16 | 17 | //#define MAX_SPLITS_OF_VOXEL 100 18 | #define MAX_SPLITS_OF_VOXEL 5 19 | 20 | #define SPLIT_COST 5 21 | 22 | #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 403 23 | # define __hot __attribute__((hot)) 24 | #else 25 | # define __hot 26 | #endif 27 | 28 | #ifdef RAY_INTERSECTIONS_STAT 29 | extern long 30 | intersections_per_ray; 31 | #endif // RAY_INTERSECTIONS_STAT 32 | 33 | // Declarations 34 | // -------------------------------------------------------------- 35 | 36 | Voxel 37 | make_initial_voxel(Object3d ** objects, 38 | int objects_count); 39 | 40 | inline KDNode * 41 | rec_build(Object3d ** objects, 42 | int objects_count, 43 | Voxel v, 44 | int iter); 45 | 46 | inline KDNode * 47 | make_leaf(Object3d ** objects, 48 | int objects_count); 49 | 50 | inline void 51 | find_plane(Object3d ** objects, 52 | const int objects_count, 53 | const Voxel v, 54 | const int tree_depth, 55 | enum Plane * const p, 56 | Coord * const c); 57 | 58 | inline int 59 | objects_in_voxel(Object3d ** objects, 60 | const int objects_count, 61 | const Voxel v); 62 | 63 | inline void 64 | split_voxel(const Voxel v, 65 | const enum Plane p, 66 | const Coord c, 67 | Voxel * const vl, 68 | Voxel * const vr); 69 | 70 | inline int 71 | filter_overlapped_objects(Object3d ** objects, 72 | const int objects_count, 73 | const Voxel v); 74 | 75 | inline Boolean 76 | vector_plane_intersection(const Vector3d vector, 77 | const Point3d vector_start, 78 | const enum Plane plane, 79 | const Coord coord, 80 | Point3d * const result); 81 | 82 | inline Boolean 83 | voxel_intersection(const Vector3d vector, 84 | const Point3d vector_start, 85 | const Voxel v); 86 | 87 | inline Boolean 88 | object_in_voxel(Object3d * const obj, 89 | const Voxel v); 90 | 91 | inline Boolean 92 | point_in_voxel(const Point3d p, 93 | const Voxel v); 94 | 95 | inline Boolean 96 | find_intersection_node(KDNode * const node, 97 | const Voxel v, 98 | const Point3d vector_start, 99 | const Vector3d vector, 100 | Object3d ** const nearest_obj_ptr, 101 | Point3d * const nearest_intersection_point_ptr, 102 | Float * const nearest_intersection_point_dist_ptr); 103 | 104 | inline Boolean 105 | is_intersect_anything_node(KDNode * const node, 106 | const Voxel v, 107 | const Point3d vector_start, 108 | const Vector3d vector); 109 | 110 | void 111 | release_kd_node(KDNode * node); 112 | 113 | 114 | // Code 115 | // -------------------------------------------------------------- 116 | 117 | inline Boolean 118 | point_in_voxel(const Point3d p, 119 | const Voxel v) { 120 | 121 | return ((p.x > v.x_min) && (p.x < v.x_max) && 122 | (p.y > v.y_min) && (p.y < v.y_max) && 123 | (p.z > v.z_min) && (p.z < v.z_max)); 124 | } 125 | 126 | 127 | void 128 | release_kd_tree(KDTree * tree) { 129 | release_kd_node(tree->root); 130 | free(tree); 131 | } 132 | 133 | void 134 | release_kd_node(KDNode * node) { 135 | if(node->l) 136 | release_kd_node(node->l); 137 | if(node->r) 138 | release_kd_node(node->r); 139 | if(node->objects) 140 | free(node->objects); 141 | free(node); 142 | } 143 | 144 | KDTree * 145 | build_kd_tree(Object3d ** objects, 146 | int objects_count) { 147 | 148 | KDTree * tree = malloc(sizeof(KDTree)); 149 | tree->bounding_box = make_initial_voxel(objects, objects_count); 150 | tree->root = rec_build(objects, objects_count, tree->bounding_box, 0); 151 | return tree; 152 | } 153 | 154 | inline KDNode * 155 | rec_build(Object3d ** objects, 156 | int objects_count, 157 | Voxel v, 158 | int iter) { 159 | 160 | enum Plane p; 161 | Coord c; 162 | find_plane(objects, objects_count, v, iter, &p, &c); 163 | 164 | if(p == NONE) { 165 | return make_leaf(objects, objects_count); 166 | } 167 | 168 | Voxel vl; 169 | Voxel vr; 170 | split_voxel(v, p, c, &vl, &vr); 171 | 172 | int l_objects_count = filter_overlapped_objects(objects, objects_count, vl); 173 | KDNode * l = rec_build(objects, l_objects_count, vl, iter + 1); 174 | 175 | int r_objects_count = filter_overlapped_objects(objects, objects_count, vr); 176 | KDNode * r = rec_build(objects, r_objects_count, vr, iter + 1); 177 | 178 | 179 | KDNode * node = malloc(sizeof(KDNode)); 180 | node->objects = NULL; 181 | node->objects_count = 0; 182 | node->plane = p; 183 | node->coord = c; 184 | node->l = l; 185 | node->r = r; 186 | 187 | return node; 188 | } 189 | 190 | inline int 191 | filter_overlapped_objects(Object3d ** objects, 192 | const int objects_count, 193 | const Voxel v) { 194 | 195 | int i = 0; 196 | int j = objects_count - 1; 197 | 198 | Object3d * tmp; 199 | 200 | // Put all objects, which overlap with voxel to the left part of array 201 | while(i <= j) { 202 | while((i < j) && (object_in_voxel(objects[i], v))) 203 | i++; 204 | 205 | while((j > i) && (!object_in_voxel(objects[j], v))) 206 | j--; 207 | 208 | tmp = objects[i]; 209 | objects[i] = objects[j]; 210 | objects[j] = tmp; 211 | i++; 212 | j--; 213 | } 214 | 215 | // Actually, after the loop, variable 'i' is a count of overlapped objects 216 | return i; 217 | } 218 | 219 | inline void 220 | split_voxel(const Voxel v, 221 | const enum Plane p, 222 | const Coord c, 223 | Voxel * const vl, 224 | Voxel * const vr) { 225 | 226 | *vl = v; 227 | *vr = v; 228 | 229 | switch(p) { 230 | case XY: 231 | vl->z_max = c.z; 232 | vr->z_min = c.z; 233 | break; 234 | 235 | case XZ: 236 | vl->y_max = c.y; 237 | vr->y_min = c.y; 238 | break; 239 | 240 | case YZ: 241 | vl->x_max = c.x; 242 | vr->x_min = c.x; 243 | break; 244 | case NONE: 245 | // Unreachable case 246 | printf("[split_voxel] Plane is NONE. Error"); 247 | exit(1); 248 | break; 249 | } 250 | } 251 | 252 | /* 253 | * Using Surface Area Heuristic (SAH) for finding best split pane 254 | * 255 | * SAH = 0.5 * voxel_surface_area * number_of_objects_in_voxel 256 | * 257 | * splitted_SAH = split_cost 258 | * + 0.5 * left_voxel_surface_area * number_of_objects_in_left_voxel 259 | * + 0.5 * right_voxel_surface_area * number_of_objects_in_right_voxel 260 | * 261 | * Finding coordinate of split plane (XY, XZ or YZ) which minimizing SAH 262 | * 263 | * If can't find optimal split plane - returns NONE 264 | * 265 | * see: http://stackoverflow.com/a/4633332/653511 266 | */ 267 | inline void 268 | find_plane(Object3d ** objects, 269 | const int objects_count, 270 | const Voxel v, 271 | const int tree_depth, 272 | enum Plane * const p, 273 | Coord * const c) { 274 | 275 | if((tree_depth >= MAX_TREE_DEPTH) || (objects_count <= OBJECTS_IN_LEAF)) { 276 | *p = NONE; 277 | return; 278 | } 279 | 280 | const Float hx = v.x_max - v.x_min; 281 | const Float hy = v.y_max - v.y_min; 282 | const Float hz = v.z_max - v.z_min; 283 | 284 | // Calculating square of each side of initial voxel 285 | Float Sxy = hx * hy; 286 | Float Sxz = hx * hz; 287 | Float Syz = hy * hz; 288 | 289 | const Float Ssum = Sxy + Sxz + Syz; 290 | 291 | // Let's normalize square of each side of initial voxel 292 | // to satisfy the following relationship: 293 | // Sxy + Sxz + Syz = 1 294 | Sxy /= Ssum; 295 | Sxz /= Ssum; 296 | Syz /= Ssum; 297 | 298 | const int max_splits = MAX_SPLITS_OF_VOXEL; 299 | const Float split_cost = SPLIT_COST; 300 | 301 | // Assume that at the beginning best SAH has initial voxel 302 | // SAH = 0.5 * square * objects_count 303 | // square of initial voxel is Sxy + Sxz + Syz = 1 304 | Float bestSAH = objects_count; 305 | // initial voxel doesn't have split pane 306 | *p = NONE; 307 | 308 | Float currSAH; 309 | Coord curr_split_coord; 310 | int i; 311 | Voxel vl; 312 | Voxel vr; 313 | Float l; 314 | Float r; 315 | 316 | Float S_split; 317 | Float S_non_split; 318 | 319 | // Let's find split surface, which have the least SAH 320 | 321 | // TODO: maybe do some refactoring (because of following 3 loops are very similar) 322 | 323 | // trying to minimize SAH by splitting across XY plane 324 | S_split = Sxy; 325 | S_non_split = Sxz + Syz; 326 | for(i = 1; i < max_splits; i++) { 327 | 328 | l = ((float) i) / max_splits; 329 | r = 1 - l; 330 | 331 | // Current coordinate of split surface 332 | curr_split_coord.z = v.z_min + l * hz; 333 | 334 | split_voxel(v, XY, curr_split_coord, &vl, &vr); 335 | 336 | currSAH = (S_split + l * S_non_split) * objects_in_voxel(objects, objects_count, vl) 337 | + (S_split + r * S_non_split) * objects_in_voxel(objects, objects_count, vr) 338 | + split_cost; 339 | 340 | if(currSAH < bestSAH) { 341 | bestSAH = currSAH; 342 | *p = XY; 343 | *c = curr_split_coord; 344 | } 345 | } 346 | 347 | // trying to minimize SAH by splitting across XZ plane 348 | S_split = Sxz; 349 | S_non_split = Sxy + Syz; 350 | for(i = 1; i < max_splits; i++) { 351 | 352 | l = ((float) i) / max_splits; 353 | r = 1 - l; 354 | 355 | // Current coordinate of split surface 356 | curr_split_coord.y = v.y_min + l * hy; 357 | 358 | split_voxel(v, XZ, curr_split_coord, &vl, &vr); 359 | 360 | currSAH = (S_split + l * S_non_split) * objects_in_voxel(objects, objects_count, vl) 361 | + (S_split + r * S_non_split) * objects_in_voxel(objects, objects_count, vr) 362 | + split_cost; 363 | 364 | if(currSAH < bestSAH) { 365 | bestSAH = currSAH; 366 | *p = XZ; 367 | *c = curr_split_coord; 368 | } 369 | } 370 | 371 | // trying to minimize SAH by splitting across YZ plane 372 | S_split = Syz; 373 | S_non_split = Sxy + Sxz; 374 | for(i = 1; i < max_splits; i++) { 375 | 376 | l = ((float) i) / max_splits; 377 | r = 1 - l; 378 | 379 | // Current coordinate of split surface 380 | curr_split_coord.x = v.x_min + l * hx; 381 | 382 | split_voxel(v, YZ, curr_split_coord, &vl, &vr); 383 | 384 | currSAH = (S_split + l * S_non_split) * objects_in_voxel(objects, objects_count, vl) 385 | + (S_split + r * S_non_split) * objects_in_voxel(objects, objects_count, vr) 386 | + split_cost; 387 | 388 | if(currSAH < bestSAH) { 389 | bestSAH = currSAH; 390 | *p = YZ; 391 | *c = curr_split_coord; 392 | } 393 | } 394 | } 395 | 396 | inline int 397 | objects_in_voxel(Object3d ** objects, 398 | const int objects_count, 399 | const Voxel v) { 400 | 401 | int i; 402 | int count = 0; 403 | for(i = 0; i < objects_count; i++) 404 | if(object_in_voxel(objects[i], v)) 405 | ++count; 406 | 407 | return count; 408 | } 409 | 410 | Voxel 411 | make_initial_voxel(Object3d ** objects, 412 | int objects_count) { 413 | 414 | if(!objects_count) { 415 | Voxel v = {-1, -1, -1, 1, 1, 1}; 416 | return v; 417 | } 418 | 419 | Point3d min_p = objects[0]->get_min_boundary_point(objects[0]->data); 420 | Point3d max_p = objects[0]->get_max_boundary_point(objects[0]->data); 421 | 422 | Float x_min = min_p.x; 423 | Float y_min = min_p.y; 424 | Float z_min = min_p.z; 425 | 426 | Float x_max = max_p.x; 427 | Float y_max = max_p.y; 428 | Float z_max = max_p.z; 429 | 430 | int i; 431 | for(i = 0; i < objects_count; i++) { 432 | min_p = objects[i]->get_min_boundary_point(objects[i]->data); 433 | max_p = objects[i]->get_max_boundary_point(objects[i]->data); 434 | 435 | x_min = (x_min < min_p.x) ? x_min : min_p.x; 436 | y_min = (y_min < min_p.y) ? y_min : min_p.y; 437 | z_min = (z_min < min_p.z) ? z_min : min_p.z; 438 | 439 | x_max = (x_max > max_p.x) ? x_max : max_p.x; 440 | y_max = (y_max > max_p.y) ? y_max : max_p.y; 441 | z_max = (z_max > max_p.z) ? z_max : max_p.z; 442 | } 443 | 444 | Voxel v = {x_min - 1, y_min - 1, z_min - 1, x_max + 1, y_max + 1, z_max + 1}; 445 | return v; 446 | } 447 | 448 | 449 | inline __hot Boolean 450 | object_in_voxel(Object3d * const obj, 451 | const Voxel v) { 452 | 453 | Point3d min_p = obj->get_min_boundary_point(obj->data); 454 | Point3d max_p = obj->get_max_boundary_point(obj->data); 455 | 456 | return 457 | !((max_p.x < v.x_min) 458 | || (max_p.y < v.y_min) 459 | || (max_p.z < v.z_min) 460 | || (min_p.x > v.x_max) 461 | || (min_p.y > v.y_max) 462 | || (min_p.z > v.z_max)); 463 | } 464 | 465 | inline KDNode * 466 | make_leaf(Object3d ** objects, 467 | int objects_count) { 468 | 469 | KDNode * leaf = malloc(sizeof(KDNode)); 470 | leaf->plane = NONE; 471 | leaf->objects_count = objects_count; 472 | leaf->l = NULL; 473 | leaf->r = NULL; 474 | if(objects_count) { 475 | leaf->objects = (Object3d **) malloc(objects_count * sizeof(Object3d *)); 476 | } else { 477 | leaf->objects = NULL; 478 | } 479 | 480 | memcpy(leaf->objects, objects, objects_count * sizeof(Object3d *)); 481 | 482 | return leaf; 483 | } 484 | 485 | inline __hot Boolean 486 | vector_plane_intersection(const Vector3d vector, 487 | const Point3d vector_start, 488 | const enum Plane plane, 489 | const Coord coord, 490 | Point3d * const result) { 491 | 492 | Float k; 493 | switch(plane) { 494 | case XY: 495 | if(((coord.z < vector_start.z) && (vector.z > 0)) 496 | || ((coord.z > vector_start.z) && (vector.z < 0))) 497 | return False; 498 | 499 | k = (coord.z - vector_start.z) / vector.z; 500 | *result = point3d(vector_start.x + vector.x * k, 501 | vector_start.y + vector.y * k, 502 | coord.z); 503 | break; 504 | 505 | case XZ: 506 | if(((coord.y < vector_start.y) && (vector.y > 0)) 507 | || ((coord.y > vector_start.y) && (vector.y < 0))) 508 | return False; 509 | 510 | k = (coord.y - vector_start.y) / vector.y; 511 | *result = point3d(vector_start.x + vector.x * k, 512 | coord.y, 513 | vector_start.z + vector.z * k); 514 | break; 515 | 516 | case YZ: 517 | if(((coord.x < vector_start.x) && (vector.x > 0)) 518 | || ((coord.x > vector_start.x) && (vector.x < 0))) 519 | return False; 520 | 521 | k = (coord.x - vector_start.x) / vector.x; 522 | *result = point3d(coord.x, 523 | vector_start.y + vector.y * k, 524 | vector_start.z + vector.z * k); 525 | break; 526 | 527 | case NONE: 528 | // Unreachable case 529 | printf("[vector_plane_intersection] Plane is NONE. Error"); 530 | exit(1); 531 | break; 532 | } 533 | 534 | return True; 535 | } 536 | 537 | inline Boolean 538 | voxel_intersection(const Vector3d vector, 539 | const Point3d vector_start, 540 | const Voxel v) { 541 | 542 | if(point_in_voxel(vector_start, v)) 543 | return True; 544 | 545 | Point3d p; 546 | Coord c; 547 | 548 | c.z = v.z_min; 549 | if(vector_plane_intersection(vector, vector_start, XY, c, &p) 550 | && (p.x > v.x_min) && (p.x < v.x_max) 551 | && (p.y > v.y_min) && (p.y < v.y_max)) { 552 | 553 | return True; 554 | } 555 | 556 | c.z = v.z_max; 557 | if(vector_plane_intersection(vector, vector_start, XY, c, &p) 558 | && (p.x > v.x_min) && (p.x < v.x_max) 559 | && (p.y > v.y_min) && (p.y < v.y_max)) { 560 | 561 | return True; 562 | } 563 | 564 | c.y = v.y_min; 565 | if(vector_plane_intersection(vector, vector_start, XZ, c, &p) 566 | && (p.x > v.x_min) && (p.x < v.x_max) 567 | && (p.z > v.z_min) && (p.z < v.z_max)) { 568 | 569 | return True; 570 | } 571 | 572 | c.y = v.y_max; 573 | if(vector_plane_intersection(vector, vector_start, XZ, c, &p) 574 | && (p.x > v.x_min) && (p.x < v.x_max) 575 | && (p.z > v.z_min) && (p.z < v.z_max)) { 576 | 577 | return True; 578 | } 579 | 580 | c.x = v.x_min; 581 | if(vector_plane_intersection(vector, vector_start, YZ, c, &p) 582 | && (p.y > v.y_min) && (p.y < v.y_max) 583 | && (p.z > v.z_min) && (p.z < v.z_max)) { 584 | 585 | return True; 586 | } 587 | 588 | c.x = v.x_max; 589 | if(vector_plane_intersection(vector, vector_start, YZ, c, &p) 590 | && (p.y > v.y_min) && (p.y < v.y_max) 591 | && (p.z > v.z_min) && (p.z < v.z_max)) { 592 | 593 | return True; 594 | } 595 | 596 | return False; 597 | } 598 | 599 | Boolean 600 | find_intersection_tree(KDTree * const tree, 601 | const Point3d vector_start, 602 | const Vector3d vector, 603 | Object3d ** const nearest_obj_ptr, 604 | Point3d * const nearest_intersection_point_ptr, 605 | Float * const nearest_intersection_point_dist_ptr) { 606 | 607 | #ifndef NO_BOUNDING_BOX 608 | return (voxel_intersection(vector, vector_start, tree->bounding_box) 609 | && find_intersection_node(tree->root, 610 | tree->bounding_box, 611 | vector_start, 612 | vector, 613 | nearest_obj_ptr, 614 | nearest_intersection_point_ptr, 615 | nearest_intersection_point_dist_ptr)); 616 | #else 617 | // Do not take into account scene bounds 618 | return find_intersection_node(tree->root, 619 | tree->bounding_box, 620 | vector_start, 621 | vector, 622 | nearest_obj_ptr, 623 | nearest_intersection_point_ptr, 624 | nearest_intersection_point_dist_ptr); 625 | #endif // NO_BOUNDING_BOX 626 | } 627 | 628 | inline Boolean 629 | find_intersection_node(KDNode * const node, 630 | const Voxel v, 631 | const Point3d vector_start, 632 | const Vector3d vector, 633 | Object3d ** const nearest_obj_ptr, 634 | Point3d * const nearest_intersection_point_ptr, 635 | Float * const nearest_intersection_point_dist_ptr) { 636 | 637 | // Is leaf 638 | if(node->plane == NONE) { 639 | if((node->objects_count) && (node->objects)) { 640 | int i; 641 | 642 | Object3d * obj = NULL; 643 | Point3d intersection_point; 644 | Float sqr_curr_dist; 645 | 646 | Object3d * nearest_obj = NULL; 647 | Point3d nearest_intersection_point; 648 | Float sqr_nearest_dist; 649 | 650 | int intersected = False; 651 | 652 | // Finding nearest object 653 | // and intersection point 654 | for(i = 0; i < node->objects_count; i++) { 655 | if(node->objects[i]) { 656 | obj = node->objects[i]; 657 | 658 | #ifdef RAY_INTERSECTIONS_STAT 659 | ++intersections_per_ray; 660 | #endif // RAY_INTERSECTIONS_STAT 661 | 662 | if((obj->intersect(obj->data, vector_start, vector, &intersection_point)) 663 | && (point_in_voxel(intersection_point, v))) { 664 | 665 | sqr_curr_dist = sqr_module_vector(vector3dp(vector_start, intersection_point)); 666 | 667 | if((sqr_curr_dist < sqr_nearest_dist) || (!intersected)) { 668 | 669 | nearest_obj = obj; 670 | nearest_intersection_point = intersection_point; 671 | sqr_nearest_dist = sqr_curr_dist; 672 | intersected = True; 673 | } 674 | } 675 | } 676 | } 677 | 678 | if(intersected) { 679 | Float nearest_dist = sqrt(sqr_nearest_dist); 680 | 681 | if(nearest_dist < *nearest_intersection_point_dist_ptr) { 682 | *nearest_intersection_point_dist_ptr = nearest_dist; 683 | *nearest_obj_ptr = nearest_obj; 684 | *nearest_intersection_point_ptr = nearest_intersection_point; 685 | } 686 | } 687 | 688 | return intersected; 689 | } 690 | return False; 691 | } 692 | 693 | // Otherwise 694 | 695 | Voxel front_voxel; 696 | Voxel back_voxel; 697 | 698 | KDNode * front_node; 699 | KDNode * back_node; 700 | 701 | switch(node->plane) { 702 | case XY: 703 | if(((node->coord.z > v.z_min) && (node->coord.z > vector_start.z)) 704 | || ((node->coord.z < v.z_min) && (node->coord.z < vector_start.z))) { 705 | 706 | front_node = node->l; 707 | back_node = node->r; 708 | split_voxel(v, node->plane, node->coord, &front_voxel, &back_voxel); 709 | } else { 710 | front_node = node->r; 711 | back_node = node->l; 712 | split_voxel(v, node->plane, node->coord, &back_voxel, &front_voxel); 713 | } 714 | break; 715 | 716 | case XZ: 717 | if(((node->coord.y > v.y_min) && (node->coord.y > vector_start.y)) 718 | || ((node->coord.y < v.y_min) && (node->coord.y < vector_start.y))) { 719 | 720 | front_node = node->l; 721 | back_node = node->r; 722 | split_voxel(v, node->plane, node->coord, &front_voxel, &back_voxel); 723 | } else { 724 | front_node = node->r; 725 | back_node = node->l; 726 | split_voxel(v, node->plane, node->coord, &back_voxel, &front_voxel); 727 | } 728 | break; 729 | 730 | case YZ: 731 | if(((node->coord.x > v.x_min) && (node->coord.x > vector_start.x)) 732 | || ((node->coord.x < v.x_min) && (node->coord.x < vector_start.x))) { 733 | 734 | front_node = node->l; 735 | back_node = node->r; 736 | split_voxel(v, node->plane, node->coord, &front_voxel, &back_voxel); 737 | } else { 738 | front_node = node->r; 739 | back_node = node->l; 740 | split_voxel(v, node->plane, node->coord, &back_voxel, &front_voxel); 741 | } 742 | break; 743 | 744 | case NONE: 745 | // Unreachable case 746 | printf("[find_intersection_node] Plane is NONE. Error"); 747 | exit(1); 748 | break; 749 | } 750 | 751 | if(voxel_intersection(vector, vector_start, front_voxel) 752 | && find_intersection_node(front_node, 753 | front_voxel, 754 | vector_start, 755 | vector, 756 | nearest_obj_ptr, 757 | nearest_intersection_point_ptr, 758 | nearest_intersection_point_dist_ptr)) 759 | return True; 760 | 761 | return (voxel_intersection(vector, vector_start, back_voxel) 762 | && find_intersection_node(back_node, 763 | back_voxel, 764 | vector_start, 765 | vector, 766 | nearest_obj_ptr, 767 | nearest_intersection_point_ptr, 768 | nearest_intersection_point_dist_ptr)); 769 | } -------------------------------------------------------------------------------- /render/src/obj_loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define MAX_VERTEX_COUNT 150000 11 | 12 | void 13 | parse_vertex(const char * str, 14 | Point3d * v); 15 | 16 | void 17 | parse_norm_vector(const char * str, 18 | Vector3d * v); 19 | 20 | void 21 | parse_face(char * str, 22 | Point3d v[], 23 | Vector3d vn[], 24 | void (* face_handler)(Queue * vertexes, 25 | Queue * norm_vectors, 26 | void * args), 27 | void * args); 28 | 29 | void 30 | parse_face_str(char * str, 31 | int * v_index, 32 | int * vt_index, 33 | int * vn_index); 34 | 35 | static Point3d vertexes[MAX_VERTEX_COUNT]; 36 | static Vector3d norm_vectors[MAX_VERTEX_COUNT]; 37 | 38 | // TODO: use LinkedList instead of arrays 39 | 40 | void 41 | load_obj(const char * filename, 42 | void (* face_handler)(Queue * vertexes, 43 | Queue * norm_vectors, 44 | void * args), 45 | void * args) { 46 | 47 | int vertexes_cnt = 0; 48 | int norm_vectors_cnt = 0; 49 | 50 | FILE * fp = fopen(filename, "r"); 51 | 52 | char * line = NULL; 53 | size_t len = 0; 54 | ssize_t read = 0; 55 | 56 | while ((read = getline(&line, &len, fp)) > 0) { 57 | 58 | if((line[0] != 'v') && (line[0] != 'f')) 59 | continue; 60 | 61 | if((line[0] == 'v') && (line[1] == ' ')) 62 | parse_vertex(&line[2], &vertexes[vertexes_cnt++]); 63 | 64 | if((line[0] == 'v') && (line[1] == 'n')) 65 | parse_norm_vector(&line[3], &norm_vectors[norm_vectors_cnt++]); 66 | 67 | if((line[0] == 'f') && (line[1] == ' ')) 68 | parse_face(&line[2], vertexes, norm_vectors, face_handler, args); 69 | } 70 | 71 | if (line) 72 | free(line); 73 | } 74 | 75 | void 76 | parse_vertex(const char * str, 77 | Point3d * v) { 78 | sscanf(str, "%lf %lf %lf", &v->y, &v->z, &v->x); 79 | } 80 | 81 | void 82 | parse_norm_vector(const char * str, 83 | Vector3d * v) { 84 | sscanf(str, "%lf %lf %lf", &v->y, &v->z, &v->x); 85 | } 86 | 87 | void 88 | parse_face(char * str, 89 | Point3d v[], 90 | Vector3d vn[], 91 | void (* face_handler)(Queue * vertexes, 92 | Queue * norm_vectors, 93 | void * args), 94 | void * args) { 95 | 96 | Queue * tokens = new_queue(); 97 | 98 | char * token = NULL; 99 | token = strtok(str, " \n"); 100 | while(token) { 101 | add(token, tokens); 102 | token = strtok(NULL, " \n"); 103 | } 104 | 105 | Queue * vertexes = new_queue(); 106 | Queue * norm_vectors = new_queue(); 107 | 108 | int vertex_index = 0; 109 | int texture_index = 0; 110 | int norm_index = 0; 111 | while(!is_empty(tokens)) { 112 | token = (char *) get(tokens); 113 | 114 | parse_face_str(token, &vertex_index, &texture_index, &norm_index); 115 | 116 | add(&v[vertex_index - 1], vertexes); 117 | 118 | if(norm_index > 0) 119 | add(&vn[norm_index - 1], norm_vectors); 120 | } 121 | 122 | face_handler(vertexes, norm_vectors, args); 123 | 124 | release_queue(tokens); 125 | release_queue(vertexes); 126 | release_queue(norm_vectors); 127 | } 128 | 129 | void 130 | parse_face_str(char * str, 131 | int * v_index, 132 | int * vt_index, 133 | int * vn_index) { 134 | 135 | int str_len = strlen(str); 136 | 137 | int i = 0; 138 | while((str[i] != '/') 139 | && (str[i] != ' ') 140 | && (str[i] != '\0')) 141 | i++; 142 | str[i] = '\0'; 143 | 144 | if(strlen(str) > 0) 145 | *v_index = atoi(str); 146 | 147 | i++; 148 | if(i >= str_len) 149 | return; 150 | 151 | str += i; 152 | str_len = strlen(str); 153 | i = 0; 154 | while((str[i] != '/') 155 | && (str[i] != ' ') 156 | && (str[i] != '\0')) 157 | i++; 158 | str[i] = '\0'; 159 | 160 | if(strlen(str) > 0) 161 | *vt_index = atoi(str); 162 | 163 | i++; 164 | if(i >= str_len) 165 | return; 166 | 167 | str += i; 168 | str_len = strlen(str); 169 | i = 0; 170 | while((str[i] != '/') 171 | && (str[i] != ' ') 172 | && (str[i] != '\0')) 173 | i++; 174 | str[i] = '\0'; 175 | 176 | if(strlen(str) > 0) 177 | *vn_index = atoi(str); 178 | } 179 | 180 | void 181 | scene_face_handler(Queue * vertexes, 182 | Queue * norm_vectors, 183 | void * arg) { 184 | SceneFaceHandlerParams * params = (SceneFaceHandlerParams *) arg; 185 | 186 | Scene * scene = params->scene; 187 | Float scale = params->scale; 188 | Float dx = params->dx; 189 | Float dy = params->dy; 190 | Float dz = params->dz; 191 | 192 | Float sin_al_x = params->sin_al_x; 193 | Float cos_al_x = params->cos_al_x; 194 | 195 | Float sin_al_y = params->sin_al_y; 196 | Float cos_al_y = params->cos_al_y; 197 | 198 | Float sin_al_z = params->sin_al_z; 199 | Float cos_al_z = params->cos_al_z; 200 | 201 | Color default_color = params->default_color; 202 | Material default_material = params->default_material; 203 | 204 | Point3d * p_p1 = (Point3d *) get(vertexes); 205 | Point3d * p_p2 = (Point3d *) get(vertexes); 206 | Point3d * p_p3 = NULL; 207 | 208 | Vector3d * p_v1 = (Vector3d *) get(norm_vectors); 209 | Vector3d * p_v2 = (Vector3d *) get(norm_vectors); 210 | Vector3d * p_v3 = NULL; 211 | 212 | Point3d p1; 213 | Point3d p2; 214 | Point3d p3; 215 | 216 | Vector3d v1; 217 | Vector3d v2; 218 | Vector3d v3; 219 | 220 | while(!is_empty(vertexes)) { 221 | p_p3 = (Point3d *) get(vertexes); 222 | p_v3 = (Vector3d *) get(norm_vectors); 223 | 224 | p1 = rotate_point_x(*p_p1, sin_al_x, cos_al_x); 225 | p1 = rotate_point_y(p1, sin_al_y, cos_al_y); 226 | p1 = rotate_point_z(p1, sin_al_z, cos_al_z); 227 | 228 | p2 = rotate_point_x(*p_p2, sin_al_x, cos_al_x); 229 | p2 = rotate_point_y(p2, sin_al_y, cos_al_y); 230 | p2 = rotate_point_z(p2, sin_al_z, cos_al_z); 231 | 232 | p3 = rotate_point_x(*p_p3, sin_al_x, cos_al_x); 233 | p3 = rotate_point_y(p3, sin_al_y, cos_al_y); 234 | p3 = rotate_point_z(p3, sin_al_z, cos_al_z); 235 | 236 | if(p_v1 && p_v2 && p_v3) { 237 | 238 | v1 = rotate_vector_x(*p_v1, sin_al_x, cos_al_x); 239 | v1 = rotate_vector_y(v1, sin_al_y, cos_al_y); 240 | v1 = rotate_vector_z(v1, sin_al_z, cos_al_z); 241 | 242 | v2 = rotate_vector_x(*p_v2, sin_al_x, cos_al_x); 243 | v2 = rotate_vector_y(v2, sin_al_y, cos_al_y); 244 | v2 = rotate_vector_z(v2, sin_al_z, cos_al_z); 245 | 246 | v3 = rotate_vector_x(*p_v3, sin_al_x, cos_al_x); 247 | v3 = rotate_vector_y(v3, sin_al_y, cos_al_y); 248 | v3 = rotate_vector_z(v3, sin_al_z, cos_al_z); 249 | 250 | add_object(scene, new_triangle_with_norms( 251 | point3d(p1.x * scale + dx, p1.y * scale + dy, p1.z * scale + dz), 252 | point3d(p2.x * scale + dx, p2.y * scale + dy, p2.z * scale + dz), 253 | point3d(p3.x * scale + dx, p3.y * scale + dy, p3.z * scale + dz), 254 | v1, 255 | v2, 256 | v3, 257 | default_color, 258 | default_material)); 259 | } else { 260 | add_object(scene, new_triangle( 261 | point3d(p1.x * scale + dx, p1.y * scale + dy, p1.z * scale + dz), 262 | point3d(p2.x * scale + dx, p2.y * scale + dy, p2.z * scale + dz), 263 | point3d(p3.x * scale + dx, p3.y * scale + dy, p3.z * scale + dz), 264 | default_color, 265 | default_material)); 266 | } 267 | 268 | p_p2 = p_p3; 269 | p_v2 = p_v3; 270 | } 271 | } -------------------------------------------------------------------------------- /render/src/render.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define ANTIALIASING 1 9 | 10 | #include 11 | 12 | #define CHUNK 10 13 | 14 | /* collapse is a feature from OpenMP 3 (2008) */ 15 | #if _OPENMP < 200805 16 | #define collapse(x) 17 | #endif 18 | 19 | #ifdef RAY_INTERSECTIONS_STAT 20 | extern long 21 | intersections_per_ray; 22 | #endif // RAY_INTERSECTIONS_STAT 23 | 24 | #include 25 | 26 | 27 | void 28 | render_scene(const Scene * const scene, 29 | const Camera * const camera, 30 | Canvas * canvas, 31 | const int num_threads) { 32 | 33 | const int w = canvas->w; 34 | const int h = canvas->h; 35 | const Float dx = w / 2.0; 36 | const Float dy = h / 2.0; 37 | const Float focus = camera->proj_plane_dist; 38 | 39 | // TODO: consider possibility to define these OpenMP parameters 40 | // in declarative style (using directives of preprocessor) 41 | omp_set_num_threads((num_threads < 2) ? 1 : num_threads); 42 | 43 | #ifdef RAY_INTERSECTIONS_STAT 44 | // intersections_per_ray is not atomic variable 45 | // avoid multithreaded rendering to prevent from race-conditions 46 | // in case of incrementing this variable 47 | omp_set_num_threads(1); 48 | intersections_per_ray = 0; 49 | #endif // RAY_INTERSECTIONS_STAT 50 | 51 | int i; 52 | int j; 53 | #pragma omp parallel private(i, j) 54 | #pragma omp for collapse(2) schedule(dynamic, CHUNK) 55 | for(i = 0; i < w; i++) { 56 | for(j = 0; j < h; j++) { 57 | const Float x = i - dx; 58 | const Float y = j - dy; 59 | const Vector3d ray = vector3df(x, y, focus); 60 | const Color col = trace(scene, camera, ray); 61 | set_pixel(i, j, col, canvas); 62 | } 63 | } 64 | 65 | // TODO: argument of the function? global variable? 66 | const int antialiasing = ANTIALIASING; 67 | 68 | if(antialiasing) { 69 | Canvas * edges = detect_edges_canvas(canvas, num_threads); 70 | #pragma omp parallel private(i, j) 71 | #pragma omp for collapse(2) schedule(dynamic, CHUNK) 72 | for(i = 1; i < w - 1; i++) { 73 | for(j = 1; j < h - 1; j++) { 74 | // edges canvas is grayscaled 75 | // it means that color components (r, g, b) are equal 76 | Byte gray = get_pixel(i, j, edges).r; 77 | 78 | // TODO: improve 79 | if(gray > 10) { 80 | const Float x = i - dx; 81 | const Float y = j - dy; 82 | 83 | Color c = get_pixel(i, j, canvas); 84 | 85 | const Float weight = 1.0 / 4; 86 | c = mul_color(c, weight); 87 | c = add_colors(c, mul_color(trace(scene, camera, vector3df(x + 0.5, y, focus)), weight)); 88 | c = add_colors(c, mul_color(trace(scene, camera, vector3df(x, y + 0.5, focus)), weight)); 89 | c = add_colors(c, mul_color(trace(scene, camera, vector3df(x + 0.5, y + 0.5, focus)), weight)); 90 | 91 | set_pixel(i, j, c, canvas); 92 | } 93 | } 94 | } 95 | release_canvas(edges); 96 | } 97 | 98 | #ifdef RAY_INTERSECTIONS_STAT 99 | intersections_per_ray /= (w * h); 100 | printf("Average intersections number per pixel: %li\n", intersections_per_ray); 101 | #endif // RAY_INTERSECTIONS_STAT 102 | } -------------------------------------------------------------------------------- /render/src/scene.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Declarations 12 | // -------------------------------------------------------------- 13 | 14 | static inline void 15 | rebuild_kd_tree(Scene * scene); 16 | 17 | // Code 18 | // -------------------------------------------------------------- 19 | 20 | Scene * 21 | new_scene(const int objects_count, 22 | const int light_sources_count, 23 | const Color background_color) { 24 | 25 | Scene * s = malloc(sizeof(Scene)); 26 | s->objects_count=objects_count; 27 | s->objects = calloc(objects_count, sizeof(Object3d *)); 28 | if(light_sources_count) { 29 | s->light_sources = calloc(light_sources_count, sizeof(LightSource3d *)); 30 | } 31 | s->light_sources_count = light_sources_count; 32 | s->background_color = background_color; 33 | s->last_object_index = -1; 34 | s->last_light_source_index = -1; 35 | s->fog_parameters = NULL; 36 | s->fog_density = NULL; 37 | 38 | s->kd_tree = NULL; 39 | return s; 40 | } 41 | 42 | void 43 | release_scene(Scene * scene) { 44 | int i; 45 | 46 | for(i = 0; i < scene->objects_count; i++) { 47 | if(scene->objects[i]) { 48 | release_object3d(scene->objects[i]); 49 | } 50 | } 51 | 52 | for(i = 0; i < scene->light_sources_count; i++) { 53 | if(scene->light_sources[i]) { 54 | free(scene->light_sources[i]); 55 | } 56 | } 57 | 58 | free(scene->objects); 59 | free(scene->light_sources); 60 | 61 | if(scene->fog_parameters) { 62 | free(scene->fog_parameters); 63 | } 64 | 65 | if(scene->kd_tree) 66 | release_kd_tree(scene->kd_tree); 67 | free(scene); 68 | } 69 | 70 | void 71 | add_object(Scene * const scene, 72 | Object3d * const object) { 73 | 74 | scene->objects[++scene->last_object_index] = object; 75 | } 76 | 77 | void 78 | prepare_scene(Scene * const scene) { 79 | rebuild_kd_tree(scene); 80 | } 81 | 82 | void 83 | add_light_source(Scene * const scene, 84 | LightSource3d * const light_source) { 85 | 86 | scene->light_sources[++scene->last_light_source_index] = light_source; 87 | } 88 | 89 | static inline void 90 | rebuild_kd_tree(Scene * scene) { 91 | if(scene->kd_tree) 92 | release_kd_tree(scene->kd_tree); 93 | scene->kd_tree = build_kd_tree(scene->objects, scene->last_object_index + 1); 94 | } 95 | 96 | void 97 | release_object3d(Object3d * obj) { 98 | obj->release_data(obj->data); 99 | free(obj); 100 | } 101 | 102 | LightSource3d * 103 | new_light_source(const Point3d location, 104 | const Color color) { 105 | 106 | LightSource3d * ls_p = malloc(sizeof(LightSource3d)); 107 | 108 | ls_p->location_world = location; 109 | ls_p->location = location; 110 | ls_p->color = color; 111 | 112 | return ls_p; 113 | } 114 | 115 | Material 116 | material(const Float Ka, 117 | const Float Kd, 118 | const Float Ks, 119 | const Float Kr, 120 | const Float Kt, 121 | const Float p) { 122 | 123 | Float sum = Ka + Kd + Ks + Kr + Kt; 124 | Material m = {.Ka = Ka / sum, 125 | .Kd = Kd / sum, 126 | .Ks = Ks / sum, 127 | .Kr = Kr / sum, 128 | .Kt = Kt / sum, 129 | .p = p}; 130 | return m; 131 | } 132 | 133 | Camera * 134 | new_camera(const Point3d camera_position, 135 | const Float al_x, 136 | const Float al_y, 137 | const Float al_z, 138 | const Float proj_plane_dist) { 139 | 140 | 141 | Camera * cam = malloc(sizeof(Camera)); 142 | 143 | cam->camera_position = camera_position; 144 | 145 | cam->al_x = al_x; 146 | cam->sin_al_x = sin(al_x); 147 | cam->cos_al_x = cos(al_x); 148 | 149 | cam->al_y = al_y; 150 | cam->sin_al_y = sin(al_y); 151 | cam->cos_al_y = cos(al_y); 152 | 153 | cam->al_z = al_z; 154 | cam->sin_al_z = sin(al_z); 155 | cam->cos_al_z = cos(al_z); 156 | 157 | cam->proj_plane_dist = proj_plane_dist; 158 | 159 | return cam; 160 | } 161 | 162 | void 163 | release_camera(Camera * const cam) { 164 | free(cam); 165 | } 166 | 167 | void 168 | rotate_camera(Camera * const cam, 169 | const Float al_x, 170 | const Float al_y, 171 | const Float al_z) { 172 | 173 | if(fabs(al_x) > EPSILON) { 174 | cam->al_x += al_x; 175 | cam->sin_al_x = sin(cam->al_x); 176 | cam->cos_al_x = cos(cam->al_x); 177 | } 178 | 179 | if(fabs(al_y) > EPSILON) { 180 | cam->al_y += al_y; 181 | cam->sin_al_y = sin(cam->al_y); 182 | cam->cos_al_y = cos(cam->al_y); 183 | } 184 | 185 | if(fabs(al_z) > EPSILON) { 186 | cam->al_z += al_z; 187 | cam->sin_al_z = sin(cam->al_z); 188 | cam->cos_al_z = cos(cam->al_z); 189 | } 190 | } 191 | 192 | void 193 | move_camera(Camera * const camera, 194 | const Vector3d vector) { 195 | 196 | Vector3d r_vector = rotate_vector_x(vector, camera->sin_al_x, camera->cos_al_x); 197 | r_vector = rotate_vector_z(r_vector, camera->sin_al_z, camera->cos_al_z); 198 | r_vector = rotate_vector_y(r_vector, camera->sin_al_y, camera->cos_al_y); 199 | 200 | Point3d curr_pos = camera->camera_position; 201 | 202 | camera->camera_position = point3d(curr_pos.x + r_vector.x, 203 | curr_pos.y + r_vector.y, 204 | curr_pos.z + r_vector.z); 205 | } -------------------------------------------------------------------------------- /render/src/sphere.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // Declarations 8 | // -------------------------------------------------------------- 9 | 10 | typedef 11 | struct { 12 | Point3d center; 13 | Float radius; 14 | Color color; 15 | Material material; 16 | } 17 | Sphere; 18 | 19 | static Boolean 20 | intersect_sphere(const void * data, 21 | const Point3d vector_start, 22 | const Vector3d vector, 23 | Point3d * const intersection_point); 24 | 25 | static Point3d 26 | get_min_sphere_boundary_point(const void * data); 27 | 28 | static Point3d 29 | get_max_sphere_boundary_point(const void * data); 30 | 31 | static Color 32 | get_sphere_color(const void * data, 33 | const Point3d intersection_point); 34 | 35 | static Vector3d 36 | get_sphere_normal_vector(const void * data, 37 | const Point3d intersection_point); 38 | 39 | static Material 40 | get_sphere_material(const void * data, 41 | const Point3d intersection_point); 42 | 43 | void 44 | release_sphere_data(void * data); 45 | 46 | // Code 47 | // -------------------------------------------------------------- 48 | 49 | Object3d * 50 | new_sphere(const Point3d center, 51 | const Float radius, 52 | const Color color, 53 | const Material material) { 54 | 55 | Sphere * sphere = malloc(sizeof(Sphere)); 56 | sphere->center = center; 57 | sphere->radius = radius; 58 | sphere->color = color; 59 | sphere->material = material; 60 | 61 | Object3d * obj = malloc(sizeof(Object3d)); 62 | obj->data = sphere; 63 | obj->release_data = release_sphere_data; 64 | obj->get_color = get_sphere_color; 65 | obj->intersect = intersect_sphere; 66 | obj->get_normal_vector = get_sphere_normal_vector; 67 | obj->get_material = get_sphere_material; 68 | obj->get_min_boundary_point = get_min_sphere_boundary_point; 69 | obj->get_max_boundary_point = get_max_sphere_boundary_point; 70 | 71 | return obj; 72 | } 73 | 74 | static Color 75 | get_sphere_color(const void * data, 76 | const Point3d intersection_point) { 77 | const Sphere * sphere = data; 78 | return sphere->color; 79 | } 80 | 81 | static Vector3d 82 | get_sphere_normal_vector(const void * data, 83 | const Point3d intersection_point) { 84 | const Sphere * sphere = data; 85 | Vector3d n = vector3dp(sphere->center, intersection_point); 86 | normalize_vector(&n); 87 | return n; 88 | } 89 | 90 | static Material 91 | get_sphere_material(const void * data, 92 | const Point3d intersection_point) { 93 | const Sphere * sphere = data; 94 | return sphere->material; 95 | } 96 | 97 | void 98 | release_sphere_data(void * data) { 99 | Sphere * sphere = data; 100 | free(sphere); 101 | } 102 | 103 | Point3d 104 | get_min_sphere_boundary_point(const void * data) { 105 | const Sphere * sphere = data; 106 | const Point3d c = sphere->center; 107 | const Float r = sphere->radius; 108 | return point3d(c.x - r - 1, c.y - r - 1, c.z - r - 1); 109 | } 110 | 111 | Point3d 112 | get_max_sphere_boundary_point(const void * data) { 113 | const Sphere * sphere = data; 114 | const Point3d c = sphere->center; 115 | const Float r = sphere->radius; 116 | return point3d(c.x + r + 1, c.y + r + 1, c.z + r + 1); 117 | } 118 | 119 | static Boolean 120 | intersect_sphere(const void * data, 121 | const Point3d vector_start, 122 | const Vector3d vector, 123 | Point3d * const intersection_point) { 124 | 125 | const Sphere * sphere = data; 126 | 127 | const Point3d center = sphere->center; 128 | const Float r = sphere->radius; 129 | 130 | const Float a = vector.x * vector.x 131 | + vector.y * vector.y 132 | + vector.z * vector.z; 133 | 134 | const Float b = 2 * (vector.x * (vector_start.x - center.x) 135 | + vector.y * (vector_start.y - center.y) 136 | + vector.z * (vector_start.z - center.z)); 137 | 138 | const Float c = center.x * center.x 139 | + center.y * center.y 140 | + center.z * center.z 141 | + vector_start.x * vector_start.x 142 | + vector_start.y * vector_start.y 143 | + vector_start.z * vector_start.z 144 | - 2 * (vector_start.x * center.x 145 | + vector_start.y * center.y 146 | + vector_start.z * center.z) 147 | - r * r; 148 | 149 | const Float D = b * b - 4 * a * c; 150 | 151 | if(D < 0) 152 | return False; 153 | 154 | const Float sqrt_D = sqrt(D); 155 | const Float a_2 = 2 * a; 156 | 157 | const Float t1 = (-b + sqrt_D) / a_2; 158 | const Float t2 = (-b - sqrt_D) / a_2; 159 | 160 | const Float min_t = (t1 < t2) ? t1 : t2; 161 | const Float max_t = (t1 > t2) ? t1 : t2; 162 | 163 | const Float t = (min_t > EPSILON) ? min_t : max_t; 164 | 165 | if(t < EPSILON) 166 | return False; 167 | 168 | *intersection_point = point3d(vector_start.x + t * vector.x, 169 | vector_start.y + t * vector.y, 170 | vector_start.z + t * vector.z); 171 | 172 | return True; 173 | } -------------------------------------------------------------------------------- /render/src/tracer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define INITIAL_RAY_INTENSITY 100 12 | #define THRESHOLD_RAY_INTENSITY 10 13 | #define MAX_RAY_RECURSION_LEVEL 10 14 | 15 | // Declarations 16 | // -------------------------------------------------------------- 17 | 18 | Color 19 | trace_recursively(const Scene * const scene, 20 | const Point3d vector_start, 21 | const Vector3d vector, 22 | const Float intensity, 23 | const int recursion_level); 24 | 25 | inline Boolean 26 | is_viewable(const Point3d target_point, 27 | const Point3d starting_point, 28 | const Scene * const scene); 29 | 30 | inline Color 31 | get_lighting_color(const Point3d point, 32 | const Vector3d norm_v, 33 | const Scene * const scene); 34 | 35 | inline Color 36 | get_specular_color(const Point3d point, 37 | const Vector3d reflected_ray, 38 | const Scene * const scene, 39 | const Float p); 40 | 41 | inline Color 42 | calculate_color(const Scene * const scene, 43 | const Point3d vector_start, 44 | const Vector3d vector, 45 | Object3d * const * obj_ptr, 46 | const Point3d * const point_ptr, 47 | const Float * const dist_ptr, 48 | const Float intensity, 49 | const int recursion_level); 50 | 51 | // Code 52 | // -------------------------------------------------------------- 53 | 54 | Color 55 | trace(const Scene * const scene, 56 | const Camera * const camera, 57 | Vector3d vector) { 58 | 59 | Vector3d r_vector = rotate_vector_x(vector, camera->sin_al_x, camera->cos_al_x); 60 | r_vector = rotate_vector_z(r_vector, camera->sin_al_z, camera->cos_al_z); 61 | r_vector = rotate_vector_y(r_vector, camera->sin_al_y, camera->cos_al_y); 62 | 63 | return trace_recursively(scene, 64 | camera->camera_position, 65 | r_vector, 66 | INITIAL_RAY_INTENSITY, 67 | 0); 68 | } 69 | 70 | Color 71 | trace_recursively(const Scene * const scene, 72 | const Point3d vector_start, 73 | const Vector3d vector, 74 | const Float intensity, 75 | const int recursion_level) { 76 | 77 | // possibly - redundant code (was added to prevent overflow of Float) 78 | // TODO: remove 79 | //normalize_vector(&vector); 80 | 81 | Object3d * nearest_obj = NULL; 82 | Point3d nearest_intersection_point; 83 | Float nearest_intersection_point_dist = FLOAT_MAX; 84 | 85 | if(find_intersection_tree(scene->kd_tree, 86 | vector_start, 87 | vector, 88 | &nearest_obj, 89 | &nearest_intersection_point, 90 | &nearest_intersection_point_dist)) { 91 | 92 | return calculate_color(scene, 93 | vector_start, 94 | vector, 95 | &nearest_obj, 96 | &nearest_intersection_point, 97 | &nearest_intersection_point_dist, 98 | intensity, 99 | recursion_level); 100 | } 101 | 102 | return scene->background_color; 103 | } 104 | 105 | inline Color 106 | calculate_color(const Scene * const scene, 107 | const Point3d vector_start, 108 | const Vector3d vector, 109 | Object3d * const * obj_ptr, 110 | const Point3d * const point_ptr, 111 | const Float * const dist_ptr, 112 | const Float intensity, 113 | const int recursion_level) { 114 | 115 | const Object3d * obj = *obj_ptr; 116 | const Point3d point = *point_ptr; 117 | const Float dist = *dist_ptr; 118 | 119 | 120 | const Material material = obj->get_material(obj->data, point); 121 | 122 | const Vector3d norm = obj->get_normal_vector(obj->data, point); 123 | 124 | Color obj_color = obj->get_color(obj->data, point); 125 | Color ambient_color; 126 | Color diffuse_color; 127 | Color reflected_color; 128 | Color specular_color; 129 | 130 | Float fog_density = 0; 131 | if(scene->fog_density) { 132 | fog_density = scene->fog_density(dist, scene->fog_parameters); 133 | } 134 | 135 | 136 | Vector3d reflected_ray; 137 | if((material.Ks) || (material.Kr)) { 138 | reflected_ray = reflect_ray(vector, norm); 139 | } 140 | 141 | // Ambient 142 | if(material.Ka) { 143 | ambient_color = mix_colors(scene->background_color, obj_color); 144 | } 145 | 146 | // Diffuse 147 | if(material.Kd) { 148 | diffuse_color = obj_color; 149 | 150 | if(scene->light_sources_count) { 151 | Color light_color = get_lighting_color(point, norm, scene); 152 | diffuse_color = mix_colors(diffuse_color, light_color); 153 | } 154 | } 155 | 156 | // Specular 157 | if(material.Ks) { 158 | specular_color = scene->background_color; 159 | 160 | if(scene->light_sources_count) { 161 | specular_color = get_specular_color(point, reflected_ray, scene, material.p); 162 | } 163 | } 164 | 165 | 166 | // Reflect 167 | if(material.Kr) { 168 | // Avoid deep recursion by tracing rays, which have intensity is greather than threshold 169 | // and avoid infinite recursion by limiting number of recursive calls 170 | if((intensity > THRESHOLD_RAY_INTENSITY) 171 | && (recursion_level < MAX_RAY_RECURSION_LEVEL)) { 172 | 173 | reflected_color = trace_recursively(scene, 174 | point, 175 | reflected_ray, 176 | intensity * material.Kr * (1 - fog_density), 177 | recursion_level + 1); 178 | } else { 179 | reflected_color = scene->background_color; 180 | } 181 | } 182 | 183 | // Result 184 | Color result_color = rgb(0, 0, 0); 185 | if(material.Ka) { 186 | result_color = add_colors(result_color, 187 | mul_color(ambient_color, material.Ka)); 188 | } 189 | if(material.Kd) { 190 | result_color = add_colors(result_color, 191 | mul_color(diffuse_color, material.Kd)); 192 | } 193 | if(material.Ks) { 194 | result_color = add_colors(result_color, 195 | mul_color(specular_color, material.Ks)); 196 | } 197 | if(material.Kr) { 198 | result_color = add_colors(result_color, 199 | mul_color(reflected_color, material.Kr)); 200 | } 201 | 202 | if(scene->fog_density) { 203 | result_color = add_colors( 204 | mul_color(scene->background_color, fog_density), 205 | mul_color(result_color, 1 - fog_density)); 206 | } 207 | 208 | return result_color; 209 | } 210 | 211 | inline Color 212 | get_lighting_color(const Point3d point, 213 | const Vector3d norm_v, 214 | const Scene * const scene) { 215 | 216 | Color light_color = rgb(0, 0, 0); 217 | 218 | // possibly - redundant code (was added to prevent overflow of Float) 219 | // TODO: remove 220 | //normalize_vector(&norm_v); 221 | 222 | LightSource3d * ls; 223 | Vector3d v_ls; 224 | Float cos_ls; 225 | Color color_ls; 226 | int i; 227 | 228 | for(i = 0; i < scene->last_light_source_index + 1; i++) { 229 | if(scene->light_sources[i]) { 230 | ls = scene->light_sources[i]; 231 | 232 | // If not shaded 233 | if(is_viewable(ls->location, point, scene)) { 234 | v_ls = vector3dp(point, ls->location); 235 | 236 | // possibly - redundant code (was added to prevent overflow of Float) 237 | // TODO: remove 238 | //normalize_vector(&v_ls); 239 | 240 | cos_ls = fabs(cos_vectors(norm_v, v_ls)); 241 | color_ls = mul_color(ls->color, cos_ls); 242 | light_color = add_colors(light_color, color_ls); 243 | } 244 | } 245 | } 246 | 247 | return light_color; 248 | } 249 | 250 | inline Color 251 | get_specular_color(const Point3d point, 252 | const Vector3d reflected_ray, 253 | const Scene * const scene, 254 | const Float p) { 255 | 256 | Color light_color = rgb(0, 0, 0); 257 | 258 | // possibly - redundant code (was added to prevent overflow of Float) 259 | // TODO: remove 260 | //normalize_vector(&reflected_ray); 261 | 262 | LightSource3d * ls; 263 | Vector3d v_ls; 264 | Float cos_ls; 265 | Color color_ls; 266 | int i; 267 | 268 | for(i = 0; i < scene->last_light_source_index + 1; i++) { 269 | if(scene->light_sources[i]) { 270 | ls = scene->light_sources[i]; 271 | 272 | // If not shaded 273 | if(is_viewable(ls->location, point, scene)) { 274 | v_ls = vector3dp(point, ls->location); 275 | 276 | // possibly - redundant code (was added to prevent overflow of Float) 277 | // TODO: remove 278 | //normalize_vector(&v_ls); 279 | 280 | cos_ls = cos_vectors(reflected_ray, v_ls); 281 | if(cos_ls > EPSILON) { 282 | color_ls = mul_color(ls->color, pow(cos_ls, p)); 283 | light_color = add_colors(light_color, color_ls); 284 | } 285 | } 286 | } 287 | } 288 | 289 | return light_color; 290 | } 291 | 292 | inline Boolean 293 | is_viewable(const Point3d target_point, 294 | const Point3d starting_point, 295 | const Scene * const scene) { 296 | 297 | const Vector3d ray = vector3dp(starting_point, target_point); 298 | const Float target_dist = module_vector(ray); 299 | 300 | // possibly - redundant code (was added to prevent overflow of Float) 301 | // TODO: remove 302 | //normalize_vector(&ray); 303 | 304 | Object3d * nearest_obj = NULL; 305 | Point3d nearest_intersection_point; 306 | Float nearest_intersection_point_dist = FLOAT_MAX; 307 | 308 | if(find_intersection_tree(scene->kd_tree, 309 | starting_point, 310 | ray, 311 | &nearest_obj, 312 | &nearest_intersection_point, 313 | &nearest_intersection_point_dist)) { 314 | 315 | // Check if intersection point is closer than target_point 316 | return (target_dist < nearest_intersection_point_dist); 317 | } 318 | // Ray doesn't intersect any of scene objects 319 | return True; 320 | } -------------------------------------------------------------------------------- /render/src/triangle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // Declarations 9 | // -------------------------------------------------------------- 10 | 11 | typedef 12 | struct { 13 | /************ 14 | * Geometry * 15 | ************/ 16 | 17 | // Absolute (world) vertexes of triangle 18 | Point3d p1; 19 | Point3d p2; 20 | Point3d p3; 21 | 22 | Vector3d norm; 23 | 24 | // distance to the center of coordinates 25 | Float d; 26 | 27 | Vector3d v_p1_p2; 28 | Vector3d v_p2_p3; 29 | Vector3d v_p3_p1; 30 | 31 | /************ 32 | * Material * 33 | ************/ 34 | 35 | Color color; 36 | Material material; 37 | 38 | /************ 39 | * Texture * 40 | ************/ 41 | 42 | Point2d t1; 43 | Point2d t2; 44 | Point2d t3; 45 | 46 | Canvas * texture; 47 | 48 | /************ 49 | * Norms * 50 | ************/ 51 | 52 | Vector3d n1; 53 | Vector3d n2; 54 | Vector3d n3; 55 | } 56 | Triangle3d; 57 | 58 | inline static Triangle3d * 59 | create_plain_triangle(const Point3d p1, 60 | const Point3d p2, 61 | const Point3d p3, 62 | const Color color, 63 | const Material material); 64 | 65 | inline static Object3d * 66 | wrap_triangle(Triangle3d * triangle); 67 | 68 | inline static Boolean 69 | intersect_triangle(const void * data, 70 | const Point3d vector_start, 71 | const Vector3d vector, 72 | Point3d * const intersection_point); 73 | 74 | Point3d 75 | get_min_triangle_boundary_point(const void * data); 76 | 77 | Point3d 78 | get_max_triangle_boundary_point(const void * data); 79 | 80 | static inline Color 81 | get_triangle_color(const void * data, 82 | const Point3d intersection_point); 83 | 84 | static inline Color 85 | get_texture_color(const void * data, 86 | const Point3d intersection_point); 87 | 88 | static inline Vector3d 89 | get_triangle_normal_vector(const void * data, 90 | const Point3d intersection_point); 91 | 92 | static inline Vector3d 93 | get_phong_normal_vector(const void * data, 94 | const Point3d intersection_point); 95 | 96 | static inline Material 97 | get_triangle_material(const void * data, 98 | const Point3d intersection_point); 99 | 100 | static inline void 101 | release_triangle_data(void * data); 102 | 103 | static inline Boolean 104 | check_same_clock_dir(const Vector3d v1, 105 | const Vector3d v2, 106 | const Vector3d norm); 107 | 108 | static inline void 109 | get_weights_of_vertexes(const Triangle3d * const tr, 110 | const Point3d intersection_point, 111 | Float * const w1, 112 | Float * const w2, 113 | Float * const w3); 114 | 115 | // Code 116 | // -------------------------------------------------------------- 117 | 118 | Object3d * 119 | new_triangle(const Point3d p1, 120 | const Point3d p2, 121 | const Point3d p3, 122 | const Color color, 123 | const Material material) { 124 | 125 | Triangle3d * triangle = create_plain_triangle(p1, p2, p3, color, material); 126 | return wrap_triangle(triangle); 127 | } 128 | 129 | Object3d * 130 | new_triangle_with_texture(const Point3d p1, 131 | const Point3d p2, 132 | const Point3d p3, 133 | const Point2d t1, 134 | const Point2d t2, 135 | const Point2d t3, 136 | Canvas * texture, 137 | const Color color, 138 | const Material material) { 139 | 140 | Triangle3d * triangle = create_plain_triangle(p1, p2, p3, color, material); 141 | triangle->t1 = t1; 142 | triangle->t2 = t2; 143 | triangle->t3 = t3; 144 | triangle->texture = texture; 145 | 146 | Object3d * obj = wrap_triangle(triangle); 147 | obj->get_color = get_texture_color; 148 | 149 | return obj; 150 | } 151 | 152 | Object3d * 153 | new_triangle_with_norms(const Point3d p1, 154 | const Point3d p2, 155 | const Point3d p3, 156 | const Vector3d n1, 157 | const Vector3d n2, 158 | const Vector3d n3, 159 | const Color color, 160 | const Material material) { 161 | 162 | Triangle3d * triangle = create_plain_triangle(p1, p2, p3, color, material); 163 | triangle->n1 = n1; 164 | triangle->n2 = n2; 165 | triangle->n3 = n3; 166 | 167 | Object3d * obj = wrap_triangle(triangle); 168 | obj->get_normal_vector = get_phong_normal_vector; 169 | 170 | return obj; 171 | } 172 | 173 | inline static Triangle3d * 174 | create_plain_triangle(const Point3d p1, 175 | const Point3d p2, 176 | const Point3d p3, 177 | const Color color, 178 | const Material material) { 179 | Triangle3d * triangle = malloc(sizeof(Triangle3d)); 180 | 181 | triangle->p1 = p1; 182 | triangle->p2 = p2; 183 | triangle->p3 = p3; 184 | 185 | triangle->norm = cross_product(vector3dp(p1, p3), vector3dp(p3, p2)); 186 | 187 | triangle->d = -(p1.x * triangle->norm.x + p1.y * triangle->norm.y + p1.z * triangle->norm.z); 188 | 189 | triangle->color = color; 190 | triangle->material = material; 191 | 192 | triangle->v_p1_p2 = vector3dp(p1, p2); 193 | triangle->v_p2_p3 = vector3dp(p2, p3); 194 | triangle->v_p3_p1 = vector3dp(p3, p1); 195 | 196 | return triangle; 197 | } 198 | 199 | inline static Object3d * 200 | wrap_triangle(Triangle3d * triangle) { 201 | Object3d * obj = calloc(1, sizeof(Object3d)); 202 | obj->data = triangle; 203 | obj->release_data = release_triangle_data; 204 | obj->get_color = get_triangle_color; 205 | obj->intersect = intersect_triangle; 206 | obj->get_normal_vector = get_triangle_normal_vector; 207 | obj->get_material = get_triangle_material; 208 | obj->get_min_boundary_point = get_min_triangle_boundary_point; 209 | obj->get_max_boundary_point = get_max_triangle_boundary_point; 210 | return obj; 211 | } 212 | 213 | static inline Color 214 | get_triangle_color(const void * data, 215 | const Point3d intersection_point) { 216 | const Triangle3d * triangle = data; 217 | return triangle->color; 218 | } 219 | 220 | static inline Color 221 | get_texture_color(const void * data, 222 | const Point3d intersection_point) { 223 | 224 | const Triangle3d * tr = data; 225 | 226 | Float w1; 227 | Float w2; 228 | Float w3; 229 | 230 | get_weights_of_vertexes(tr, intersection_point, &w1, &w2, &w3); 231 | 232 | const Point2d t1 = tr->t1; 233 | const Point2d t2 = tr->t2; 234 | const Point2d t3 = tr->t3; 235 | 236 | Float xf = w1 * t1.x + w2 * t2.x + w3 * t3.x; 237 | Float yf = w1 * t1.y + w2 * t2.y + w3 * t3.y; 238 | 239 | // transform xf and yf to values from interval [0..1] 240 | xf = (xf < 0) ? (xf - (int)xf) + 1 : (xf - (int)xf); 241 | yf = (yf < 0) ? (yf - (int)yf) + 1 : (yf - (int)yf); 242 | 243 | Canvas * canvas = tr->texture; 244 | 245 | int x = (int)(xf * canvas->w); 246 | int y = (int)(yf * canvas->h); 247 | 248 | return get_pixel(x, y, canvas); 249 | } 250 | 251 | static inline Vector3d 252 | get_triangle_normal_vector(const void * data, 253 | const Point3d intersection_point) { 254 | const Triangle3d * triangle = data; 255 | return triangle->norm; 256 | } 257 | 258 | static inline Vector3d 259 | get_phong_normal_vector(const void * data, 260 | const Point3d intersection_point) { 261 | 262 | const Triangle3d * tr = data; 263 | 264 | Float w1; 265 | Float w2; 266 | Float w3; 267 | 268 | get_weights_of_vertexes(tr, intersection_point, &w1, &w2, &w3); 269 | 270 | const Vector3d n1 = tr->n1; 271 | const Vector3d n2 = tr->n2; 272 | const Vector3d n3 = tr->n3; 273 | 274 | return vector3df(w1 * n1.x + w2 * n2.x + w3 * n3.x, 275 | w1 * n1.y + w2 * n2.y + w3 * n3.y, 276 | w1 * n1.z + w2 * n2.z + w3 * n3.z); 277 | } 278 | 279 | static inline Material 280 | get_triangle_material(const void * data, 281 | const Point3d intersection_point) { 282 | const Triangle3d * triangle = data; 283 | return triangle->material; 284 | } 285 | 286 | static inline void 287 | release_triangle_data(void * data) { 288 | Triangle3d * triangle = data; 289 | free(triangle); 290 | } 291 | 292 | Point3d 293 | get_min_triangle_boundary_point(const void * data) { 294 | const Triangle3d * t = data; 295 | 296 | Float x_min = t->p1.x; 297 | Float y_min = t->p1.y; 298 | Float z_min = t->p1.z; 299 | 300 | x_min = (x_min < t->p2.x) ? x_min : t->p2.x; 301 | y_min = (y_min < t->p2.y) ? y_min : t->p2.y; 302 | z_min = (z_min < t->p2.z) ? z_min : t->p2.z; 303 | 304 | x_min = (x_min < t->p3.x) ? x_min : t->p3.x; 305 | y_min = (y_min < t->p3.y) ? y_min : t->p3.y; 306 | z_min = (z_min < t->p3.z) ? z_min : t->p3.z; 307 | 308 | return point3d(x_min - EPSILON, y_min - EPSILON, z_min - EPSILON); 309 | } 310 | 311 | Point3d 312 | get_max_triangle_boundary_point(const void * data) { 313 | const Triangle3d * t = data; 314 | 315 | Float x_max = t->p1.x; 316 | Float y_max = t->p1.y; 317 | Float z_max = t->p1.z; 318 | 319 | x_max = (x_max > t->p2.x) ? x_max : t->p2.x; 320 | y_max = (y_max > t->p2.y) ? y_max : t->p2.y; 321 | z_max = (z_max > t->p2.z) ? z_max : t->p2.z; 322 | 323 | x_max = (x_max > t->p3.x) ? x_max : t->p3.x; 324 | y_max = (y_max > t->p3.y) ? y_max : t->p3.y; 325 | z_max = (z_max > t->p3.z) ? z_max : t->p3.z; 326 | 327 | return point3d(x_max + EPSILON, y_max + EPSILON, z_max + EPSILON); 328 | } 329 | 330 | inline static Boolean 331 | intersect_triangle(const void * data, 332 | const Point3d vector_start, 333 | const Vector3d vector, 334 | Point3d * const intersection_point) { 335 | 336 | const Triangle3d * tr = data; 337 | 338 | const Float scalar_product = dot_product(tr->norm, vector); 339 | 340 | if(fabs(scalar_product) < EPSILON) { 341 | // Ray is perpendicular to triangles normal vector (A, B, C) 342 | // it means that ray is parellel to triangle 343 | // so there is no intersection 344 | return False; 345 | } 346 | 347 | const Float k = - (tr->norm.x * vector_start.x 348 | + tr->norm.y * vector_start.y 349 | + tr->norm.z * vector_start.z 350 | + tr->d) 351 | / scalar_product; 352 | 353 | if(k < EPSILON) { 354 | // Avoid intersection in the opposite direction 355 | return False; 356 | } 357 | 358 | // Intersection point 359 | const Float x = vector_start.x + vector.x * k; 360 | const Float y = vector_start.y + vector.y * k; 361 | const Float z = vector_start.z + vector.z * k; 362 | const Point3d ipt = point3d(x, y, z); 363 | 364 | if(check_same_clock_dir(tr->v_p1_p2, vector3dp(tr->p1, ipt), tr->norm) 365 | && check_same_clock_dir(tr->v_p2_p3, vector3dp(tr->p2, ipt), tr->norm) 366 | && check_same_clock_dir(tr->v_p3_p1, vector3dp(tr->p3, ipt), tr->norm)) { 367 | 368 | *intersection_point = ipt; 369 | return True; 370 | } 371 | 372 | // No intersection 373 | return False; 374 | } 375 | 376 | static inline Boolean 377 | check_same_clock_dir(const Vector3d v1, 378 | const Vector3d v2, 379 | const Vector3d norm) { 380 | 381 | const Vector3d norm_v1_v2 = cross_product(v2, v1); 382 | 383 | if(dot_product(norm_v1_v2, norm) < 0) 384 | return False; 385 | else 386 | return True; 387 | } 388 | 389 | static inline void 390 | get_weights_of_vertexes(const Triangle3d * const tr, 391 | const Point3d intersection_point, 392 | Float * const w1, 393 | Float * const w2, 394 | Float * const w3) { 395 | 396 | const Vector3d v_p1_p = vector3dp(tr->p1, intersection_point); 397 | const Vector3d v_p2_p = vector3dp(tr->p2, intersection_point); 398 | const Vector3d v_p3_p = vector3dp(tr->p3, intersection_point); 399 | 400 | const Float s1 = module_vector(cross_product(v_p2_p, tr->v_p2_p3)); 401 | const Float s2 = module_vector(cross_product(v_p3_p, tr->v_p3_p1)); 402 | const Float s3 = module_vector(cross_product(v_p1_p, tr->v_p1_p2)); 403 | 404 | const Float s_sum = s1 + s2 + s3; 405 | 406 | *w1 = s1 / s_sum; 407 | *w2 = s2 / s_sum; 408 | *w3 = s3 / s_sum; 409 | } 410 | 411 | --------------------------------------------------------------------------------