     return;
 #endif
 
-    // Metallic and Roughness material properties are packed together
-    // In glTF, these factors can be specified by fixed scalar values
-    // or from a metallic-roughness map
-    float perceptualRoughness = 0.0;
-    float metallic = 0.0;
-
-    vec3 diffuseColor = vec3(0.0);
-    vec3 specularColor= vec3(0.0);
-    vec3 f0 = vec3(0.04);
+    MaterialInfo materialInfo;
+
+    materialInfo.baseColor = baseColor.rgb;
 
 #ifdef MATERIAL_SPECULARGLOSSINESS
-
-#ifdef HAS_SPECULAR_GLOSSINESS_MAP
-    vec4 sgSample = SRGBtoLINEAR(texture(u_SpecularGlossinessSampler, getSpecularGlossinessUV()));
-    perceptualRoughness = (1.0 - sgSample.a * u_GlossinessFactor); // glossiness to roughness
-    f0 = sgSample.rgb * u_SpecularFactor; // specular
-#else
-    f0 = u_SpecularFactor;
-    perceptualRoughness = 1.0 - u_GlossinessFactor;
-#endif // ! HAS_SPECULAR_GLOSSINESS_MAP
-
-    // f0 = specular
-    specularColor = f0;
-    float oneMinusSpecularStrength = 1.0 - max(max(f0.r, f0.g), f0.b);
-    diffuseColor = baseColor.rgb * oneMinusSpecularStrength;
-
-#endif // ! MATERIAL_SPECULARGLOSSINESS
+    materialInfo = getSpecularGlossinessInfo(materialInfo);
+#endif
 
 #ifdef MATERIAL_METALLICROUGHNESS
-
-#ifdef HAS_METALLIC_ROUGHNESS_MAP
-    // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
-    // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
-    vec4 mrSample = texture(u_MetallicRoughnessSampler, getMetallicRoughnessUV());
-    perceptualRoughness = mrSample.g * u_RoughnessFactor;
-    metallic = mrSample.b * u_MetallicFactor;
-#else
-    metallic = u_MetallicFactor;
-    perceptualRoughness = u_RoughnessFactor;
-#endif
-
-    diffuseColor = baseColor.rgb * (vec3(1.0) - f0) * (1.0 - metallic);
-    specularColor = mix(f0, baseColor.rgb, metallic);
-#endif // ! MATERIAL_METALLICROUGHNESS
-
-    MaterialInfo materialInfo;
-
-    perceptualRoughness = clamp(perceptualRoughness, 0.0, 1.0);
-    metallic = clamp(metallic, 0.0, 1.0);
+    materialInfo = getMetallicRoughnessInfo(materialInfo);
+#endif
+
+#ifdef MATERIAL_SHEEN
+    materialInfo = getSheenInfo(materialInfo);
+#endif
+
+#ifdef MATERIAL_CLEARCOAT
+    materialInfo = getClearCoatInfo(materialInfo);
+#endif
+
+    materialInfo.perceptualRoughness = clamp(materialInfo.perceptualRoughness, 0.0, 1.0);
+    materialInfo.metallic = clamp(materialInfo.metallic, 0.0, 1.0);
 
     // Roughness is authored as perceptual roughness; as is convention,
-    // convert to material roughness by squaring the perceptual roughness [2].
-    float alphaRoughness = perceptualRoughness * perceptualRoughness;
+    // convert to material roughness by squaring the perceptual roughness.
+    materialInfo.alphaRoughness = materialInfo.perceptualRoughness * materialInfo.perceptualRoughness;
 
     // Compute reflectance.
-    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
-
-    vec3 specularEnvironmentR0 = specularColor.rgb;
+    float reflectance = max(max(materialInfo.f0.r, materialInfo.f0.g), materialInfo.f0.b);
+
     // Anything less than 2% is physically impossible and is instead considered to be shadowing. Compare to "Real-Time-Rendering" 4th editon on page 325.
-    vec3 specularEnvironmentR90 = vec3(clamp(reflectance * 50.0, 0.0, 1.0));
-
-#ifdef MATERIAL_SHEEN
-    #ifdef HAS_SHEEN_COLOR_INTENSITY_TEXTURE_MAP
-        vec3 sheenSample = texture(u_sheenColorIntensitySampler, getSheenUV());
-        materialInfo.sheenColor = sheenSample.xyz * u_SheenColorFactor;
-        materialInfo.sheenIntensity = sheenSample.w * u_SheenIntensityFactor;
-    #else
-        materialInfo.sheenColor = u_SheenColorFactor;
-        materialInfo.sheenIntensity = u_SheenIntensityFactor;
-    #endif
-    materialInfo.sheenRoughness = u_SheenRoughness;
-#endif
-
-#ifdef MATERIAL_CLEARCOAT
-    #ifdef HAS_CLEARCOAT_TEXTURE_MAP
-        vec4 ccSample = texture(u_ClearcoatSampler, getClearcoatUV());
-        materialInfo.clearcoatFactor = ccSample.r * u_ClearcoatFactor;
-    #else
-        materialInfo.clearcoatFactor = u_ClearcoatFactor;
-    #endif
-
-    #ifdef HAS_CLEARCOAT_ROUGHNESS_MAP
-        vec4 ccSampleRough = texture(u_ClearcoatRoughnessSampler, getClearcoatRoughnessUV());
-        materialInfo.clearcoatRoughness = ccSampleRough.g * u_ClearcoatRoughnessFactor;
-    #else
-        materialInfo.clearcoatRoughness = u_ClearcoatRoughnessFactor;
-    #endif
-
-    #ifdef HAS_CLEARCOAT_NORMAL_MAP
-        vec4 ccSampleNor = texture(u_ClearcoatNormalSampler, getClearcoatNormalUV());
-        materialInfo.clearcoatNormal = ccSampleNor.xyz;
-    #else
-        materialInfo.clearcoatNormal = getNormal(true); // get geometry normal
-    #endif
-#endif
-
-    // LIGHTING
+    materialInfo.f90 = vec3(clamp(reflectance * 50.0, 0.0, 1.0));
+
     vec3 normal = getNormal(false);
     vec3 view = normalize(u_Camera - v_Position);
 
-    materialInfo.perceptualRoughness = perceptualRoughness;
-    materialInfo.f0 = specularEnvironmentR0;
-    materialInfo.alphaRoughness = alphaRoughness;
-    materialInfo.diffuseColor = diffuseColor;
-    materialInfo.f90 = specularEnvironmentR90;
-    materialInfo.specularColor = specularColor;
     materialInfo.normal = normal;
-    materialInfo.baseColor = baseColor.rgb;
-
+
+    // LIGHTING
     vec3 f_specular = vec3(0.0);
     vec3 f_diffuse = vec3(0.0);
     vec3 f_emissive = vec3(0.0);
 
     // Calculate lighting contribution from image based lighting source (IBL)
 #ifdef USE_IBL
-    f_specular += getSpecularIBLContribution(normal, view, perceptualRoughness, specularColor);
-    f_diffuse += getDiffuseIBLContribution(normal, diffuseColor);
+    f_specular += getSpecularIBLContribution(normal, view, materialInfo.perceptualRoughness, materialInfo.f0); // specularColor
+    f_diffuse += getDiffuseIBLContribution(normal, materialInfo.albedoColor);
 #endif
 
 vec3 punctualColor = vec3(0.0);

#else // debug output

    #ifdef DEBUG_METALLIC
        g_finalColor.rgb = vec3(metallic);
    #endif

    #ifdef DEBUG_ROUGHNESS
        g_finalColor.rgb = vec3(perceptualRoughness);
    #endif

    #ifdef DEBUG_NORMAL

uniform vec3 u_Camera;
uniform int u_MipCount;

struct MaterialInfo
{
    float perceptualRoughness;      // roughness value, as authored by the model creator (input to shader)
    vec3 f0;                        // full reflectance color (normal incidence angle)

    float alphaRoughness;           // roughness mapped to a more linear change in the roughness (proposed by [2])
    vec3 albedoColor;

    vec3 f90;                       // reflectance color at grazing angle
    float metallic;

    vec3 normal; // getNormal()
    vec3 baseColor; // getBaseColor()

    float sheenIntensity;
    vec3 sheenColor;
    float sheenRoughness;

    float clearcoatFactor;
    vec3 clearcoatNormal;
    float clearcoatRoughness;
};

vec4 getBaseColor()
{
    vec4 baseColor = vec4(1, 1, 1, 1);
