Revision control

+ // Copyright (C) 2026 The Qt Company Ltd.
+ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+ #ifndef IRIDESCENCE_GLSLLIB
+ #define IRIDESCENCE_GLSLLIB 1
+
+ #include "principledMaterialFresnel.glsllib" // qt_schlick
+
+ #ifndef M_PI
+ #define M_PI 3.14159265359
+ #endif
+
+ // Thin-film model of KHR_materials_iridescence (Belcour and Barla 2017)
+
+ vec3 qt_iridescenceFresnel0ToIor(vec3 f0)
+ {
+ vec3 sqrtF0 = sqrt(f0);
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ float qt_iridescenceIorToFresnel0(float transmittedIor, float incidentIor)
+ {
+ float r = (transmittedIor - incidentIor) / (transmittedIor + incidentIor);
+ return r * r;
+ }
+
+ vec3 qt_iridescenceIorToFresnel0(vec3 transmittedIor, float incidentIor)
+ {
+ vec3 r = (transmittedIor - vec3(incidentIor)) / (transmittedIor + vec3(incidentIor));
+ return r * r;
+ }
+
+ // Optical path difference to color, using the extension's fit of the CIE XYZ curves
+ vec3 qt_iridescenceEvalSensitivity(float opd, vec3 shift)
+ {
+ float phase = 2.0 * M_PI * opd * 1.0e-9;
+ vec3 val = vec3(5.4856e-13, 4.4201e-13, 5.2481e-13);
+ vec3 pos = vec3(1.6810e+06, 1.7953e+06, 2.2084e+06);
+ vec3 var = vec3(4.3278e+09, 9.3046e+09, 6.6121e+09);
+
+ vec3 xyz = val * sqrt(2.0 * M_PI * var) * cos(pos * phase + shift) * exp(-(phase * phase) * var);
+ xyz.x += 9.7470e-14 * sqrt(2.0 * M_PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift[0])
+ * exp(-4.5282e+09 * (phase * phase));
+ xyz /= 1.0685e-7;
+
+ // Column major
+ const mat3 qt_xyzToRec709 = mat3( 3.2404542, -0.9692660, 0.0556434,
+ -1.5371385, 1.8760108, -0.2040259,
+ -0.4985314, 0.0415560, 1.0572252);
+ return qt_xyzToRec709 * xyz;
+ }
+
+ // Thickness is in nanometers
+ vec3 qt_evalIridescence(float outsideIor, float iridescenceIor, float cosTheta1,
+ float iridescenceThickness, vec3 baseF0)
+ {
+ float r0 = qt_iridescenceIorToFresnel0(iridescenceIor, outsideIor);
+ float r12 = r0 + (1.0 - r0) * qt_schlick(cosTheta1);
+ float t121 = 1.0 - r12;
+
+ // Total internal reflection
+ float sinTheta2Sq = (outsideIor / iridescenceIor) * (outsideIor / iridescenceIor)
+ * (1.0 - cosTheta1 * cosTheta1);
+ float cosTheta2Sq = 1.0 - sinTheta2Sq;
+ if (cosTheta2Sq < 0.0)
+ return vec3(1.0);
+ float cosTheta2 = sqrt(cosTheta2Sq);
+
+ // The conversion is singular at F0 = 1
+ vec3 baseIor = qt_iridescenceFresnel0ToIor(clamp(baseF0, 0.0, 0.9999));
+ vec3 r1 = qt_iridescenceIorToFresnel0(baseIor, iridescenceIor);
+ vec3 r23 = r1 + (vec3(1.0) - r1) * qt_schlick(cosTheta2);
+
+ float opd = 2.0 * iridescenceIor * iridescenceThickness * cosTheta2;
+ float phi12 = iridescenceIor < outsideIor ? M_PI : 0.0;
+ float phi21 = M_PI - phi12;
+ vec3 phi23 = vec3(baseIor[0] < iridescenceIor ? M_PI : 0.0,
+ baseIor[1] < iridescenceIor ? M_PI : 0.0,
+ baseIor[2] < iridescenceIor ? M_PI : 0.0);
+ vec3 phi = vec3(phi21) + phi23;
+
+ // Two terms of the series are enough, as r123 < 1
+ vec3 r123 = clamp(vec3(r12) * r23, 1e-5, 0.9999);
+ vec3 r123Sqrt = sqrt(r123);
+ vec3 rs = (t121 * t121) * r23 / (vec3(1.0) - r123);
+
+ vec3 c0 = vec3(r12) + rs;
+ vec3 i = c0;
+
+ vec3 cm = rs - vec3(t121);
+ for (int m = 1; m <= 2; ++m) {
+ cm *= r123Sqrt;
+ vec3 sm = 2.0 * qt_iridescenceEvalSensitivity(float(m) * opd, float(m) * phi);
+ i += cm * sm;
+ }
+
+ return max(i, vec3(0.0));
+ }
+
+ #endif // IRIDESCENCE_GLSLLIB