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