1 #ifndef SCHLICKSHADER_HXX
2 #define SCHLICKSHADER_HXX
3
4 #include "Shader.hxx"
5 #include "Material.hxx"
6
7 #include "defines.h"
8
9 // This shader implements the illumination model described by Christophe Schlick.
10 // It is based on the Cook-Torrance model and approximates the specular term, s.t.
11 // no Fresnel term is needed any longer.
12 class SchlickShader : public Shader
13 {
14 Randomizer randomizer;
15 public:
16 Vec3f ambient, // ambient radiance
17 light_ray_org_std; // template for light_ray.org, to calculate it only once
18 float p12, // square of the isotropy factor for primary surface
19 p22; // square of the isotropy factor for primary surface
20 float mirror, // Material based mirroring percentage
21 glossy_mirror, // Material based glossy reflection percentage
22 glossy_transparency, // Material based glossy refraction percentage
23 transparency; // Material based transparency percentage
24
25 SchlickShader(Scene *scene, Material *material)
26 : Shader(scene, material)
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 ~SchlickShader()
46 {};
47
48 // As this is a rather common method, it is only maintained once
49 #include "SchlickTerm.include"
50
51 virtual Vec3f FancyColor(const Ray &origray, const Vec3f &normal, const bool &into)
52 {
53 Ray ray;
54 float m = mirror,
55 t = transparency,
56 gm = glossy_mirror,
57 gt = glossy_transparency;
58
59 // Project the ray direction onto the normal vector...
60 Vec3f projected = Dot(normal, -origray.dir) * normal,
61 std_ray_org = origray.org + origray.t * origray.dir;
62
63
64 // Start with the reflection calculation (which is needed anyway for t/m)
65 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
66 Vec3f reflective_color = Vec3f(0),
67 refractive_color = Vec3f(0),
68 glossy_reflection_color = Vec3f(0),
69 glossy_refraction_color = Vec3f(0);
70
71 if (t || m)
72 {
73 ray.org = std_ray_org;
74 ray.t = Infinity;
75 ray.hit = NULL;
76
77 // ...and mirror away
78 ray.dir = origray.dir + 2 * projected;
79 if (Nonzero(ray.dir))
80 {
81 Normalize(ray.dir);
82
83 ray.refrac_index = origray.refrac_index;
84 reflective_color = scene->RayTrace(ray);
85 }
86 else
87 reflective_color = Vec3f(0);
88 }
89
90 // Now perform the transparency part
91 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92 if (t)
93 {
94 ray.org = std_ray_org;
95 ray.t = Infinity;
96 ray.hit = NULL;
97
98 // Cosine of the incident angle
99 float cosa = Dot(normal, -origray.dir),
100 new_refrac_index;
101
102 if (into)
103 new_refrac_index = origray.refrac_index + (material->refrac_index - 1.0f);
104 else
105 new_refrac_index = origray.refrac_index - (material->refrac_index - 1.0f);
106
107 float n = origray.refrac_index / new_refrac_index,
108
109 /*
110 * According to Snell's law we know that
111 * sina/sinb = n2/n1 = 1/n,
112 * hence
113 * (sina/sinb)^2 = (1/n)^2
114 * or
115 * sin^2 b = sin^2 a * n^2.
116 * As
117 * sin^2 a + cos^2 a = 1,
118 * we know that
119 * sin^2 b = n^2 * (1 - cos^2 a)
120 * and thus
121 * cos^2 b = 1 - n^2 * (1 - cos^2 a).
122 */
123
124 cosb2 = 1.0f - n * n * (1.0f - cosa*cosa);
125
126 if (cosb2 < 0.0f)
127 // Total reflection
128 m += t;
129 else
130 {
131 // Here again, we use the Schlick approximation
132 // (Hey, we are in the Schlick shader ;-) !)
133
134 float cosb = sqrtf(cosb2),
135 c = (n - 1) / (n + 1),
136 schlick;
137
138 c *= c;
139
140 // We use the angle in the less dense medium for Schlick's term
141 if (into)
142 schlick = c + (1 - c) * powf((1 - cosa), 5);
143 else
144 schlick = c + (1 - c) * powf((1 - cosb), 5);
145
146 schlick *= t;
147
148 m += schlick;
149 t -= schlick;
150
151 ray.dir = (n * cosa - cosb) * normal + n * origray.dir;
152
153 // Apply refraction index now as we are sure to have changed the medium
154 ray.refrac_index = new_refrac_index;
155 }
156
157 refractive_color = scene->RayTrace(ray);
158 }
159
160 if (gm || gt)
161 {
162 // Glossy reflection... think what? Yes... Same as in the photon distributor!
163 // Have fun...
164
165 // Get a new random direction
166 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
167
168 // Randomize the parametrized direction
169 float incident_part = randomizer.drand() * 2 - 1,
170 normal_part = randomizer.drand();
171
172 // Span the incident plane
173 Vec3f incident_direction,
174 new_direction;
175 if (projected == normal)
176 new_direction = normal;
177 else
178 {
179 incident_direction = origray.dir + projected;
180 Normalize(incident_direction);
181
182 // Deparametrize this direction
183 new_direction = incident_part * incident_direction + normal_part * normal;
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 float n = origray.refrac_index / new_refrac_index,
235 cosb2 = 1.0f - n * n * (1.0f - cosa*cosa);
236
237 if (cosb2 < 0.0f)
238 // Total reflection
239 gm += gt;
240 else
241 {
242 // Here again, we use the Schlick approximation
243 // (Hey, we are in the Schlick shader ;-) !)
244
245 float cosb = sqrtf(cosb2),
246 c = (n - 1) / (n + 1),
247 schlick;
248
249 c *= c;
250
251 // We use the angle in the less dense medium for Schlick's term
252 if (into)
253 schlick = c + (1 - c) * powf((1 - cosa), 5);
254 else
255 schlick = c + (1 - c) * powf((1 - cosb), 5);
256
257 schlick *= gt;
258
259 gm += schlick;
260 gt -= schlick;
261
262 ray.dir = (n * cosa - cosb) * normal + n * origray.dir;
263
264 // Apply refraction index now as we are sure to have changed the medium
265 ray.refrac_index = new_refrac_index;
266 }
267
268 // What is this 15.0f about? Well... we look out in all directions where there is only little
269 // irradiance according to the BRDF. This value compensates for that.
270 glossy_refraction_color += (15.0f * specular) * scene->RayTrace(ray);
271
272 // Apply optional color filter
273 if (material->glossy_transparency_filter != UNDEF)
274 {
275 glossy_refraction_color = (material->glossy_transparency_filter
276 * (glossy_refraction_color.x + glossy_refraction_color.y + glossy_refraction_color.z) / 3.0f)
277 * material->color
278 + (1.0f - material->glossy_transparency_filter) * glossy_refraction_color;
279 }
280 }
281
282 if (gm)
283 {
284 ray.org = std_ray_org;
285 ray.t = Infinity;
286 ray.hit = NULL;
287 ray.dir = new_direction;
288
289 // What is this 15.0f about? Well... we look out in all directions where there is only little
290 // irradiance according to the BRDF. This value compensates for that.
291 glossy_reflection_color += (15.0f * specular) * scene->RayTrace(ray);
292
293 // Apply optional color filter
294 if (material->glossy_mirror_filter != UNDEF)
295 {
296 glossy_reflection_color = (material->glossy_mirror_filter
297 * (glossy_reflection_color.x + glossy_reflection_color.y + glossy_reflection_color.z) / 3.0f)
298 * material->color
299 + (1.0f - material->glossy_mirror_filter) * glossy_reflection_color;
300 }
301 }
302 }
303 else
304 {
305 glossy_reflection_color = Vec3f(0);
306 glossy_refraction_color = Vec3f(0);
307 }
308 }
309
310 // Apply optional color filter
311 if (material->refrac_filter != UNDEF)
312 {
313 refractive_color = (material->refrac_filter * (refractive_color.x + refractive_color.y + refractive_color.z) / 3.0f)
314 * material->color
315 + (1.0f - material->refrac_filter) * refractive_color;
316 }
317
318 // Apply optional color filter
319 if (material->mirror_filter != UNDEF)
320 {
321 reflective_color = (material->mirror_filter * (reflective_color.x + reflective_color.y + reflective_color.z) / 3.0f)
322 * material->color
323 + (1.0f - material->mirror_filter) * reflective_color;
324 }
325
326 return t * refractive_color + m * reflective_color + gm * glossy_reflection_color + gt * glossy_refraction_color;
327 }
328
329 virtual Vec3f Shade(Ray &ray)
330 {
331 Vec3f Lr(0,0,0),
332 Ll,
333 H,
334 N = ray.hit->GetNormal(ray),
335 pixelColor;
336 Ray light_ray;
337 float Lr_fac, // Factor of Lr, for time improvement
338 t, // +
339 u, // |
340 v, // > cosines of various angles between normal vectors
341 x, // |
342 w; // +
343 bool into;
344
345 into = true;
346 if (Dot(N,ray.dir) > 0)
347 {
348 N = -N;
349 into = false;
350 }
351
352 light_ray_org_std = ray.org + ray.t * ray.dir;
353
354 for (vector<Procedural*>::iterator it = material->procedural.begin(); it != material->procedural.end(); ++it)
355 if ((*it)->ProvidesBumpMap())
356 (*it)->BumpMap(N, light_ray_org_std);
357
358 if (material->c1 != UNDEF)
359 {
360 for (std::vector<Light*>::iterator it = scene->lights.begin(); it != scene->lights.end(); ++it)
361 {
362 light_ray.org = light_ray_org_std;
363
364 #ifdef AREALIGHT
365 for (int i = 0; i < NUM_AREA_SAMPLES; ++i)
366 #endif
367 {
368 (*it)->Illuminate(light_ray, Ll);
369 Lr_fac = 0;
370
371 Vec3f occolor = (*it)->intensity.NormTo(1.0);
372
373 if (!scene->Occluded(light_ray, occolor))
374 {
375 // Get the halfway vector in between incoming and outgoing ray
376 H = light_ray.dir - ray.dir;
377 Normalize(H);
378
379 // Calculation of various scaling factors
380 t = fabs(Dot(H, N));
381 u = fabs(Dot(light_ray.dir, H));
382 v = fabs(Dot(light_ray.dir, N));
383 x = fabs(Dot(-ray.dir, N));
384 w = fabs(Dot(H - N, N));
385
386 // Precomputation of w^2
387 w = w * w;
388
389 Lr_fac += SchlickTerm(t, u, v, x, w);
390
391 pixelColor = Product(material->color, Ll);
392
393 if (material->texture)
394 {
395 float u = 0,
396 v = 0;
397
398 ray.hit->GetUV(ray, u, v);
399 v = 1.0f - v;
400
401 pixelColor = Product(pixelColor, material->texture->GetTexel(u,v));
402 }
403
404 for (vector<Procedural*>::iterator it = material->procedural.begin(); it != material->procedural.end(); ++it)
405 if ((*it)->ProvidesTexture())
406 pixelColor = Product(pixelColor, (*it)->GetColor(light_ray_org_std));
407
408 pixelColor = Product(pixelColor, occolor);
409
410 Lr += Lr_fac * pixelColor;
411 }
412 }
413 }
414
415 #ifdef AREALIGHT
416 Lr /= NUM_AREA_SAMPLES;
417 #endif
418 }
419
420 if (mirror || transparency || glossy_mirror || glossy_transparency)
421 {
422 Lr *= (1.0f - mirror - transparency - glossy_mirror - glossy_transparency);
423 Lr += FancyColor(ray, N, into);
424 }
425
426 Lr += ambient;
427 return Lr;
428 };
429 };
430
431 #endif
syntax highlighted by Code2HTML, v. 0.9.1