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