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