Revision control
+ // Copyright (C) 2026 The Qt Company Ltd.
+ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+ #ifndef SHEEN_GLSLIB
+ #define SHEEN_GLSLIB 1
+
+ #ifndef M_PI
+ #define M_PI 3.14159265359
+ #endif
+
+ // Charlie sheen distribution (Estevez and Kulla 2017)
+ float qt_D_Charlie(in float sheenRoughness, in float NdotH)
+ {
+ float alpha = clamp(sheenRoughness * sheenRoughness, 0.000001, 1.0);
+ float invAlpha = 1.0 / alpha;
+ float cos2h = NdotH * NdotH;
+ float sin2h = max(1.0 - cos2h, 0.0078125); // 2^-7, so sin2h^2 does not underflow in fp16
+ return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * M_PI);
+ }
+
+ // Neubelt and Pettineo 2013 visibility, as permitted by KHR_materials_sheen
+ float qt_V_Neubelt(in float NdotL, in float NdotV)
+ {
+ return clamp(1.0 / (4.0 * (NdotL + NdotV - NdotL * NdotV)), 0.0, 1.0);
+ }
+
+ vec3 qt_sheenBSDF(in vec3 normal, in vec3 L, in vec3 V, in vec3 lightColor,
+ in vec3 sheenColor, in float sheenRoughness)
+ {
+ vec3 H = normalize(L + V);
+ float NdotL = clamp(dot(normal, L), 0.0, 1.0);
+ float NdotV = clamp(dot(normal, V), 0.0, 1.0);
+ float NdotH = clamp(dot(normal, H), 0.0, 1.0);
+
+ float D = qt_D_Charlie(sheenRoughness, NdotH);
+ float Vis = qt_V_Neubelt(NdotL, NdotV);
+ return sheenColor * D * Vis * NdotL * lightColor;
+ }
+
+ // Directional albedo of qt_D_Charlie with qt_V_Neubelt, fitted to the
+ // numerically integrated values with a maximum error of 0.03
+ float qt_E_sheen(in float NdotV, in float sheenRoughness)
+ {
+ float r = clamp(sheenRoughness, 0.0, 1.0);
+ float t = 1.0 / (r + 0.05);
+ float t2 = t * t;
+
+ float a = -1.3278 + 0.5106 * r - 0.2782 * t - 0.1400 * t2;
+ float b = 1.2805 - 0.4833 * r - 0.8451 * t - 0.0904 * t2;
+ float c = -0.8153 - 0.1470 * r + 0.3803 * t - 0.0222 * t2;
+
+ return clamp(exp((a + b * NdotV) * NdotV + c), 0.0, 1.0);
+ }
+
+ // Energy left for the layers beneath the sheen, from the view direction only
+ float qt_sheenAlbedoScaling(in vec3 sheenColor, in float NdotV, in float sheenRoughness)
+ {
+ float maxSheen = max(max(sheenColor.r, sheenColor.g), sheenColor.b);
+ return 1.0 - maxSheen * qt_E_sheen(NdotV, sheenRoughness);
+ }
+
+ #endif // SHEEN_GLSLIB