6e728f3b |
1 | #ifdef _MSC_VER |
2 | #define PATH_TRACING // just for editing in MS VS |
3 | |
4 | #define in |
5 | #define out |
6 | #define inout |
7 | |
8 | typedef struct { float x; float y; } vec2; |
9 | typedef struct { float x; float y; float z; } vec3; |
10 | typedef struct { float x; float y; float z; float w; } vec4; |
11 | #endif |
12 | |
189f85a3 |
13 | #ifdef PATH_TRACING |
14 | |
15 | /////////////////////////////////////////////////////////////////////////////////////// |
16 | // Specific data types |
17 | |
18 | //! Describes local space at the hit point (visualization space). |
19 | struct SLocalSpace |
20 | { |
21 | //! Local X axis. |
22 | vec3 AxisX; |
23 | |
24 | //! Local Y axis. |
25 | vec3 AxisY; |
26 | |
27 | //! Local Z axis. |
28 | vec3 AxisZ; |
29 | }; |
30 | |
31 | //! Describes material properties (BSDF). |
05aa616d |
32 | struct SBSDF |
189f85a3 |
33 | { |
05aa616d |
34 | //! Weight of coat specular/glossy BRDF. |
35 | vec4 Kc; |
36 | |
37 | //! Weight of base diffuse BRDF. |
189f85a3 |
38 | vec4 Kd; |
39 | |
05aa616d |
40 | //! Weight of base specular/glossy BRDF. |
41 | vec4 Ks; |
189f85a3 |
42 | |
05aa616d |
43 | //! Weight of base specular/glossy BTDF. |
189f85a3 |
44 | vec3 Kt; |
45 | |
05aa616d |
46 | //! Fresnel coefficients of coat layer. |
47 | vec3 FresnelCoat; |
189f85a3 |
48 | |
05aa616d |
49 | //! Fresnel coefficients of base layer. |
50 | vec3 FresnelBase; |
189f85a3 |
51 | }; |
52 | |
53 | /////////////////////////////////////////////////////////////////////////////////////// |
54 | // Support subroutines |
55 | |
56 | //======================================================================= |
6e728f3b |
57 | // function : buildLocalSpace |
189f85a3 |
58 | // purpose : Generates local space for the given normal |
59 | //======================================================================= |
6e728f3b |
60 | SLocalSpace buildLocalSpace (in vec3 theNormal) |
189f85a3 |
61 | { |
6e728f3b |
62 | vec3 anAxisX = vec3 (theNormal.z, 0.f, -theNormal.x); |
63 | vec3 anAxisY = vec3 (0.f, -theNormal.z, theNormal.y); |
189f85a3 |
64 | |
65 | float aSqrLenX = dot (anAxisX, anAxisX); |
66 | float aSqrLenY = dot (anAxisY, anAxisY); |
67 | |
68 | if (aSqrLenX > aSqrLenY) |
69 | { |
70 | anAxisX *= inversesqrt (aSqrLenX); |
71 | anAxisY = cross (anAxisX, theNormal); |
72 | } |
73 | else |
74 | { |
75 | anAxisY *= inversesqrt (aSqrLenY); |
76 | anAxisX = cross (anAxisY, theNormal); |
77 | } |
78 | |
79 | return SLocalSpace (anAxisX, anAxisY, theNormal); |
80 | } |
81 | |
82 | //======================================================================= |
83 | // function : toLocalSpace |
84 | // purpose : Transforms the vector to local space from world space |
85 | //======================================================================= |
86 | vec3 toLocalSpace (in vec3 theVector, in SLocalSpace theSpace) |
87 | { |
88 | return vec3 (dot (theVector, theSpace.AxisX), |
89 | dot (theVector, theSpace.AxisY), |
90 | dot (theVector, theSpace.AxisZ)); |
91 | } |
92 | |
93 | //======================================================================= |
94 | // function : fromLocalSpace |
95 | // purpose : Transforms the vector from local space to world space |
96 | //======================================================================= |
97 | vec3 fromLocalSpace (in vec3 theVector, in SLocalSpace theSpace) |
98 | { |
99 | return theVector.x * theSpace.AxisX + |
100 | theVector.y * theSpace.AxisY + |
101 | theVector.z * theSpace.AxisZ; |
102 | } |
103 | |
104 | //======================================================================= |
105 | // function : convolve |
106 | // purpose : Performs a linear convolution of the vector components |
107 | //======================================================================= |
108 | float convolve (in vec3 theVector, in vec3 theFactor) |
109 | { |
110 | return dot (theVector, theFactor) * (1.f / max (theFactor.x + theFactor.y + theFactor.z, 1e-15f)); |
111 | } |
112 | |
189f85a3 |
113 | //======================================================================= |
114 | // function : fresnelSchlick |
115 | // purpose : Computes the Fresnel reflection formula using |
116 | // Schlick's approximation. |
117 | //======================================================================= |
118 | vec3 fresnelSchlick (in float theCosI, in vec3 theSpecularColor) |
119 | { |
120 | return theSpecularColor + (UNIT - theSpecularColor) * pow (1.f - theCosI, 5.f); |
121 | } |
122 | |
123 | //======================================================================= |
124 | // function : fresnelDielectric |
125 | // purpose : Computes the Fresnel reflection formula for dielectric in |
126 | // case of circularly polarized light (Based on PBRT code). |
127 | //======================================================================= |
128 | float fresnelDielectric (in float theCosI, |
129 | in float theCosT, |
130 | in float theEtaI, |
131 | in float theEtaT) |
132 | { |
133 | float aParl = (theEtaT * theCosI - theEtaI * theCosT) / |
134 | (theEtaT * theCosI + theEtaI * theCosT); |
135 | |
136 | float aPerp = (theEtaI * theCosI - theEtaT * theCosT) / |
137 | (theEtaI * theCosI + theEtaT * theCosT); |
138 | |
139 | return (aParl * aParl + aPerp * aPerp) * 0.5f; |
140 | } |
141 | |
142 | #define ENVIRONMENT_IOR 1.f |
143 | |
144 | //======================================================================= |
145 | // function : fresnelDielectric |
146 | // purpose : Computes the Fresnel reflection formula for dielectric in |
147 | // case of circularly polarized light (based on PBRT code) |
148 | //======================================================================= |
149 | float fresnelDielectric (in float theCosI, in float theIndex) |
150 | { |
05aa616d |
151 | float aFresnel = 1.f; |
152 | |
189f85a3 |
153 | float anEtaI = theCosI > 0.f ? 1.f : theIndex; |
154 | float anEtaT = theCosI > 0.f ? theIndex : 1.f; |
155 | |
05aa616d |
156 | float aSinT2 = (anEtaI * anEtaI) / (anEtaT * anEtaT) * (1.f - theCosI * theCosI); |
189f85a3 |
157 | |
05aa616d |
158 | if (aSinT2 < 1.f) |
189f85a3 |
159 | { |
05aa616d |
160 | aFresnel = fresnelDielectric (abs (theCosI), sqrt (1.f - aSinT2), anEtaI, anEtaT); |
189f85a3 |
161 | } |
162 | |
05aa616d |
163 | return aFresnel; |
189f85a3 |
164 | } |
165 | |
166 | //======================================================================= |
167 | // function : fresnelConductor |
168 | // purpose : Computes the Fresnel reflection formula for conductor in case |
169 | // of circularly polarized light (based on PBRT source code) |
170 | //======================================================================= |
171 | float fresnelConductor (in float theCosI, in float theEta, in float theK) |
172 | { |
173 | float aTmp = 2.f * theEta * theCosI; |
174 | |
175 | float aTmp1 = theEta * theEta + theK * theK; |
176 | |
177 | float aSPerp = (aTmp1 - aTmp + theCosI * theCosI) / |
178 | (aTmp1 + aTmp + theCosI * theCosI); |
179 | |
180 | float aTmp2 = aTmp1 * theCosI * theCosI; |
181 | |
182 | float aSParl = (aTmp2 - aTmp + 1.f) / |
183 | (aTmp2 + aTmp + 1.f); |
184 | |
185 | return (aSPerp + aSParl) * 0.5f; |
186 | } |
187 | |
188 | #define FRESNEL_SCHLICK -0.5f |
189 | #define FRESNEL_CONSTANT -1.5f |
190 | #define FRESNEL_CONDUCTOR -2.5f |
191 | #define FRESNEL_DIELECTRIC -3.5f |
192 | |
193 | //======================================================================= |
194 | // function : fresnelMedia |
195 | // purpose : Computes the Fresnel reflection formula for general medium |
196 | // in case of circularly polarized light. |
197 | //======================================================================= |
6e728f3b |
198 | vec3 fresnelMedia (in float theCosI, in vec3 theFresnel) |
189f85a3 |
199 | { |
05aa616d |
200 | vec3 aFresnel; |
201 | |
6e728f3b |
202 | if (theFresnel.x > FRESNEL_SCHLICK) |
189f85a3 |
203 | { |
05aa616d |
204 | aFresnel = fresnelSchlick (abs (theCosI), theFresnel); |
189f85a3 |
205 | } |
05aa616d |
206 | else if (theFresnel.x > FRESNEL_CONSTANT) |
189f85a3 |
207 | { |
05aa616d |
208 | aFresnel = vec3 (theFresnel.z); |
189f85a3 |
209 | } |
05aa616d |
210 | else if (theFresnel.x > FRESNEL_CONDUCTOR) |
211 | { |
212 | aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z)); |
213 | } |
214 | else |
189f85a3 |
215 | { |
05aa616d |
216 | aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y)); |
189f85a3 |
217 | } |
218 | |
05aa616d |
219 | return aFresnel; |
189f85a3 |
220 | } |
221 | |
222 | //======================================================================= |
223 | // function : transmitted |
224 | // purpose : Computes transmitted direction in tangent space |
225 | // (in case of TIR returned result is undefined!) |
226 | //======================================================================= |
227 | void transmitted (in float theIndex, in vec3 theIncident, out vec3 theTransmit) |
228 | { |
229 | // Compute relative index of refraction |
230 | float anEta = (theIncident.z > 0.f) ? 1.f / theIndex : theIndex; |
231 | |
6e728f3b |
232 | // Handle total internal reflection (TIR) |
189f85a3 |
233 | float aSinT2 = anEta * anEta * (1.f - theIncident.z * theIncident.z); |
234 | |
6e728f3b |
235 | // Compute direction of transmitted ray |
236 | float aCosT = sqrt (1.f - min (aSinT2, 1.f)) * sign (-theIncident.z); |
189f85a3 |
237 | |
238 | theTransmit = normalize (vec3 (-anEta * theIncident.x, |
239 | -anEta * theIncident.y, |
240 | aCosT)); |
241 | } |
242 | |
243 | ////////////////////////////////////////////////////////////////////////////////////////////// |
244 | // Handlers and samplers for materials |
245 | ////////////////////////////////////////////////////////////////////////////////////////////// |
246 | |
247 | //======================================================================= |
b4327ba8 |
248 | // function : EvalLambertianReflection |
249 | // purpose : Evaluates Lambertian BRDF, with cos(N, PSI) |
189f85a3 |
250 | //======================================================================= |
b4327ba8 |
251 | float EvalLambertianReflection (in vec3 theWi, in vec3 theWo) |
189f85a3 |
252 | { |
b4327ba8 |
253 | return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI); |
189f85a3 |
254 | } |
255 | |
05aa616d |
256 | #define FLT_EPSILON 1.0e-5f |
257 | |
189f85a3 |
258 | //======================================================================= |
6e728f3b |
259 | // function : SmithG1 |
260 | // purpose : |
189f85a3 |
261 | //======================================================================= |
6e728f3b |
262 | float SmithG1 (in vec3 theDirection, in vec3 theM, in float theRoughness) |
189f85a3 |
263 | { |
05aa616d |
264 | float aResult = 0.f; |
189f85a3 |
265 | |
05aa616d |
266 | if (dot (theDirection, theM) * theDirection.z > 0.f) |
6e728f3b |
267 | { |
05aa616d |
268 | float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z; |
189f85a3 |
269 | |
05aa616d |
270 | if (aTanThetaM == 0.f) |
271 | { |
272 | aResult = 1.f; |
273 | } |
274 | else |
275 | { |
276 | float aVal = 1.f / (theRoughness * aTanThetaM); |
189f85a3 |
277 | |
05aa616d |
278 | // Use rational approximation to shadowing-masking function (from Mitsuba) |
279 | aResult = (3.535f + 2.181f * aVal) / (1.f / aVal + 2.276f + 2.577f * aVal); |
280 | } |
6e728f3b |
281 | } |
189f85a3 |
282 | |
05aa616d |
283 | return min (aResult, 1.f); |
6e728f3b |
284 | } |
189f85a3 |
285 | |
6e728f3b |
286 | //======================================================================= |
b4327ba8 |
287 | // function : EvalBlinnReflection |
288 | // purpose : Evaluates Blinn glossy BRDF, with cos(N, PSI) |
6e728f3b |
289 | //======================================================================= |
b4327ba8 |
290 | vec3 EvalBlinnReflection (in vec3 theWi, in vec3 theWo, in vec3 theFresnel, in float theRoughness) |
6e728f3b |
291 | { |
292 | // calculate the reflection half-vec |
b4327ba8 |
293 | vec3 aH = normalize (theWi + theWo); |
189f85a3 |
294 | |
6e728f3b |
295 | // roughness value -> Blinn exponent |
296 | float aPower = max (2.f / (theRoughness * theRoughness) - 2.f, 0.f); |
189f85a3 |
297 | |
6e728f3b |
298 | // calculate microfacet distribution |
299 | float aD = (aPower + 2.f) * (1.f / M_2_PI) * pow (aH.z, aPower); |
189f85a3 |
300 | |
6e728f3b |
301 | // calculate shadow-masking function |
b4327ba8 |
302 | float aG = SmithG1 (theWo, aH, theRoughness) * |
303 | SmithG1 (theWi, aH, theRoughness); |
189f85a3 |
304 | |
6e728f3b |
305 | // return total amount of reflection |
b4327ba8 |
306 | return (theWi.z <= 0.f || theWo.z <= 0.f) ? ZERO : |
307 | aD * aG / (4.f * theWo.z) * fresnelMedia (dot (theWo, aH), theFresnel); |
189f85a3 |
308 | } |
309 | |
310 | //======================================================================= |
05aa616d |
311 | // function : EvalBsdfLayered |
b4327ba8 |
312 | // purpose : Evaluates BSDF for specified material, with cos(N, PSI) |
189f85a3 |
313 | //======================================================================= |
05aa616d |
314 | vec3 EvalBsdfLayered (in SBSDF theBSDF, in vec3 theWi, in vec3 theWo) |
189f85a3 |
315 | { |
b4327ba8 |
316 | #ifdef TWO_SIDED_BXDF |
317 | theWi.z *= sign (theWi.z); |
318 | theWo.z *= sign (theWo.z); |
319 | #endif |
320 | |
05aa616d |
321 | vec3 aBxDF = theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo); |
322 | |
323 | if (theBSDF.Ks.w > FLT_EPSILON) |
324 | { |
325 | aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w); |
326 | } |
327 | |
328 | aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat); |
329 | |
330 | if (theBSDF.Kc.w > FLT_EPSILON) |
331 | { |
332 | aBxDF += theBSDF.Kc.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelCoat, theBSDF.Kc.w); |
333 | } |
334 | |
335 | return aBxDF; |
189f85a3 |
336 | } |
337 | |
189f85a3 |
338 | //======================================================================= |
6e728f3b |
339 | // function : SampleLambertianReflection |
189f85a3 |
340 | // purpose : Samples Lambertian BRDF, W = BRDF * cos(N, PSI) / PDF(PSI) |
341 | //======================================================================= |
b4327ba8 |
342 | vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float thePDF) |
189f85a3 |
343 | { |
344 | float aKsi1 = RandFloat(); |
345 | float aKsi2 = RandFloat(); |
346 | |
b4327ba8 |
347 | theWi = vec3 (cos (M_2_PI * aKsi1), |
348 | sin (M_2_PI * aKsi1), |
349 | sqrt (1.f - aKsi2)); |
350 | |
351 | theWi.xy *= sqrt (aKsi2); |
189f85a3 |
352 | |
b4327ba8 |
353 | #ifdef TWO_SIDED_BXDF |
354 | theWi.z *= sign (theWo.z); |
355 | #endif |
189f85a3 |
356 | |
b4327ba8 |
357 | thePDF *= theWi.z * (1.f / M_PI); |
8c820969 |
358 | |
b4327ba8 |
359 | #ifdef TWO_SIDED_BXDF |
360 | return UNIT; |
361 | #else |
06e06389 |
362 | return UNIT * step (0.f, theWo.z); |
b4327ba8 |
363 | #endif |
6e728f3b |
364 | } |
8c820969 |
365 | |
189f85a3 |
366 | //======================================================================= |
05aa616d |
367 | // function : SampleGlossyBlinnReflection |
6e728f3b |
368 | // purpose : Samples Blinn BRDF, W = BRDF * cos(N, PSI) / PDF(PSI) |
369 | // The BRDF is a product of three main terms, D, G, and F, |
370 | // which is then divided by two cosine terms. Here we perform |
371 | // importance sample the D part of the Blinn model; trying to |
372 | // develop a sampling procedure that accounted for all of the |
373 | // terms would be complex, and it is the D term that accounts |
374 | // for most of the variation. |
189f85a3 |
375 | //======================================================================= |
05aa616d |
376 | vec3 SampleGlossyBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF) |
189f85a3 |
377 | { |
6e728f3b |
378 | float aKsi1 = RandFloat(); |
379 | float aKsi2 = RandFloat(); |
189f85a3 |
380 | |
6e728f3b |
381 | // roughness value --> Blinn exponent |
382 | float aPower = max (2.f / (theRoughness * theRoughness) - 2.f, 0.f); |
8c820969 |
383 | |
6e728f3b |
384 | // normal from microface distribution |
385 | float aCosThetaM = pow (aKsi1, 1.f / (aPower + 2.f)); |
189f85a3 |
386 | |
6e728f3b |
387 | vec3 aM = vec3 (cos (M_2_PI * aKsi2), |
388 | sin (M_2_PI * aKsi2), |
389 | aCosThetaM); |
189f85a3 |
390 | |
6e728f3b |
391 | aM.xy *= sqrt (1.f - aCosThetaM * aCosThetaM); |
189f85a3 |
392 | |
6e728f3b |
393 | // calculate PDF of sampled direction |
394 | thePDF *= (aPower + 2.f) * (1.f / M_2_PI) * pow (aCosThetaM, aPower + 1.f); |
395 | |
b4327ba8 |
396 | #ifdef TWO_SIDED_BXDF |
397 | bool toFlip = theWo.z < 0.f; |
398 | |
399 | if (toFlip) |
400 | theWo.z = -theWo.z; |
401 | #endif |
402 | |
403 | float aCosDelta = dot (theWo, aM); |
6e728f3b |
404 | |
405 | // pick input based on half direction |
b4327ba8 |
406 | theWi = -theWo + 2.f * aCosDelta * aM; |
6e728f3b |
407 | |
b4327ba8 |
408 | if (theWi.z <= 0.f || theWo.z <= 0.f) |
6e728f3b |
409 | { |
410 | return ZERO; |
8c820969 |
411 | } |
189f85a3 |
412 | |
6e728f3b |
413 | // Jacobian of half-direction mapping |
05aa616d |
414 | thePDF /= 4.f * aCosDelta; |
6e728f3b |
415 | |
416 | // compute shadow-masking coefficient |
b4327ba8 |
417 | float aG = SmithG1 (theWo, aM, theRoughness) * |
418 | SmithG1 (theWi, aM, theRoughness); |
419 | |
420 | #ifdef TWO_SIDED_BXDF |
421 | if (toFlip) |
422 | theWi.z = -theWi.z; |
423 | #endif |
6e728f3b |
424 | |
b4327ba8 |
425 | return (aG * aCosDelta) / (theWo.z * aM.z) * fresnelMedia (aCosDelta, theFresnel); |
189f85a3 |
426 | } |
427 | |
428 | //======================================================================= |
05aa616d |
429 | // function : BsdfPdfLayered |
430 | // purpose : Calculates BSDF of sampling input knowing output |
189f85a3 |
431 | //======================================================================= |
05aa616d |
432 | float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight) |
189f85a3 |
433 | { |
05aa616d |
434 | float aPDF = 0.f; // PDF of sampling input direction |
189f85a3 |
435 | |
05aa616d |
436 | // We choose whether the light is reflected or transmitted |
437 | // by the coating layer according to the Fresnel equations |
438 | vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat); |
189f85a3 |
439 | |
05aa616d |
440 | // Coat BRDF is scaled by its Fresnel reflectance term. For |
441 | // reasons of simplicity we scale base BxDFs only by coat's |
442 | // Fresnel transmittance term |
443 | vec3 aCoatT = UNIT - aCoatF; |
189f85a3 |
444 | |
05aa616d |
445 | float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight); |
446 | float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight); |
447 | float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight); |
448 | float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight); |
189f85a3 |
449 | |
05aa616d |
450 | if (theWi.z * theWo.z > 0.f) |
189f85a3 |
451 | { |
05aa616d |
452 | vec3 aH = normalize (theWi + theWo); |
6e728f3b |
453 | |
05aa616d |
454 | aPDF = aPd * abs (theWi.z / M_PI); |
189f85a3 |
455 | |
05aa616d |
456 | if (theBSDF.Kc.w > FLT_EPSILON) |
457 | { |
458 | float aPower = max (2.f / (theBSDF.Kc.w * theBSDF.Kc.w) - 2.f, 0.f); // roughness --> exponent |
189f85a3 |
459 | |
05aa616d |
460 | aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH); |
461 | } |
189f85a3 |
462 | |
05aa616d |
463 | if (theBSDF.Ks.w > FLT_EPSILON) |
464 | { |
465 | float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); // roughness --> exponent |
189f85a3 |
466 | |
05aa616d |
467 | aPDF += aPs * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH); |
468 | } |
6e728f3b |
469 | } |
189f85a3 |
470 | |
05aa616d |
471 | return aPDF / (aPc + aPd + aPs + aPt); |
189f85a3 |
472 | } |
473 | |
6e728f3b |
474 | //! Tool macro to handle sampling of particular BxDF |
05aa616d |
475 | #define PICK_BXDF_LAYER(p, k) aPDF = p / aTotalR; theWeight *= k / aPDF; |
6e728f3b |
476 | |
189f85a3 |
477 | //======================================================================= |
05aa616d |
478 | // function : SampleBsdfLayered |
189f85a3 |
479 | // purpose : Samples specified composite material (BSDF) |
480 | //======================================================================= |
05aa616d |
481 | float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside) |
189f85a3 |
482 | { |
05aa616d |
483 | // NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive |
484 | // layer, covered by one glossy/specular coat. In the current model, the layers themselves |
485 | // have no thickness; they can simply reflect light or transmits it to the layer under it. |
486 | // We use actual BRDF model only for direct reflection by the coat layer. For transmission |
487 | // through this layer, we approximate it as a flat specular surface. |
488 | |
489 | float aPDF = 0.f; // PDF of sampled direction |
490 | |
491 | // We choose whether the light is reflected or transmitted |
492 | // by the coating layer according to the Fresnel equations |
493 | vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat); |
494 | |
495 | // Coat BRDF is scaled by its Fresnel term. According to |
496 | // Wilkie-Weidlich layered BSDF model, transmission term |
497 | // for light passing through the coat at direction I and |
498 | // leaving it in O is T = ( 1 - F (O) ) x ( 1 - F (I) ). |
499 | // For reasons of simplicity, we discard the second term |
500 | // and scale base BxDFs only by the first term. |
501 | vec3 aCoatT = UNIT - aCoatF; |
502 | |
503 | float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight); |
504 | float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight); |
505 | float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight); |
506 | float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight); |
507 | |
508 | // Calculate total reflection probability |
509 | float aTotalR = (aPc + aPd) + (aPs + aPt); |
510 | |
511 | // Generate random variable to select BxDF |
512 | float aKsi = aTotalR * RandFloat(); |
513 | |
514 | if (aKsi < aPc) // REFLECTION FROM COAT |
515 | { |
516 | PICK_BXDF_LAYER (aPc, theBSDF.Kc.rgb) |
189f85a3 |
517 | |
05aa616d |
518 | if (theBSDF.Kc.w < FLT_EPSILON) |
519 | { |
520 | theWeight *= aCoatF; |
8c820969 |
521 | |
05aa616d |
522 | theWi = vec3 (-theWo.x, |
523 | -theWo.y, |
524 | theWo.z); |
525 | } |
526 | else |
527 | { |
528 | theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelCoat, theBSDF.Kc.w, aPDF); |
529 | } |
189f85a3 |
530 | |
05aa616d |
531 | aPDF = mix (aPDF, MAXFLOAT, theBSDF.Kc.w < FLT_EPSILON); |
189f85a3 |
532 | } |
05aa616d |
533 | else if (aKsi < aTotalR) // REFLECTION FROM BASE |
189f85a3 |
534 | { |
05aa616d |
535 | theWeight *= aCoatT; |
6e728f3b |
536 | |
05aa616d |
537 | if (aKsi < aPc + aPd) // diffuse BRDF |
538 | { |
539 | PICK_BXDF_LAYER (aPd, theBSDF.Kd.rgb) |
540 | |
541 | theWeight *= SampleLambertianReflection (theWo, theWi, aPDF); |
542 | } |
543 | else if (aKsi < (aPc + aPd) + aPs) // specular/glossy BRDF |
544 | { |
545 | PICK_BXDF_LAYER (aPs, theBSDF.Ks.rgb) |
6e728f3b |
546 | |
05aa616d |
547 | if (theBSDF.Ks.w < FLT_EPSILON) |
548 | { |
549 | theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase); |
189f85a3 |
550 | |
05aa616d |
551 | theWi = vec3 (-theWo.x, |
552 | -theWo.y, |
553 | theWo.z); |
554 | } |
555 | else |
556 | { |
557 | theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF); |
558 | } |
6e728f3b |
559 | |
05aa616d |
560 | aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON); |
561 | } |
562 | else // specular transmission |
563 | { |
564 | PICK_BXDF_LAYER (aPt, theBSDF.Kt.rgb) |
6e728f3b |
565 | |
05aa616d |
566 | // refracted direction should exist if we are here |
567 | transmitted (theBSDF.FresnelCoat.y, theWo, theWi); |
568 | |
569 | theInside = !theInside; aPDF = MAXFLOAT; |
570 | } |
189f85a3 |
571 | } |
8c820969 |
572 | |
573 | // path termination for extra small weights |
05aa616d |
574 | theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aTotalR)); |
6e728f3b |
575 | |
576 | return aPDF; |
189f85a3 |
577 | } |
578 | |
579 | ////////////////////////////////////////////////////////////////////////////////////////////// |
580 | // Handlers and samplers for light sources |
581 | ////////////////////////////////////////////////////////////////////////////////////////////// |
582 | |
6e728f3b |
583 | // ======================================================================= |
584 | // function : Latlong |
585 | // purpose : Converts world direction to environment texture coordinates |
586 | // ======================================================================= |
587 | vec2 Latlong (in vec3 thePoint) |
189f85a3 |
588 | { |
6e728f3b |
589 | float aPsi = acos (-thePoint.z); |
189f85a3 |
590 | |
6e728f3b |
591 | float aPhi = atan (thePoint.y, thePoint.x) + M_PI; |
189f85a3 |
592 | |
6e728f3b |
593 | return vec2 (aPhi * 0.1591549f, |
594 | aPsi * 0.3183098f); |
189f85a3 |
595 | } |
596 | |
597 | //======================================================================= |
6e728f3b |
598 | // function : SampleLight |
3a9b5dc8 |
599 | // purpose : General sampling function for directional and point lights |
189f85a3 |
600 | //======================================================================= |
6e728f3b |
601 | vec3 SampleLight (in vec3 theToLight, inout float theDistance, in bool isInfinite, in float theSmoothness, inout float thePDF) |
189f85a3 |
602 | { |
6e728f3b |
603 | SLocalSpace aSpace = buildLocalSpace (theToLight * (1.f / theDistance)); |
189f85a3 |
604 | |
8c820969 |
605 | // for point lights smoothness defines radius |
3a9b5dc8 |
606 | float aCosMax = isInfinite ? theSmoothness : |
607 | inversesqrt (1.f + theSmoothness * theSmoothness / (theDistance * theDistance)); |
189f85a3 |
608 | |
609 | float aKsi1 = RandFloat(); |
610 | float aKsi2 = RandFloat(); |
611 | |
612 | float aTmp = 1.f - aKsi2 * (1.f - aCosMax); |
613 | |
6e728f3b |
614 | vec3 anInput = vec3 (cos (M_2_PI * aKsi1), |
615 | sin (M_2_PI * aKsi1), |
189f85a3 |
616 | aTmp); |
617 | |
8c820969 |
618 | anInput.xy *= sqrt (1.f - aTmp * aTmp); |
189f85a3 |
619 | |
6e728f3b |
620 | thePDF = (aCosMax < 1.f) ? (thePDF / M_2_PI) / (1.f - aCosMax) : MAXFLOAT; |
189f85a3 |
621 | |
622 | return normalize (fromLocalSpace (anInput, aSpace)); |
623 | } |
624 | |
6e728f3b |
625 | //======================================================================= |
626 | // function : HandlePointLight |
627 | // purpose : |
628 | //======================================================================= |
629 | float HandlePointLight (in vec3 theInput, in vec3 theToLight, in float theRadius, in float theDistance, inout float thePDF) |
189f85a3 |
630 | { |
6e728f3b |
631 | float aCosMax = inversesqrt (1.f + theRadius * theRadius / (theDistance * theDistance)); |
189f85a3 |
632 | |
6e728f3b |
633 | float aVisibility = step (aCosMax, dot (theInput, theToLight)); |
189f85a3 |
634 | |
6e728f3b |
635 | thePDF *= step (-1.f, -aCosMax) * aVisibility * (1.f / M_2_PI) / (1.f - aCosMax); |
636 | |
637 | return aVisibility; |
638 | } |
639 | |
640 | //======================================================================= |
641 | // function : HandleDistantLight |
642 | // purpose : |
643 | //======================================================================= |
644 | float HandleDistantLight (in vec3 theInput, in vec3 theToLight, in float theCosMax, inout float thePDF) |
645 | { |
646 | float aVisibility = step (theCosMax, dot (theInput, theToLight)); |
647 | |
648 | thePDF *= step (-1.f, -theCosMax) * aVisibility * (1.f / M_2_PI) / (1.f - theCosMax); |
649 | |
650 | return aVisibility; |
189f85a3 |
651 | } |
652 | |
653 | // ======================================================================= |
6e728f3b |
654 | // function: IntersectLight |
8c820969 |
655 | // purpose : Checks intersections with light sources |
189f85a3 |
656 | // ======================================================================= |
6e728f3b |
657 | vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, out float thePDF) |
189f85a3 |
658 | { |
6e728f3b |
659 | vec3 aTotalRadiance = ZERO; |
189f85a3 |
660 | |
6e728f3b |
661 | thePDF = 0.f; // PDF of sampling light sources |
189f85a3 |
662 | |
6e728f3b |
663 | for (int aLightIdx = 0; aLightIdx < uLightCount; ++aLightIdx) |
189f85a3 |
664 | { |
665 | vec4 aLight = texelFetch ( |
666 | uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx)); |
667 | vec4 aParam = texelFetch ( |
668 | uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx)); |
669 | |
6e728f3b |
670 | // W component: 0 for infinite light and 1 for point light |
671 | aLight.xyz -= mix (ZERO, theRay.Origin, aLight.w); |
672 | |
673 | float aPDF = 1.f / uLightCount; |
674 | |
189f85a3 |
675 | if (aLight.w != 0.f) // point light source |
676 | { |
6e728f3b |
677 | float aCenterDst = length (aLight.xyz); |
678 | |
679 | if (aCenterDst < theHitDistance) |
680 | { |
681 | float aVisibility = HandlePointLight ( |
682 | theRay.Direct, normalize (aLight.xyz), aParam.w /* radius */, aCenterDst, aPDF); |
683 | |
684 | if (aVisibility > 0.f) |
685 | { |
686 | theHitDistance = aCenterDst; |
687 | aTotalRadiance = aParam.rgb; |
688 | |
689 | thePDF = aPDF; |
690 | } |
691 | } |
189f85a3 |
692 | } |
6e728f3b |
693 | else if (theHitDistance == MAXFLOAT) // directional light source |
189f85a3 |
694 | { |
6e728f3b |
695 | aTotalRadiance += aParam.rgb * HandleDistantLight ( |
696 | theRay.Direct, aLight.xyz, aParam.w /* angle cosine */, aPDF); |
697 | |
698 | thePDF += aPDF; |
189f85a3 |
699 | } |
700 | } |
701 | |
6e728f3b |
702 | if (thePDF == 0.f && theHitDistance == MAXFLOAT) // light source not found |
703 | { |
704 | if (theDepth + uSphereMapForBack == 0) // view ray and map is hidden |
705 | { |
706 | aTotalRadiance = pow (BackgroundColor().rgb, vec3 (2.f)); |
707 | } |
708 | else |
709 | { |
710 | aTotalRadiance = pow (FetchEnvironment (Latlong (theRay.Direct)).rgb, vec3 (2.f)); |
711 | } |
712 | } |
713 | |
714 | return aTotalRadiance; |
189f85a3 |
715 | } |
716 | |
6e728f3b |
717 | #define MIN_THROUGHPUT vec3 (1.0e-3f) |
718 | #define MIN_CONTRIBUTION vec3 (1.0e-2f) |
189f85a3 |
719 | |
05aa616d |
720 | #define MATERIAL_KC(index) (19 * index + 11) |
721 | #define MATERIAL_KD(index) (19 * index + 12) |
722 | #define MATERIAL_KS(index) (19 * index + 13) |
723 | #define MATERIAL_KT(index) (19 * index + 14) |
724 | #define MATERIAL_LE(index) (19 * index + 15) |
725 | #define MATERIAL_FRESNEL_COAT(index) (19 * index + 16) |
726 | #define MATERIAL_FRESNEL_BASE(index) (19 * index + 17) |
727 | #define MATERIAL_ABSORPT_BASE(index) (19 * index + 18) |
189f85a3 |
728 | |
05aa616d |
729 | //! Enables experimental Russian roulette sampling path termination. |
4eaaf9d8 |
730 | //! In most cases, it provides faster image convergence with minimal |
731 | //! bias, so it is enabled by default. |
8c820969 |
732 | #define RUSSIAN_ROULETTE |
733 | |
4eaaf9d8 |
734 | //! Frame step to increase number of bounces. This mode is used |
735 | //! for interaction with the model, when path length is limited |
736 | //! for the first samples, and gradually increasing when camera |
737 | //! is stabilizing. |
738 | #ifdef ADAPTIVE_SAMPLING |
739 | #define FRAME_STEP 4 |
740 | #else |
741 | #define FRAME_STEP 5 |
742 | #endif |
383c6c9f |
743 | |
05aa616d |
744 | //======================================================================= |
745 | // function : IsNotZero |
746 | // purpose : Checks whether BSDF reflects direct light |
747 | //======================================================================= |
748 | bool IsNotZero (in SBSDF theBSDF, in vec3 theThroughput) |
749 | { |
750 | vec3 aGlossy = theBSDF.Kc.rgb * step (FLT_EPSILON, theBSDF.Kc.w) + |
751 | theBSDF.Ks.rgb * step (FLT_EPSILON, theBSDF.Ks.w); |
752 | |
753 | return convolve (theBSDF.Kd.rgb + aGlossy, theThroughput) > FLT_EPSILON; |
754 | } |
755 | |
189f85a3 |
756 | //======================================================================= |
757 | // function : PathTrace |
758 | // purpose : Calculates radiance along the given ray |
759 | //======================================================================= |
4eaaf9d8 |
760 | vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples) |
189f85a3 |
761 | { |
1d865689 |
762 | float aRaytraceDepth = MAXFLOAT; |
189f85a3 |
763 | |
764 | vec3 aRadiance = ZERO; |
765 | vec3 aThroughput = UNIT; |
766 | |
6e728f3b |
767 | int aTransfID = 0; // ID of object transformation |
768 | bool aInMedium = false; // is the ray inside an object |
189f85a3 |
769 | |
6e728f3b |
770 | float aExpPDF = 1.f; |
771 | float aImpPDF = 1.f; |
189f85a3 |
772 | |
773 | for (int aDepth = 0; aDepth < NB_BOUNCES; ++aDepth) |
774 | { |
775 | SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO); |
776 | |
6e728f3b |
777 | ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTransfID); |
8c820969 |
778 | |
779 | // check implicit path |
6e728f3b |
780 | vec3 aLe = IntersectLight (theRay, aDepth, aHit.Time, aExpPDF); |
189f85a3 |
781 | |
8c820969 |
782 | if (any (greaterThan (aLe, ZERO)) || aTriIndex.x == -1) |
189f85a3 |
783 | { |
6e728f3b |
784 | float aMIS = (aDepth == 0 || aImpPDF == MAXFLOAT) ? 1.f : |
785 | aImpPDF * aImpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF); |
786 | |
787 | aRadiance += aThroughput * aLe * aMIS; break; // terminate path |
189f85a3 |
788 | } |
789 | |
6e728f3b |
790 | vec3 aInvTransf0 = texelFetch (uSceneTransformTexture, aTransfID + 0).xyz; |
791 | vec3 aInvTransf1 = texelFetch (uSceneTransformTexture, aTransfID + 1).xyz; |
792 | vec3 aInvTransf2 = texelFetch (uSceneTransformTexture, aTransfID + 2).xyz; |
189f85a3 |
793 | |
8c820969 |
794 | // compute geometrical normal |
189f85a3 |
795 | aHit.Normal = normalize (vec3 (dot (aInvTransf0, aHit.Normal), |
796 | dot (aInvTransf1, aHit.Normal), |
797 | dot (aInvTransf2, aHit.Normal))); |
798 | |
8c820969 |
799 | theRay.Origin += theRay.Direct * aHit.Time; // get new intersection point |
189f85a3 |
800 | |
6e728f3b |
801 | // evaluate depth on first hit |
1d865689 |
802 | if (aDepth == 0) |
803 | { |
be86ba90 |
804 | vec4 aNDCPoint = uViewMat * vec4 (theRay.Origin, 1.f); |
3a9b5dc8 |
805 | |
be86ba90 |
806 | float aPolygonOffset = PolygonOffset (aHit.Normal, theRay.Origin); |
807 | aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w + aPolygonOffset * POLYGON_OFFSET_SCALE) * 0.5f + 0.5f; |
1d865689 |
808 | } |
809 | |
05aa616d |
810 | SBSDF aBSDF; |
811 | |
812 | // fetch BxDF weights |
813 | aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriIndex.w)); |
814 | aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w)); |
815 | aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w)); |
816 | aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w)).rgb; |
817 | |
818 | // compute smooth normal (in parallel with fetch) |
819 | vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex); |
820 | |
821 | aNormal = normalize (vec3 (dot (aInvTransf0, aNormal), |
822 | dot (aInvTransf1, aNormal), |
823 | dot (aInvTransf2, aNormal))); |
824 | |
825 | SLocalSpace aSpace = buildLocalSpace (aNormal); |
189f85a3 |
826 | |
827 | #ifdef USE_TEXTURES |
05aa616d |
828 | if (aBSDF.Kd.w >= 0.f) |
189f85a3 |
829 | { |
830 | vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f); |
831 | |
832 | vec4 aTrsfRow1 = texelFetch ( |
833 | uRaytraceMaterialTexture, MATERIAL_TRS1 (aTriIndex.w)); |
834 | vec4 aTrsfRow2 = texelFetch ( |
835 | uRaytraceMaterialTexture, MATERIAL_TRS2 (aTriIndex.w)); |
836 | |
837 | aTexCoord.st = vec2 (dot (aTrsfRow1, aTexCoord), |
838 | dot (aTrsfRow2, aTexCoord)); |
839 | |
f411f94f |
840 | vec4 aTexColor = textureLod ( |
05aa616d |
841 | sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.f); |
189f85a3 |
842 | |
05aa616d |
843 | aBSDF.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2) |
f411f94f |
844 | |
845 | if (aTexColor.w != 1.0f) |
846 | { |
847 | // mix transparency BTDF with texture alpha-channel |
05aa616d |
848 | aBSDF.Kt = (UNIT - aTexColor.www) + aTexColor.w * aBSDF.Kt; |
f411f94f |
849 | } |
189f85a3 |
850 | } |
851 | #endif |
852 | |
05aa616d |
853 | // fetch Fresnel reflectance for both layers |
854 | aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriIndex.w)).xyz; |
855 | aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriIndex.w)).xyz; |
189f85a3 |
856 | |
05aa616d |
857 | if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput)) |
189f85a3 |
858 | { |
6e728f3b |
859 | aExpPDF = 1.f / uLightCount; |
860 | |
189f85a3 |
861 | int aLightIdx = min (int (floor (RandFloat() * uLightCount)), uLightCount - 1); |
862 | |
863 | vec4 aLight = texelFetch ( |
864 | uRaytraceLightSrcTexture, LIGHT_POS (aLightIdx)); |
865 | vec4 aParam = texelFetch ( |
866 | uRaytraceLightSrcTexture, LIGHT_PWR (aLightIdx)); |
867 | |
8c820969 |
868 | // 'w' component is 0 for infinite light and 1 for point light |
869 | aLight.xyz -= mix (ZERO, theRay.Origin, aLight.w); |
189f85a3 |
870 | |
6e728f3b |
871 | float aDistance = length (aLight.xyz); |
189f85a3 |
872 | |
6e728f3b |
873 | aLight.xyz = SampleLight (aLight.xyz, aDistance, |
874 | aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aExpPDF); |
189f85a3 |
875 | |
05aa616d |
876 | aImpPDF = BsdfPdfLayered (aBSDF, |
6e728f3b |
877 | toLocalSpace (-theRay.Direct, aSpace), toLocalSpace (aLight.xyz, aSpace), aThroughput); |
878 | |
879 | // MIS weight including division by explicit PDF |
880 | float aMIS = (aExpPDF == MAXFLOAT) ? 1.f : aExpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF); |
881 | |
05aa616d |
882 | vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalBsdfLayered ( |
883 | aBSDF, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace)); |
189f85a3 |
884 | |
6e728f3b |
885 | if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important |
189f85a3 |
886 | { |
887 | SRay aShadow = SRay (theRay.Origin + aLight.xyz * uSceneEpsilon, aLight.xyz); |
888 | |
889 | aShadow.Origin += aHit.Normal * mix ( |
890 | -uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, aLight.xyz))); |
891 | |
892 | float aVisibility = SceneAnyHit (aShadow, |
8c820969 |
893 | InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance); |
189f85a3 |
894 | |
05aa616d |
895 | aRadiance += aVisibility * (aThroughput * aContrib); |
189f85a3 |
896 | } |
897 | } |
898 | |
05aa616d |
899 | // account for self-emission |
900 | aRadiance += aThroughput * texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb; |
901 | |
6e728f3b |
902 | if (aInMedium) // handle attenuation |
189f85a3 |
903 | { |
05aa616d |
904 | vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w)); |
905 | |
906 | aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb)); |
189f85a3 |
907 | } |
908 | |
6e728f3b |
909 | vec3 anInput = UNIT; // sampled input direction |
189f85a3 |
910 | |
05aa616d |
911 | aImpPDF = SampleBsdfLayered (aBSDF, |
6e728f3b |
912 | toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aInMedium); |
913 | |
05aa616d |
914 | float aSurvive = float (any (greaterThan (aThroughput, MIN_THROUGHPUT))); |
6e728f3b |
915 | |
916 | #ifdef RUSSIAN_ROULETTE |
05aa616d |
917 | aSurvive = aDepth < 3 ? aSurvive : min (dot (LUMA, aThroughput), 0.95f); |
6e728f3b |
918 | #endif |
8c820969 |
919 | |
4eaaf9d8 |
920 | // here, we additionally increase path length for non-diffuse bounces |
921 | if (RandFloat() > aSurvive || all (lessThan (aThroughput, MIN_THROUGHPUT)) || aDepth >= theNbSamples / FRAME_STEP + step (1.f / M_PI, aImpPDF)) |
8c820969 |
922 | { |
923 | aDepth = INVALID_BOUNCES; // terminate path |
189f85a3 |
924 | } |
925 | |
6e728f3b |
926 | #ifdef RUSSIAN_ROULETTE |
8c820969 |
927 | aThroughput /= aSurvive; |
928 | #endif |
929 | |
189f85a3 |
930 | anInput = normalize (fromLocalSpace (anInput, aSpace)); |
931 | |
932 | theRay = SRay (theRay.Origin + anInput * uSceneEpsilon + |
933 | aHit.Normal * mix (-uSceneEpsilon, uSceneEpsilon, step (0.f, dot (aHit.Normal, anInput))), anInput); |
934 | |
935 | theInverse = InverseDirection (anInput); |
189f85a3 |
936 | } |
937 | |
1d865689 |
938 | gl_FragDepth = aRaytraceDepth; |
939 | |
3a9b5dc8 |
940 | return vec4 (aRadiance, aRaytraceDepth); |
189f85a3 |
941 | } |
942 | |
943 | #endif |