0030476: Visualization, Path Tracing - Adaptive Screen Sampling leads to unstable...
[occt.git] / src / Shaders / PathtraceBase.fs
CommitLineData
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).
19struct 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 32struct 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 60SLocalSpace 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//=======================================================================
86vec3 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//=======================================================================
97vec3 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//=======================================================================
108float 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//=======================================================================
118vec3 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//=======================================================================
128float 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//=======================================================================
149float 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//=======================================================================
171float 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 198vec3 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//=======================================================================
227void 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 251float 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 262float 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 290vec3 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 314vec3 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 342vec3 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 376vec3 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 432float 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 481float 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// =======================================================================
587vec2 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 601vec3 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//=======================================================================
629float 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//=======================================================================
644float 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 657vec3 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//=======================================================================
748bool 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 760vec4 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