1 #ifndef SCHLICKPHOTONDISTRIBUTOR_HXX
  2 #define SCHLICKPHOTONDISTRIBUTOR_HXX
  3 
  4 #include "PhotonDistributor.hxx"
  5 
  6 class SchlickPhotonDistributor : public PhotonDistributor
  7 {
  8 private:
  9 	Randomizer randomizer;
 10 public:
 11 
 12 	Vec3f	   hit_point;           // the hit point on the surface
 13 	float      p12,                 // square of the isotropy factor for primary surface
 14 		   p22;                 // square of the isotropy factor for primary surface
 15 	float      mirror,              // Material based mirroring    percentage
 16 		   glossy_mirror,       // Material based glossy reflection percentage
 17 		   glossy_transparency, // Material based glossy refraction percentage
 18 		   transparency;        // Material based transparency percentage
 19 
 20 	SchlickPhotonDistributor(Scene *scene, Material *material)
 21 		: PhotonDistributor(scene, material)
 22 // Optionally fix the random seed for parallization
 23 #ifdef RANDOMSEED
 24 		, randomizer(RANDOMSEED)
 25 #endif
 26 	{
 27 		// Initialization of reflection and refraction
 28 		if (material->mirror > 1.0 || material->transparency > 1.0 || material->mirror + material->transparency > 1.0)
 29 			cerr << "WARNING: Material '" << material << "' has wrong reflection/refraction percentages (> 1.0)!" << endl;
 30 
 31 		mirror	      = (material->mirror == UNDEF) ? 0.0f : material->mirror;
 32 		glossy_mirror       = (material->glossy_mirror       == UNDEF) ? 0.0f : material->glossy_mirror;
 33 		glossy_transparency = (material->glossy_transparency == UNDEF) ? 0.0f : material->glossy_transparency;
 34 		transparency	= (material->transparency == UNDEF) ? 0.0f : material->transparency;
 35 
 36 		// Precomputation of p^2
 37 		p12 = material->p1 * material->p1;
 38 		p22 = material->p2 * material->p2;
 39 	};
 40 
 41 	virtual ~SchlickPhotonDistributor()
 42 	{};
 43 
 44 	// As this is a rather common method, it is only maintained once
 45 	#include "SchlickTerm.include"
 46 
 47 	virtual void FancyDistribution(TravellingPhoton &photon, vector<Photon*> &list, const Vec3f &normal, const bool &into, const float &what_to_do)
 48 	{
 49 		float m = mirror,
 50 		      t = transparency;
 51 
 52 		// Get the real distribution of the mirror coefficient first
 53 		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 54 		float cosa	     = 1.0,
 55 		      cosb	     = 1.0,
 56 		      cosb2,
 57 		      new_refrac_index = 1.0,
 58 		      c,
 59 		      n		= 1.0,
 60 		      schlick;
 61 
 62 		if (t)
 63 		{
 64 			// Cosine of the incident angle
 65 			cosa = Dot(normal, -photon.dir);
 66 
 67 			if (into)
 68 				new_refrac_index = photon.refrac_index + (material->refrac_index - 1.0f);
 69 			else
 70 				new_refrac_index = photon.refrac_index - (material->refrac_index - 1.0f);
 71 
 72 			n     = photon.refrac_index / new_refrac_index,
 73 			cosb2 = 1.0f - n * n * (1.0f - cosa*cosa);
 74 
 75 			if (cosb2 < 0.0f)
 76 				// Total reflection
 77 				m += t;
 78 			else
 79 			{
 80 				cosb = sqrtf(cosb2),
 81 				c    = (n - 1) / (n + 1);
 82 
 83 				c *= c;
 84 
 85 				// We use the angle in the less dense medium for Schlick's term
 86 				if (into)
 87 					schlick = c + (1 - c) * powf((1 - cosa), 5);
 88 				else
 89 					schlick = c + (1 - c) * powf((1 - cosb), 5);
 90 
 91 				schlick *= t;
 92 
 93 				m += schlick;
 94 				t -= schlick;
 95 			}
 96 		}
 97 
 98 		// Reset the photon's path
 99 		photon.org = photon.org + photon.t * photon.dir;
100 		photon.t   = Infinity;
101 		photon.hit = NULL;
102 
103 		// Evaluate against the random parameter
104 		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105 		if (what_to_do + t > 1.0f)
106 		{
107 			// Transparency
108 			// ~ ~ ~ ~ ~ ~
109 			photon.dir = (n * cosa - cosb) * normal + n * photon.dir;
110 
111 			// Apply refraction index now as we are sure to have changed the medium
112 			photon.refrac_index = new_refrac_index;
113 
114 			if (material->refrac_filter != UNDEF)
115 				photon.energy = Product(photon.energy, material->refrac_filter       * material->color
116 								     + (1 - material->refrac_filter) * Vec3f(1.0f));
117 		}
118 		else
119 		{
120 			// Mirror
121 			// ~ ~ ~
122 			// Project the photon direction onto the normal vector...
123 			Vec3f projected = Dot(normal, -photon.dir) * normal;
124 
125 			// ...and mirror away
126 			photon.dir += 2 * projected;
127 			Normalize(photon.dir);
128 
129 			if (material->mirror_filter != UNDEF)
130 				photon.energy = Product(photon.energy, material->mirror_filter       * material->color
131 								     + (1 - material->mirror_filter) * Vec3f(1.0f));
132 		}
133 
134 		scene->PhotonTrace(photon, list);
135 	}
136 
137 	virtual void Distribute(TravellingPhoton &photon, vector<Photon*> &list)
138 	{
139 
140 		Vec3f H,
141 		      N = photon.hit->GetNormal(photon);
142 		float t,		 // +
143 		      u,		 //  |
144 		      v,		 //   > cosines of various angles between normal vectors
145 		      x,		 //  |
146 		      w;		 // +
147 		bool  into;
148 
149 		into = true;
150 		if (Dot(N,photon.dir) > 0)
151 		{
152 			N = -N;
153 			into = false;
154 		}
155 
156 		hit_point = photon.org + photon.t * photon.dir;
157 
158 		for (vector<Procedural*>::iterator it = material->procedural.begin(); it != material->procedural.end(); ++it)
159 			if ((*it)->ProvidesBumpMap())
160 				(*it)->BumpMap(N, hit_point);
161 
162 		float what_to_do = randomizer.drand();
163 
164 		if (what_to_do < 1.0f - mirror - transparency)
165 		{
166 			// Here, we do some approximation for specular reflectance:
167 			// - We choose a random reflection direction within both the upper hemisphere
168 			//   and the incident plane
169 			// - Then, we calculate the BRDF according to this point (i.e. the inverse BRDF!)
170 			// - The photon is now split in two: One is reflected carrying to the specular energy,
171 			//   while the other is absorbed by the surface keepig the remianing energy for the
172 			//   photon map
173 
174 			// Get a new random direction
175 			// ~~~~~~~~~~~~~~~~~~~~~~~~~~
176 
177 		       // Randomize the parametrized direction
178 			float incident_part = randomizer.drand() * 2 - 1,
179 			      normal_part   = randomizer.drand();
180 
181 			// Span the incident plane
182 			Vec3f incident_direction,
183 			      new_direction;
184 
185 			incident_direction = photon.dir + Dot(N, -photon.dir) * N;
186 
187 			if (Nonzero(incident_direction))
188 			{
189 				Normalize(incident_direction);
190 
191 				// Deparametrize this direction
192 				new_direction = incident_part * incident_direction + normal_part * N;
193 				Normalize(new_direction);
194 			}
195 			else
196 				new_direction = N;
197 
198 			// Calculate the inverse BRDF and act accordingly
199 			// We do this in ways of the Schlick model (Compare Schlick Shader for further details)
200 			// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
201 
202 			float specular;
203 
204 			// Get the halfway vector in between incoming and outgoing ray
205 			H = new_direction - photon.dir;
206 			if (Nonzero(H))
207 			{
208 				Normalize(H);
209 
210 				// Calculation of various scaling factors
211 				t = fabs(Dot(H,	     N));
212 				u = fabs(Dot(-photon.dir,   H));
213 				v = fabs(Dot(-photon.dir,   N));
214 				x = fabs(Dot(new_direction, N));
215 				w = fabs(Dot(H - N,	 N));
216 
217 				// Precomputation of w^2
218 				w = w * w;
219 
220 				specular = SchlickTerm(t, u, v, x, w);
221 			}
222 			else
223 				specular = 0.0f;
224 
225 			// Now perform the split and make sure that the reflected photon carries color information etc.
226 			Vec3f pixelColor = material->color;
227 
228 			for (vector<Procedural*>::iterator it = material->procedural.begin(); it != material->procedural.end(); ++it)
229 				if ((*it)->ProvidesTexture())
230 					pixelColor = Product(pixelColor, (*it)->GetColor(hit_point));
231 
232 			photon.org = hit_point;
233 
234 			if (glossy_mirror < 1.0)
235 			{
236 				// A new slim photon is created to be inserted into the photon map
237 				Photon *absorbed_photon = new Photon(photon);
238 				absorbed_photon->energy *= (1.0f - glossy_mirror) * (1.0f - specular);
239 
240 				list.push_back(absorbed_photon);
241 			}
242 
243 			if (specular)
244 			{
245 				// The old photon is used to follow the further path
246 				photon.energy = Product((glossy_mirror + specular - glossy_mirror * specular) * photon.energy, pixelColor);
247 
248 				if (photon.energy.x + photon.energy.y + photon.energy.z > MINIMAL_PHOTONENERGY)
249 				{
250 					photon.dir = new_direction;
251 					photon.t   = Infinity;
252 					photon.hit = NULL;
253 
254 					scene->PhotonTrace(photon, list);
255 				}
256 			}
257 		}
258 		else 
259 			// Any other effect is performed
260 			FancyDistribution(photon, list, N, into, what_to_do);
261 
262 	};
263 };
264 
265 #endif


syntax highlighted by Code2HTML, v. 0.9.1