three-gpu-pathtracer 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1004 -981
  3. package/build/index.module.js +7413 -6902
  4. package/build/index.module.js.map +1 -1
  5. package/build/index.umd.cjs +7446 -6933
  6. package/build/index.umd.cjs.map +1 -1
  7. package/package.json +73 -73
  8. package/src/core/DynamicPathTracingSceneGenerator.js +119 -119
  9. package/src/core/MaterialReducer.js +256 -256
  10. package/src/core/PathTracingRenderer.js +346 -346
  11. package/src/core/PathTracingSceneGenerator.js +69 -69
  12. package/src/core/QuiltPathTracingRenderer.js +223 -223
  13. package/src/detectors/CompatibilityDetector.js +38 -0
  14. package/src/detectors/MaterialCompileDetector.js +50 -0
  15. package/src/detectors/PrecisionDetector.js +85 -0
  16. package/src/detectors/PrecisionMaterial.js +160 -0
  17. package/src/index.js +40 -36
  18. package/src/materials/MaterialBase.js +56 -56
  19. package/src/materials/debug/GraphMaterial.js +243 -243
  20. package/src/materials/fullscreen/AlphaDisplayMaterial.js +50 -48
  21. package/src/materials/fullscreen/BlendMaterial.js +67 -67
  22. package/src/materials/fullscreen/DenoiseMaterial.js +142 -142
  23. package/src/materials/fullscreen/GradientMapMaterial.js +82 -0
  24. package/src/materials/pathtracing/LambertPathTracingMaterial.js +296 -296
  25. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +118 -196
  26. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +177 -179
  27. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +84 -81
  28. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +93 -0
  29. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +323 -317
  30. package/src/materials/pathtracing/glsl/renderStructs.glsl.js +50 -0
  31. package/src/materials/pathtracing/glsl/traceScene.glsl.js +52 -54
  32. package/src/materials/surface/AmbientOcclusionMaterial.js +207 -207
  33. package/src/materials/surface/FogVolumeMaterial.js +23 -23
  34. package/src/objects/EquirectCamera.js +13 -13
  35. package/src/objects/PhysicalCamera.js +42 -28
  36. package/src/objects/PhysicalSpotLight.js +25 -14
  37. package/src/objects/ShapedAreaLight.js +22 -12
  38. package/src/shader/bsdf/bsdfSampling.glsl.js +499 -490
  39. package/src/shader/bsdf/fog.glsl.js +22 -23
  40. package/src/shader/bsdf/ggx.glsl.js +102 -102
  41. package/src/shader/bsdf/iridescence.glsl.js +135 -135
  42. package/src/shader/bsdf/sheen.glsl.js +98 -98
  43. package/src/shader/common/arraySamplerTexelFetch.glsl.js +25 -25
  44. package/src/shader/common/bvhAnyHit.glsl.js +76 -76
  45. package/src/shader/common/fresnel.glsl.js +98 -98
  46. package/src/shader/common/intersectShapes.glsl.js +62 -62
  47. package/src/shader/common/math.glsl.js +81 -81
  48. package/src/shader/common/utils.glsl.js +116 -116
  49. package/src/shader/rand/pcg.glsl.js +57 -57
  50. package/src/shader/rand/sobol.glsl.js +256 -256
  51. package/src/shader/sampling/equirectSampling.glsl.js +62 -62
  52. package/src/shader/sampling/lightSampling.glsl.js +223 -223
  53. package/src/shader/sampling/shapeSampling.glsl.js +86 -86
  54. package/src/shader/structs/cameraStruct.glsl.js +13 -13
  55. package/src/shader/structs/equirectStruct.glsl.js +13 -14
  56. package/src/shader/structs/fogMaterialBvh.glsl.js +62 -62
  57. package/src/shader/structs/lightsStruct.glsl.js +78 -78
  58. package/src/shader/structs/materialStruct.glsl.js +207 -207
  59. package/src/textures/GradientEquirectTexture.js +35 -35
  60. package/src/textures/ProceduralEquirectTexture.js +75 -75
  61. package/src/uniforms/AttributesTextureArray.js +35 -35
  62. package/src/uniforms/EquirectHdrInfoUniform.js +269 -277
  63. package/src/uniforms/FloatAttributeTextureArray.js +169 -169
  64. package/src/uniforms/IESProfilesTexture.js +100 -100
  65. package/src/uniforms/LightsInfoUniformStruct.js +212 -212
  66. package/src/uniforms/MaterialsTexture.js +503 -503
  67. package/src/uniforms/PhysicalCameraUniform.js +36 -36
  68. package/src/uniforms/RenderTarget2DArray.js +97 -97
  69. package/src/uniforms/utils.js +30 -30
  70. package/src/utils/BlurredEnvMapGenerator.js +116 -116
  71. package/src/utils/GeometryPreparationUtils.js +214 -214
  72. package/src/utils/IESLoader.js +325 -325
  73. package/src/utils/SobolNumberMapGenerator.js +80 -80
  74. package/src/utils/UVUnwrapper.js +101 -101
  75. package/src/utils/macroify.js +9 -9
  76. package/src/workers/PathTracingSceneWorker.js +42 -42
@@ -1,23 +1,22 @@
1
- export const fogGLSL = /* glsl */`
2
-
3
- // returns the hit distance given the material density
4
- float intersectFogVolume( Material material, float u ) {
5
-
6
- // https://raytracing.github.io/books/RayTracingTheNextWeek.html#volumes/constantdensitymediums
7
- return material.opacity == 0.0 ? INFINITY : ( - 1.0 / material.opacity ) * log( u );
8
-
9
- }
10
-
11
- ScatterRecord sampleFogVolume( SurfaceRecord surf, vec2 uv ) {
12
-
13
- ScatterRecord sampleRec;
14
- sampleRec.specularPdf = 0.0;
15
- sampleRec.pdf = 1.0 / ( 2.0 * PI );
16
- sampleRec.direction = sampleSphere( uv );
17
- sampleRec.clearcoatDirection = sampleRec.direction;
18
- sampleRec.color = surf.color;
19
- return sampleRec;
20
-
21
- }
22
-
23
- `;
1
+ export const fogGLSL = /* glsl */`
2
+
3
+ // returns the hit distance given the material density
4
+ float intersectFogVolume( Material material, float u ) {
5
+
6
+ // https://raytracing.github.io/books/RayTracingTheNextWeek.html#volumes/constantdensitymediums
7
+ return material.opacity == 0.0 ? INFINITY : ( - 1.0 / material.opacity ) * log( u );
8
+
9
+ }
10
+
11
+ ScatterRecord sampleFogVolume( SurfaceRecord surf, vec2 uv ) {
12
+
13
+ ScatterRecord sampleRec;
14
+ sampleRec.specularPdf = 0.0;
15
+ sampleRec.pdf = 1.0 / ( 2.0 * PI );
16
+ sampleRec.direction = sampleSphere( uv );
17
+ sampleRec.color = surf.color;
18
+ return sampleRec;
19
+
20
+ }
21
+
22
+ `;
@@ -1,102 +1,102 @@
1
- export const ggxGLSL = /* glsl */`
2
-
3
- // The GGX functions provide sampling and distribution information for normals as output so
4
- // in order to get probability of scatter direction the half vector must be computed and provided.
5
- // [0] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
6
- // [1] https://hal.archives-ouvertes.fr/hal-01509746/document
7
- // [2] http://jcgt.org/published/0007/04/01/
8
- // [4] http://jcgt.org/published/0003/02/03/
9
-
10
- // trowbridge-reitz === GGX === GTR
11
-
12
- vec3 ggxDirection( vec3 incidentDir, vec2 roughness, vec2 uv ) {
13
-
14
- // TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
15
- // function below, as well
16
-
17
- // Implementation from reference [1]
18
- // stretch view
19
- vec3 V = normalize( vec3( roughness * incidentDir.xy, incidentDir.z ) );
20
-
21
- // orthonormal basis
22
- vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
23
- vec3 T2 = cross( T1, V );
24
-
25
- // sample point with polar coordinates (r, phi)
26
- float a = 1.0 / ( 1.0 + V.z );
27
- float r = sqrt( uv.x );
28
- float phi = ( uv.y < a ) ? uv.y / a * PI : PI + ( uv.y - a ) / ( 1.0 - a ) * PI;
29
- float P1 = r * cos( phi );
30
- float P2 = r * sin( phi ) * ( ( uv.y < a ) ? 1.0 : V.z );
31
-
32
- // compute normal
33
- vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
34
-
35
- // unstretch
36
- N = normalize( vec3( roughness * N.xy, max( 0.0, N.z ) ) );
37
-
38
- return N;
39
-
40
- }
41
-
42
- // Below are PDF and related functions for use in a Monte Carlo path tracer
43
- // as specified in Appendix B of the following paper
44
- // See equation (34) from reference [0]
45
- float ggxLamda( float theta, float roughness ) {
46
-
47
- float tanTheta = tan( theta );
48
- float tanTheta2 = tanTheta * tanTheta;
49
- float alpha2 = roughness * roughness;
50
-
51
- float numerator = - 1.0 + sqrt( 1.0 + alpha2 * tanTheta2 );
52
- return numerator / 2.0;
53
-
54
- }
55
-
56
- // See equation (34) from reference [0]
57
- float ggxShadowMaskG1( float theta, float roughness ) {
58
-
59
- return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
60
-
61
- }
62
-
63
- // See equation (125) from reference [4]
64
- float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
65
-
66
- float incidentTheta = acos( wi.z );
67
- float scatterTheta = acos( wo.z );
68
- return 1.0 / ( 1.0 + ggxLamda( incidentTheta, roughness ) + ggxLamda( scatterTheta, roughness ) );
69
-
70
- }
71
-
72
- // See equation (33) from reference [0]
73
- float ggxDistribution( vec3 halfVector, float roughness ) {
74
-
75
- float a2 = roughness * roughness;
76
- a2 = max( EPSILON, a2 );
77
- float cosTheta = halfVector.z;
78
- float cosTheta4 = pow( cosTheta, 4.0 );
79
-
80
- if ( cosTheta == 0.0 ) return 0.0;
81
-
82
- float theta = acosSafe( halfVector.z );
83
- float tanTheta = tan( theta );
84
- float tanTheta2 = pow( tanTheta, 2.0 );
85
-
86
- float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
87
- return ( a2 / denom );
88
-
89
- }
90
-
91
- // See equation (3) from reference [2]
92
- float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
93
-
94
- float incidentTheta = acos( wi.z );
95
- float D = ggxDistribution( halfVector, roughness );
96
- float G1 = ggxShadowMaskG1( incidentTheta, roughness );
97
-
98
- return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
99
-
100
- }
101
-
102
- `;
1
+ export const ggxGLSL = /* glsl */`
2
+
3
+ // The GGX functions provide sampling and distribution information for normals as output so
4
+ // in order to get probability of scatter direction the half vector must be computed and provided.
5
+ // [0] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
6
+ // [1] https://hal.archives-ouvertes.fr/hal-01509746/document
7
+ // [2] http://jcgt.org/published/0007/04/01/
8
+ // [4] http://jcgt.org/published/0003/02/03/
9
+
10
+ // trowbridge-reitz === GGX === GTR
11
+
12
+ vec3 ggxDirection( vec3 incidentDir, vec2 roughness, vec2 uv ) {
13
+
14
+ // TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
15
+ // function below, as well
16
+
17
+ // Implementation from reference [1]
18
+ // stretch view
19
+ vec3 V = normalize( vec3( roughness * incidentDir.xy, incidentDir.z ) );
20
+
21
+ // orthonormal basis
22
+ vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
23
+ vec3 T2 = cross( T1, V );
24
+
25
+ // sample point with polar coordinates (r, phi)
26
+ float a = 1.0 / ( 1.0 + V.z );
27
+ float r = sqrt( uv.x );
28
+ float phi = ( uv.y < a ) ? uv.y / a * PI : PI + ( uv.y - a ) / ( 1.0 - a ) * PI;
29
+ float P1 = r * cos( phi );
30
+ float P2 = r * sin( phi ) * ( ( uv.y < a ) ? 1.0 : V.z );
31
+
32
+ // compute normal
33
+ vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
34
+
35
+ // unstretch
36
+ N = normalize( vec3( roughness * N.xy, max( 0.0, N.z ) ) );
37
+
38
+ return N;
39
+
40
+ }
41
+
42
+ // Below are PDF and related functions for use in a Monte Carlo path tracer
43
+ // as specified in Appendix B of the following paper
44
+ // See equation (34) from reference [0]
45
+ float ggxLamda( float theta, float roughness ) {
46
+
47
+ float tanTheta = tan( theta );
48
+ float tanTheta2 = tanTheta * tanTheta;
49
+ float alpha2 = roughness * roughness;
50
+
51
+ float numerator = - 1.0 + sqrt( 1.0 + alpha2 * tanTheta2 );
52
+ return numerator / 2.0;
53
+
54
+ }
55
+
56
+ // See equation (34) from reference [0]
57
+ float ggxShadowMaskG1( float theta, float roughness ) {
58
+
59
+ return 1.0 / ( 1.0 + ggxLamda( theta, roughness ) );
60
+
61
+ }
62
+
63
+ // See equation (125) from reference [4]
64
+ float ggxShadowMaskG2( vec3 wi, vec3 wo, float roughness ) {
65
+
66
+ float incidentTheta = acos( wi.z );
67
+ float scatterTheta = acos( wo.z );
68
+ return 1.0 / ( 1.0 + ggxLamda( incidentTheta, roughness ) + ggxLamda( scatterTheta, roughness ) );
69
+
70
+ }
71
+
72
+ // See equation (33) from reference [0]
73
+ float ggxDistribution( vec3 halfVector, float roughness ) {
74
+
75
+ float a2 = roughness * roughness;
76
+ a2 = max( EPSILON, a2 );
77
+ float cosTheta = halfVector.z;
78
+ float cosTheta4 = pow( cosTheta, 4.0 );
79
+
80
+ if ( cosTheta == 0.0 ) return 0.0;
81
+
82
+ float theta = acosSafe( halfVector.z );
83
+ float tanTheta = tan( theta );
84
+ float tanTheta2 = pow( tanTheta, 2.0 );
85
+
86
+ float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
87
+ return ( a2 / denom );
88
+
89
+ }
90
+
91
+ // See equation (3) from reference [2]
92
+ float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
93
+
94
+ float incidentTheta = acos( wi.z );
95
+ float D = ggxDistribution( halfVector, roughness );
96
+ float G1 = ggxShadowMaskG1( incidentTheta, roughness );
97
+
98
+ return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
99
+
100
+ }
101
+
102
+ `;
@@ -1,135 +1,135 @@
1
- export const iridescenceGLSL = /* glsl */`
2
-
3
- // XYZ to sRGB color space
4
- const mat3 XYZ_TO_REC709 = mat3(
5
- 3.2404542, -0.9692660, 0.0556434,
6
- -1.5371385, 1.8760108, -0.2040259,
7
- -0.4985314, 0.0415560, 1.0572252
8
- );
9
-
10
- vec3 fresnel0ToIor( vec3 fresnel0 ) {
11
-
12
- vec3 sqrtF0 = sqrt( fresnel0 );
13
- return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );
14
-
15
- }
16
-
17
- // Conversion FO/IOR
18
- vec3 iorToFresnel0( vec3 transmittedIor, float incidentIor ) {
19
-
20
- return square( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );
21
-
22
- }
23
-
24
- // ior is a value between 1.0 and 3.0. 1.0 is air interface
25
- float iorToFresnel0( float transmittedIor, float incidentIor ) {
26
-
27
- return square( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ) );
28
-
29
- }
30
-
31
- // Fresnel equations for dielectric/dielectric interfaces. See https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html
32
- vec3 evalSensitivity( float OPD, vec3 shift ) {
33
-
34
- float phase = 2.0 * PI * OPD * 1.0e-9;
35
-
36
- vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );
37
- vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );
38
- vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );
39
-
40
- vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - square( phase ) * var );
41
- xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * square( phase ) );
42
- xyz /= 1.0685e-7;
43
-
44
- vec3 srgb = XYZ_TO_REC709 * xyz;
45
- return srgb;
46
-
47
- }
48
-
49
- // See Section 4. Analytic Spectral Integration, A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence, https://hal.archives-ouvertes.fr/hal-01518344/document
50
- vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
51
-
52
- vec3 I;
53
-
54
- // Force iridescenceIor -> outsideIOR when thinFilmThickness -> 0.0
55
- float iridescenceIor = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
56
-
57
- // Evaluate the cosTheta on the base layer (Snell law)
58
- float sinTheta2Sq = square( outsideIOR / iridescenceIor ) * ( 1.0 - square( cosTheta1 ) );
59
-
60
- // Handle TIR:
61
- float cosTheta2Sq = 1.0 - sinTheta2Sq;
62
- if ( cosTheta2Sq < 0.0 ) {
63
-
64
- return vec3( 1.0 );
65
-
66
- }
67
-
68
- float cosTheta2 = sqrt( cosTheta2Sq );
69
-
70
- // First interface
71
- float R0 = iorToFresnel0( iridescenceIor, outsideIOR );
72
- float R12 = schlickFresnel( cosTheta1, R0 );
73
- float R21 = R12;
74
- float T121 = 1.0 - R12;
75
- float phi12 = 0.0;
76
- if ( iridescenceIor < outsideIOR ) {
77
-
78
- phi12 = PI;
79
-
80
- }
81
-
82
- float phi21 = PI - phi12;
83
-
84
- // Second interface
85
- vec3 baseIOR = fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
86
- vec3 R1 = iorToFresnel0( baseIOR, iridescenceIor );
87
- vec3 R23 = schlickFresnel( cosTheta2, R1 );
88
- vec3 phi23 = vec3( 0.0 );
89
- if ( baseIOR[0] < iridescenceIor ) {
90
-
91
- phi23[ 0 ] = PI;
92
-
93
- }
94
-
95
- if ( baseIOR[1] < iridescenceIor ) {
96
-
97
- phi23[ 1 ] = PI;
98
-
99
- }
100
-
101
- if ( baseIOR[2] < iridescenceIor ) {
102
-
103
- phi23[ 2 ] = PI;
104
-
105
- }
106
-
107
- // Phase shift
108
- float OPD = 2.0 * iridescenceIor * thinFilmThickness * cosTheta2;
109
- vec3 phi = vec3( phi21 ) + phi23;
110
-
111
- // Compound terms
112
- vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );
113
- vec3 r123 = sqrt( R123 );
114
- vec3 Rs = square( T121 ) * R23 / ( vec3( 1.0 ) - R123 );
115
-
116
- // Reflectance term for m = 0 (DC term amplitude)
117
- vec3 C0 = R12 + Rs;
118
- I = C0;
119
-
120
- // Reflectance term for m > 0 (pairs of diracs)
121
- vec3 Cm = Rs - T121;
122
- for ( int m = 1; m <= 2; ++ m ) {
123
-
124
- Cm *= r123;
125
- vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );
126
- I += Cm * Sm;
127
-
128
- }
129
-
130
- // Since out of gamut colors might be produced, negative color values are clamped to 0.
131
- return max( I, vec3( 0.0 ) );
132
-
133
- }
134
-
135
- `;
1
+ export const iridescenceGLSL = /* glsl */`
2
+
3
+ // XYZ to sRGB color space
4
+ const mat3 XYZ_TO_REC709 = mat3(
5
+ 3.2404542, -0.9692660, 0.0556434,
6
+ -1.5371385, 1.8760108, -0.2040259,
7
+ -0.4985314, 0.0415560, 1.0572252
8
+ );
9
+
10
+ vec3 fresnel0ToIor( vec3 fresnel0 ) {
11
+
12
+ vec3 sqrtF0 = sqrt( fresnel0 );
13
+ return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );
14
+
15
+ }
16
+
17
+ // Conversion FO/IOR
18
+ vec3 iorToFresnel0( vec3 transmittedIor, float incidentIor ) {
19
+
20
+ return square( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );
21
+
22
+ }
23
+
24
+ // ior is a value between 1.0 and 3.0. 1.0 is air interface
25
+ float iorToFresnel0( float transmittedIor, float incidentIor ) {
26
+
27
+ return square( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ) );
28
+
29
+ }
30
+
31
+ // Fresnel equations for dielectric/dielectric interfaces. See https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html
32
+ vec3 evalSensitivity( float OPD, vec3 shift ) {
33
+
34
+ float phase = 2.0 * PI * OPD * 1.0e-9;
35
+
36
+ vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );
37
+ vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );
38
+ vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );
39
+
40
+ vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - square( phase ) * var );
41
+ xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * square( phase ) );
42
+ xyz /= 1.0685e-7;
43
+
44
+ vec3 srgb = XYZ_TO_REC709 * xyz;
45
+ return srgb;
46
+
47
+ }
48
+
49
+ // See Section 4. Analytic Spectral Integration, A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence, https://hal.archives-ouvertes.fr/hal-01518344/document
50
+ vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
51
+
52
+ vec3 I;
53
+
54
+ // Force iridescenceIor -> outsideIOR when thinFilmThickness -> 0.0
55
+ float iridescenceIor = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
56
+
57
+ // Evaluate the cosTheta on the base layer (Snell law)
58
+ float sinTheta2Sq = square( outsideIOR / iridescenceIor ) * ( 1.0 - square( cosTheta1 ) );
59
+
60
+ // Handle TIR:
61
+ float cosTheta2Sq = 1.0 - sinTheta2Sq;
62
+ if ( cosTheta2Sq < 0.0 ) {
63
+
64
+ return vec3( 1.0 );
65
+
66
+ }
67
+
68
+ float cosTheta2 = sqrt( cosTheta2Sq );
69
+
70
+ // First interface
71
+ float R0 = iorToFresnel0( iridescenceIor, outsideIOR );
72
+ float R12 = schlickFresnel( cosTheta1, R0 );
73
+ float R21 = R12;
74
+ float T121 = 1.0 - R12;
75
+ float phi12 = 0.0;
76
+ if ( iridescenceIor < outsideIOR ) {
77
+
78
+ phi12 = PI;
79
+
80
+ }
81
+
82
+ float phi21 = PI - phi12;
83
+
84
+ // Second interface
85
+ vec3 baseIOR = fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
86
+ vec3 R1 = iorToFresnel0( baseIOR, iridescenceIor );
87
+ vec3 R23 = schlickFresnel( cosTheta2, R1 );
88
+ vec3 phi23 = vec3( 0.0 );
89
+ if ( baseIOR[0] < iridescenceIor ) {
90
+
91
+ phi23[ 0 ] = PI;
92
+
93
+ }
94
+
95
+ if ( baseIOR[1] < iridescenceIor ) {
96
+
97
+ phi23[ 1 ] = PI;
98
+
99
+ }
100
+
101
+ if ( baseIOR[2] < iridescenceIor ) {
102
+
103
+ phi23[ 2 ] = PI;
104
+
105
+ }
106
+
107
+ // Phase shift
108
+ float OPD = 2.0 * iridescenceIor * thinFilmThickness * cosTheta2;
109
+ vec3 phi = vec3( phi21 ) + phi23;
110
+
111
+ // Compound terms
112
+ vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );
113
+ vec3 r123 = sqrt( R123 );
114
+ vec3 Rs = square( T121 ) * R23 / ( vec3( 1.0 ) - R123 );
115
+
116
+ // Reflectance term for m = 0 (DC term amplitude)
117
+ vec3 C0 = R12 + Rs;
118
+ I = C0;
119
+
120
+ // Reflectance term for m > 0 (pairs of diracs)
121
+ vec3 Cm = Rs - T121;
122
+ for ( int m = 1; m <= 2; ++ m ) {
123
+
124
+ Cm *= r123;
125
+ vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );
126
+ I += Cm * Sm;
127
+
128
+ }
129
+
130
+ // Since out of gamut colors might be produced, negative color values are clamped to 0.
131
+ return max( I, vec3( 0.0 ) );
132
+
133
+ }
134
+
135
+ `;