|
1 /* |
|
2 * C++ RayTracer |
|
3 * file: raytracer.cc |
|
4 * |
|
5 * Radek Brich, 2006 |
|
6 */ |
|
7 |
|
8 #include <stdio.h> |
|
9 #include <malloc.h> |
|
10 #include <float.h> |
|
11 #include "raytracer.h" |
|
12 |
|
13 // Hammersley spherical point distribution |
|
14 // http://www.cse.cuhk.edu.hk/~ttwong/papers/udpoint/udpoints.html |
|
15 Vector3 Raytracer::SphereDistribute(int i, int n, float extent, Vector3 &normal) |
|
16 { |
|
17 float p, t, st, phi, phirad; |
|
18 int kk; |
|
19 |
|
20 t = 0; |
|
21 for (p=0.5, kk=i; kk; p*=0.5, kk>>=1) |
|
22 if (kk & 1) |
|
23 t += p; |
|
24 t = 1.0 + (t - 1.0)*extent; |
|
25 |
|
26 phi = (i + 0.5) / n; |
|
27 phirad = phi * 2.0 * M_PI; |
|
28 |
|
29 st = sqrt(1.0 - t*t); |
|
30 |
|
31 float x, y, z, xx, yy, zz, q; |
|
32 x = st * cos(phirad); |
|
33 y = st * sin(phirad); |
|
34 z = t; |
|
35 |
|
36 // rotate against Y axis |
|
37 q = acos(normal.z); |
|
38 zz = z*cos(q) - x*sin(q); |
|
39 xx = z*sin(q) + x*cos(q); |
|
40 yy = y; |
|
41 |
|
42 // rotate against Z axis |
|
43 q = atan2f(normal.y, normal.x); |
|
44 x = xx*cos(q) - yy*sin(q); |
|
45 y = xx*sin(q) + yy*cos(q); |
|
46 z = zz; |
|
47 |
|
48 return Vector3(x, y, z); |
|
49 } |
|
50 |
|
51 inline Shape *Raytracer::nearest_intersection(const Shape *origin_shape, const Ray &ray, |
|
52 float &nearest_distance) |
|
53 { |
|
54 Shape *nearest_shape = NULL; |
|
55 ShapeList::iterator shape; |
|
56 for (shape = shapes.begin(); shape != shapes.end(); shape++) |
|
57 if (*shape != origin_shape && (*shape)->intersect(ray, nearest_distance)) |
|
58 nearest_shape = *shape; |
|
59 return nearest_shape; |
|
60 } |
|
61 |
|
62 // ---- tyto dve funkce budou v budouci verzi metody objektu PhongShader |
|
63 |
|
64 // calculate shader function |
|
65 // P is point of intersection, N normal in this point |
|
66 Colour PhongShader_ambient(Material &mat, Vector3 &P) |
|
67 { |
|
68 Colour col = mat.texture.colour; //mat.texture.evaluate(P); |
|
69 |
|
70 // ambient |
|
71 return mat.ambient * col; |
|
72 } |
|
73 |
|
74 /* |
|
75 P is point of intersection, |
|
76 N normal in this point, |
|
77 R direction of reflected ray, |
|
78 V direction to the viewer |
|
79 */ |
|
80 Colour PhongShader_calculate(Material &mat, Vector3 &P, Vector3 &N, Vector3 &R, Vector3 &V, |
|
81 Light &light) |
|
82 { |
|
83 Colour I = Colour(); |
|
84 Vector3 L = light.pos - P; |
|
85 L.normalize(); |
|
86 float L_dot_N = dot(L, N); |
|
87 float R_dot_V = dot(R, V); |
|
88 |
|
89 Colour col = mat.texture.colour; //mat.texture.evaluate(P); |
|
90 |
|
91 // diffuse |
|
92 I = mat.diffuse * col * light.colour * L_dot_N; |
|
93 |
|
94 // specular |
|
95 if (R_dot_V > 0) |
|
96 I += mat.specular * light.colour * powf(R_dot_V, mat.shininess); |
|
97 return I; |
|
98 } |
|
99 |
|
100 Colour Raytracer::raytrace(Ray &ray, int depth, Shape *origin_shape) |
|
101 { |
|
102 float nearest_distance = FLT_MAX; //Infinity |
|
103 Shape *nearest_shape = nearest_intersection(origin_shape, ray, nearest_distance); |
|
104 |
|
105 if (nearest_shape == NULL) { |
|
106 return bg_colour; |
|
107 } else { |
|
108 Colour acc = Colour(); |
|
109 Vector3 P = ray.a + ray.dir * nearest_distance; // point of intersection |
|
110 Vector3 normal = nearest_shape->normal(P); |
|
111 acc = PhongShader_ambient(*nearest_shape->material, P); |
|
112 |
|
113 vector<Light*>::iterator light; |
|
114 for (light = lights.begin(); light != lights.end(); light++) { |
|
115 Vector3 jo, L = (*light)->pos - P; // direction vector to light |
|
116 L.normalize(); |
|
117 float L_dot_N = dot(L, normal); |
|
118 if (L_dot_N > 0) { |
|
119 // test if this light is occluded (sharp shadows) |
|
120 if ((*light)->shadows) { |
|
121 Ray shadow_ray = Ray(P, L); |
|
122 float dist = FLT_MAX; |
|
123 if (nearest_intersection(nearest_shape, shadow_ray, dist)) |
|
124 continue; |
|
125 } |
|
126 |
|
127 // shading function |
|
128 Vector3 R = L - 2.0 * L_dot_N * normal; |
|
129 acc += PhongShader_calculate(*nearest_shape->material, |
|
130 P, normal, R, ray.dir, **light); |
|
131 } |
|
132 } |
|
133 |
|
134 // reflection |
|
135 int trace_max_depth = 4; |
|
136 Vector3 newdir = ray.dir - 2.0 * dot(ray.dir, normal) * normal; |
|
137 if (depth < trace_max_depth && nearest_shape->material->reflection > 0.01) { |
|
138 Ray newray = Ray(P, newdir); |
|
139 Colour refl_col = raytrace(newray, depth + 1, nearest_shape); |
|
140 acc += nearest_shape->material->reflection * refl_col; |
|
141 } |
|
142 |
|
143 // refraction |
|
144 /* ... */ |
|
145 |
|
146 // ambient occlusion |
|
147 if (ao_samples) |
|
148 { |
|
149 float miss = 0; |
|
150 for (int i = 0; i < ao_samples; i++) { |
|
151 Vector3 dir = SphereDistribute(i, ao_samples, ao_angle, normal); |
|
152 Ray ao_ray = Ray(P, dir); |
|
153 float dist = ao_distance; |
|
154 Shape *shape_in_way = nearest_intersection(nearest_shape, ao_ray, dist); |
|
155 if (shape_in_way == NULL) |
|
156 miss += 1.0; |
|
157 else |
|
158 miss += dist / ao_distance; |
|
159 } |
|
160 float ao_intensity = miss / ao_samples; |
|
161 acc = acc * ao_intensity; |
|
162 } |
|
163 |
|
164 return acc; |
|
165 } |
|
166 } |
|
167 |
|
168 float *Raytracer::render(int w, int h) |
|
169 { |
|
170 int x, y; |
|
171 float *data, *iter; |
|
172 |
|
173 data = (float *) malloc(w*h*3*sizeof(float)); |
|
174 if (!data) |
|
175 return NULL; |
|
176 |
|
177 float startx = -1.0 * 4, starty = (float)h/w * 4; |
|
178 float dx = -2*startx/w, dy = -2*starty/h; |
|
179 float vx, vy; |
|
180 |
|
181 //srand(time(NULL)); |
|
182 |
|
183 // eye - viewing point |
|
184 Vector3 eye(0, 0, -5); |
|
185 |
|
186 // for each pixel... |
|
187 iter = data; |
|
188 for (vy = starty, y = 0; y < h; y++) { |
|
189 vx = startx; |
|
190 for (x = 0; x < w; x++) { |
|
191 // generate a ray from eye passing through this pixel |
|
192 #if 1 |
|
193 // no oversampling |
|
194 Vector3 dir = Vector3(vx, vy, 0) - eye; |
|
195 dir.normalize(); |
|
196 Ray ray(eye, dir); |
|
197 Colour c = raytrace(ray, 0, NULL); |
|
198 #else |
|
199 // 5x oversampling |
|
200 Vector3 dir = Vector3(); |
|
201 Colour c = Colour(); |
|
202 |
|
203 for (int i = 0; i < 5; i++) |
|
204 { |
|
205 float osax[] = {0.0, -0.4, +0.4, +0.4, -0.4}; |
|
206 float osay[] = {0.0, -0.4, -0.4, +0.4, +0.4}; |
|
207 dir = Vector3(vx + osax[i]*dx, |
|
208 vy + osay[i]*dy , 0) - eye; |
|
209 dir.normalize(); |
|
210 Ray ray(eye, dir); |
|
211 c += raytrace(ray, 0, NULL); |
|
212 } |
|
213 c = c * (1./5); |
|
214 #endif |
|
215 *iter++ = c.r; |
|
216 *iter++ = c.g; |
|
217 *iter++ = c.b; |
|
218 vx += dx; |
|
219 } |
|
220 vy += dy; |
|
221 printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%2d%% done (row %4d)", y*100/(h-1), y); |
|
222 } |
|
223 printf("\n"); |
|
224 return data; |
|
225 } |
|
226 |
|
227 void Raytracer::addshape(Shape *shape) |
|
228 { |
|
229 shapes.push_back(shape); |
|
230 } |
|
231 |
|
232 void Raytracer::addlight(Light *light) |
|
233 { |
|
234 lights.push_back(light); |
|
235 } |
|
236 |
|
237 void Raytracer::ambientocclusion(int samples, float distance, float angle) |
|
238 { |
|
239 ao_samples = samples; |
|
240 ao_distance = distance; |
|
241 ao_angle = angle; |
|
242 if (ao_distance == 0) |
|
243 /* 0 ==> Inf */ |
|
244 ao_distance = FLT_MAX; |
|
245 } |