├── .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 | 
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 | 
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 |
--------------------------------------------------------------------------------