1 #ifndef SCHLICKPHOTONSHADER_HXX
2 #define SCHLICKPHOTONSHADER_HXX
3
4 #include "Shader.hxx"
5 #include "Material.hxx"
6 #include "PhotonKDTree.hxx"
7
8 #include "defines.h"
9
10 // This shader is equivalent to the photon shader, but uses photon maps for indirect illumination
11 class SchlickPhotonShader : public Shader
12 {
13 Randomizer randomizer;
14 public:
15 Vec3f ambient, // ambient radiance
16 light_ray_org_std; // template for light_ray.org, to calculate it only once
17 float p12, // square of the isotropy factor for primary surface
18 p22; // square of the isotropy factor for primary surface
19 float mirror, // Material based mirroring percentage
20 glossy_mirror, // Material based glossy reflection percentage
21 glossy_transparency, // Material based glossy refraction percentage
22 transparency; // Material based transparency percentage
23 PhotonKDTree *photon_tree;
24
25 SchlickPhotonShader(Scene *scene, Material *material, PhotonKDTree *photon_tree)
26 : Shader(scene, material), photon_tree(photon_tree)
27 {
28 // Ambient illumination part
29 ambient = material->ambient * Product(material->color, La);
30
31 // Initialization of reflection and refraction
32 mirror = (material->mirror == UNDEF) ? 0.0f : material->mirror;
33 glossy_mirror = (material->glossy_mirror == UNDEF) ? 0.0f : material->glossy_mirror;
34 glossy_transparency = (material->glossy_transparency == UNDEF) ? 0.0f : material->glossy_transparency;
35 transparency = (material->transparency == UNDEF) ? 0.0f : material->transparency;
36
37 if (mirror + glossy_transparency + glossy_mirror + transparency > 1.0)
38 cerr << "WARNING: Material '" << material << "' has wrong reflection/refraction percentages (> 1.0)!" << endl;
39
40 // Precomputation of p^2
41 p12 = material->p1 * material->p1;
42 p22 = material->p2 * material->p2;
43 };
44
45 virtual ~SchlickPhotonShader()
46 {};
47
48 #include "SchlickTerm.include"
49
50 virtual Vec3f FancyColor(const Ray &origray, const Vec3f &normal, const bool &into)
51 {
52 Ray ray;
53 float m = mirror,
54 t = transparency,
55 gm = glossy_mirror,
56 gt = glossy_transparency;
57
58 // Project the ray direction onto the normal vector...
59 Vec3f projected = Dot(normal, -origray.dir) * normal,
60 std_ray_org = origray.org + origray.t * origray.dir;
61
62
63 // Start with the reflection calculation (which is needed anyway for t/m)
64 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
65 Vec3f reflective_color = Vec3f(0),
66 refractive_color = Vec3f(0),
67 glossy_reflection_color = Vec3f(0),
68 glossy_refraction_color = Vec3f(0);
69
70 if (t || m)
71 {
72 ray.org = std_ray_org;
73 ray.t = Infinity;
74 ray.hit = NULL;
75
76 // ...and mirror away
77 ray.dir = origray.dir + 2 * projected;
78 if (Nonzero(ray.dir))
79 {
80 Normalize(ray.dir);
81
82 ray.refrac_index = origray.refrac_index;
83 reflective_color = scene->RayTrace(ray);
84 }
85 else
86 reflective_color = Vec3f(0);
87 }
88
89 // Now perform the transparency part
90 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
91 if (t)
92 {
93 ray.org = std_ray_org;
94 ray.t = Infinity;
95 ray.hit = NULL;
96
97 // Cosine of the incident angle
98 float cosa = Dot(normal, -origray.dir),
99 new_refrac_index;
100
101 if (into)
102 new_refrac_index = origray.refrac_index + (material->refrac_index - 1.0f);
103 else
104 new_refrac_index = origray.refrac_index - (material->refrac_index - 1.0f);
105
106 float n = origray.refrac_index / new_refrac_index,
107
108 /*
109 * According to Snell's law we know that
110 * sina/sinb = n2/n1 = 1/n,
111 * hence
112 * (sina/sinb)^2 = (1/n)^2
113 * or
114 * sin^2 b = sin^2 a * n^2.
115 * As
116 * sin^2 a + cos^2 a = 1,
117 * we know that
118 * sin^2 b = n^2 * (1 - cos^2 a)
119 * and thus
120 * cos^2 b = 1 - n^2 * (1 - cos^2 a).
121 */
122
123 cosb2 = 1.0f - n * n * (1.0f - cosa*cosa);
124
125 if (cosb2 < 0.0f)
126 // Total reflection
127 m += t;
128 else
129 {
130 // Here again, we use the Schlick approximation
131 // (Hey, we are in the Schlick shader ;-) !)
132
133 float cosb = sqrtf(cosb2),
134 c = (n - 1) / (n + 1),
135 schlick;
136
137 c *= c;
138
139 // We use the angle in the less dense medium for Schlick's term
140 if (into)
141 schlick = c + (1 - c) * powf((1 - cosa), 5);
142 else
143 schlick = c + (1 - c) * powf((1 - cosb), 5);
144
145 schlick *= t;
146
147 m += schlick;
148 t -= schlick;
149
150 ray.dir = (n * cosa - cosb) * normal + n * origray.dir;
151
152 // Apply refraction index now as we are sure to have changed the medium
153 ray.refrac_index = new_refrac_index;
154 }
155
156 refractive_color = scene->RayTrace(ray);
157 }
158
159 if (gm || gt)
160 {
161 // Glossy reflection... think what? Yes... Same as in the photon distributor!
162 // Have fun...
163
164 // Get a new random direction
165 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
166
167 // Randomize the parametrized direction
168 float incident_part = randomizer.drand() * 2 - 1,
169 normal_part = randomizer.drand();
170
171 // Span the incident plane
172 Vec3f incident_direction,
173 new_direction;
174 if (projected == normal)
175 new_direction = normal;
176 else
177 {
178 incident_direction = origray.dir + projected;
179 Normalize(incident_direction);
180
181 // Deparametrize this direction
182 new_direction = incident_part * incident_direction + normal_part * normal;
183 if (Nonzero(new_direction))
184 Normalize(new_direction);
185 }
186
187 // Calculate the inverse BRDF and act accordingly
188 // We do this in ways of the Schlick model (Compare Schlick Shader for further details)
189 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
190
191 float specular;
192
193 // Get the halfway vector in between incoming and outgoing ray
194 Vec3f H = new_direction - origray.dir;
195 if (Nonzero(H))
196 {
197 Normalize(H);
198
199 float t, u, v, x, w;
200
201 // Calculation of various scaling factors
202 t = fabs(Dot(H, normal));
203 u = fabs(Dot(-origray.dir, H));
204 v = fabs(Dot(-origray.dir, normal));
205 x = fabs(Dot(new_direction, normal));
206 w = fabs(Dot(H - normal, normal));
207
208 // Precomputation of w^2
209 w = w * w;
210
211 specular = SchlickTerm(t, u, v, x, w);
212 }
213 else
214 specular = 0.0f;
215
216 if (specular)
217 {
218 if (gt)
219 {
220 ray.org = std_ray_org;
221 ray.t = Infinity;
222 ray.hit = NULL;
223 ray.dir = new_direction - 2 * Dot(new_direction, normal);
224
225 // Cosine of the incident angle
226 float cosa = Dot(normal, ray.dir),
227 new_refrac_index;
228
229 if (into)
230 new_refrac_index = origray.refrac_index + (material->refrac_index - 1.0f);
231 else
232 new_refrac_index = origray.refrac_index - (material->refrac_index - 1.0f);
233
234 if (new_refrac_index)
235 {
236 float n = origray.refrac_index / new_refrac_index,
237 cosb2 = 1.0f - n * n * (1.0f - cosa*cosa);
238
239 if (cosb2 < 0.0f)
240 {
241 // Total reflection
242 gm += gt;
243 gt = 0;
244 }
245 else
246 {
247 // Here again, we use the Schlick approximation
248 // (Hey, we are in the Schlick shader ;-) !)
249
250 float cosb = sqrtf(cosb2),
251 c = (n - 1) / (n + 1),
252 schlick;
253
254 c *= c;
255
256 // We use the angle in the less dense medium for Schlick's term
257 if (into)
258 schlick = c + (1 - c) * powf((1 - cosa), 5);
259 else
260 schlick = c + (1 - c) * powf((1 - cosb), 5);
261
262 schlick *= gt;
263
264 gm += schlick;
265 gt -= schlick;
266
267 ray.dir = (n * cosa - cosb) * normal + n * ray.dir;
268
269 // Apply refraction index now as we are sure to have changed the medium
270 ray.refrac_index = new_refrac_index;
271
272 glossy_refraction_color += (15.0f * specular) * scene->RayTrace(ray);
273
274 // Apply optional color filter
275 if (material->glossy_transparency_filter != UNDEF)
276 {
277 glossy_refraction_color = (material->glossy_transparency_filter
278 * (glossy_refraction_color.x + glossy_refraction_color.y + glossy_refraction_color.z) / 3.0f)
279 * material->color
280 + (1.0f - material->glossy_transparency_filter) * glossy_refraction_color;
281 }
282 }
283 }
284 }
285
286 if (gm)
287 {
288 ray.org = std_ray_org;
289 ray.t = Infinity;
290 ray.hit = NULL;
291 ray.dir = new_direction;
292
293 // What is this 15.0f about? Well... we look out in all directions where there is only little
294 // irradiance according to the BRDF. This value compensates for that.
295 glossy_reflection_color += (15.0f * specular) * scene->RayTrace(ray);
296
297 // Apply optional color filter
298 if (material->glossy_mirror_filter != UNDEF)
299 {
300 glossy_reflection_color = (material->glossy_mirror_filter
301 * (glossy_reflection_color.x + glossy_reflection_color.y + glossy_reflection_color.z) / 3.0f)
302 * material->color
303 + (1.0f - material->glossy_mirror_filter) * glossy_reflection_color;
304 }
305 }
306 }
307 else
308 {
309 glossy_reflection_color = Vec3f(0);
310 glossy_refraction_color = Vec3f(0);
311 }
312 }
313
314 // Apply optional color filter
315 if (material->refrac_filter != UNDEF)
316 {
317 refractive_color = (material->refrac_filter * (refractive_color.x + refractive_color.y + refractive_color.z) / 3.0f)
318 * material->color
319 + (1.0f - material->refrac_filter) * refractive_color;
320 }
321
322 // Apply optional color filter
323 if (material->mirror_filter != UNDEF)
324 {
325 reflective_color = (material->mirror_filter * (reflective_color.x + reflective_color.y + reflective_color.z) / 3.0f)
326 * material->color
327 + (1.0f - material->mirror_filter) * reflective_color;
328 }
329
330 return t * refractive_color + m * reflective_color + gm * glossy_reflection_color + gt * glossy_refraction_color;
331 }
332
333
334 virtual Vec3f Shade(Ray &ray)
335 {
336 Vec3f Lr(0,0,0),
337 Ll(0.0f),
338 H,
339 N = ray.hit->GetNormal(ray),
340 pixelColor;
341 Ray light_ray;
342 float Lr_fac, // Factor of Lr, for time improvement
343 t, // +
344 u, // |
345 v, // > cosines of various angles between normal vectors
346 x, // |
347 w; // +
348 bool into;
349
350 into = true;
351 if (Dot(N,ray.dir) > 0)
352 {
353 N = -N;
354 into = false;
355 }
356
357 light_ray_org_std = ray.org + ray.t * ray.dir;
358
359 for (vector<Procedural*>::iterator it = material->procedural.begin(); it != material->procedural.end(); ++it)
360 if ((*it)->ProvidesBumpMap())
361 (*it)->BumpMap(N, light_ray_org_std);
362
363 #define NEIGHBORS 1000
364 #define MAXDIST2 1.0f
365 int count = 0;
366
367 Normalize(ray.dir);
368
369 // Integrate over a neighborhood of NEIGHBORS elements within MAXDIST2
370 PhotonHeap heap(light_ray_org_std, NEIGHBORS, MAXDIST2);
371 photon_tree->GetNextPhotons(heap);
372
373 if (material->c1 != UNDEF)
374 {
375 for (int i = 0; i < NEIGHBORS; ++i)
376 if (heap[i])
377 {
378 // Check whether pure photon mapping or simple subsurface scattering should be applied
379 Vec3f photon_dir = heap[i]->point - light_ray_org_std,
380 photon_len = photon_dir;
381
382 if (Nonzero(photon_dir))
383 Normalize(photon_dir);
384
385 if (Dot(N, photon_dir) < -0.15)
386 {
387 const float maxdist = sqrtf(MAXDIST2);
388
389 // We are in a frustrum of 2*80 degrees wrt the surface normal, this is
390 // most probably a problem for subsurface scattering!
391 // Subsurface scattering? Err... well... sort of. This is actually a quite risky approximation
392 // and does only hold for rather thick objects. But hey, we don't want any points for it, okay?
393 // It is simply for the sake of cool images!
394 float weight = 0;
395
396 if (Nonzero(photon_len))
397 weight = Length(photon_len)/maxdist;
398
399 Lr += (1.0f - weight) * material->subsurface_scat * Product(heap[i]->energy, material->color);
400 }
401 else
402 {
403 // These photons were gathered around the hitpoint and will be responsible for
404 // "classical" indirect illumination
405 light_ray.dir = -heap[i]->dir;
406 Ll = heap[i]->energy;
407
408 count++;
409
410 if (Nonzero(light_ray.dir))
411 {
412 Normalize(light_ray.dir);
413
414 // This was a test we did: Lambertian illumination. We got better results when we weighted the
415 // incoming illumination directions and energies, why we discarded this one
416 #if 0
417 float weight = Dot(light_ray.dir, ray.dir);
418 if (weight > 0.0f)
419 Ll *= weight;
420 else
421 Ll = Vec3f(0.0f);
422 #endif
423
424 Lr_fac = 0;
425
426 // Get the halfway vector in between incoming and outgoing ray
427 H = light_ray.dir - ray.dir;
428 Normalize(H);
429
430 // Calculation of various scaling factors
431 t = fabs(Dot(H, N));
432 u = fabs(Dot(light_ray.dir, H));
433 v = fabs(Dot(light_ray.dir, N));
434 x = fabs(Dot(-ray.dir, N));
435 w = fabs(Dot(H - N, N));
436
437 // Precomputation of w^2
438 w = w * w;
439
440 Lr_fac = SchlickTerm(t, u, v, x, w);
441
442 pixelColor = Product(material->color, Ll);
443
444 // This is the one "bitmap" texture
445 if (material->texture)
446 {
447 float u = 0,
448 v = 0;
449
450 ray.hit->GetUV(ray, u, v);
451 v = 1.0f - v;
452
453 pixelColor = Product(pixelColor, material->texture->GetTexel(u,v));
454 }
455
456 // Check for (multiple) procedural textures to be applied...
457 for (vector<Procedural*>::iterator it = material->procedural.begin(); it != material->procedural.end(); ++it)
458 if ((*it)->ProvidesTexture())
459 pixelColor = Product(pixelColor, (*it)->GetColor(light_ray_org_std));
460
461 Lr += Lr_fac * pixelColor;
462 }
463 }
464 }
465
466 float scale = heap.maxdist2 * M_PI;
467
468 if (count && scale)
469 Lr *= 1.0f / (scale * static_cast<float>(count));
470 }
471
472 if (mirror || transparency || glossy_mirror || glossy_transparency)
473 {
474 Lr *= (1.0f - mirror - transparency - glossy_mirror - glossy_transparency);
475 Lr += FancyColor(ray, N, into);
476 }
477
478 Lr = Min(Vec3f(1.0f), Max(Vec3f(0.0), Lr));
479
480 Lr += ambient;
481 return Lr;
482 };
483 };
484
485 #endif
syntax highlighted by Code2HTML, v. 0.9.1