From 838d6f065cb98edd168b2f3388cb74296638c933 Mon Sep 17 00:00:00 2001 From: Jiaye Date: Thu, 19 Mar 2026 15:00:56 +0800 Subject: [PATCH] refactor(pbr): clearer semantics --- .../Hair Specular/Shaders/Hair/Hair.hlsli | 8 +-- package/Shaders/Common/BRDF.hlsli | 4 +- package/Shaders/Common/LightingEval.hlsli | 9 +-- package/Shaders/Common/PBR.hlsli | 30 ++++----- package/Shaders/Common/PBRMath.hlsli | 36 +++++------ package/Shaders/Tests/TestPBR.hlsl | 64 +++++++++---------- 6 files changed, 75 insertions(+), 76 deletions(-) diff --git a/features/Hair Specular/Shaders/Hair/Hair.hlsli b/features/Hair Specular/Shaders/Hair/Hair.hlsli index eb64135c94..d7d1ea07bb 100644 --- a/features/Hair Specular/Shaders/Hair/Hair.hlsli +++ b/features/Hair Specular/Shaders/Hair/Hair.hlsli @@ -140,7 +140,7 @@ namespace Hair }; float hairIOR = 1.55; - float3 specularColor = HairF0(); + float3 F0 = HairF0(); float3 Tp; float Mp, Np, Fp, a, h, f; @@ -151,14 +151,14 @@ namespace Hair // R Mp = Hair_g(B[0], ThetaH - Alpha[0]); Np = 0.25 * cosHalfPhi; - Fp = BRDF::F_Schlick(specularColor, sqrt(saturate(0.5 + 0.5 * VdotL))).x; + Fp = BRDF::F_Schlick(F0, sqrt(saturate(0.5 + 0.5 * VdotL))).x; R = (Mp * Np) * (Fp * lerp(1, backlit, saturate(-VdotL))); // TT Mp = Hair_g(B[1], ThetaH - Alpha[1]); a = (1.55f / hairIOR) * rcp(n_prime); h = cosHalfPhi * (1 + a * (0.6 - 0.8 * cosPhi)); - f = BRDF::F_Schlick(specularColor, cosThetaD * sqrt(saturate(1 - h * h))).x; + f = BRDF::F_Schlick(F0, cosThetaD * sqrt(saturate(1 - h * h))).x; Fp = (1 - f) * (1 - f); Tp = pow(abs(baseColor), 0.5 * sqrt(1 - (h * a) * (h * a)) / cosThetaD); Np = exp(-3.65 * cosPhi - 3.98); @@ -166,7 +166,7 @@ namespace Hair // TRT Mp = Hair_g(B[2], ThetaH - Alpha[2]); - f = BRDF::F_Schlick(specularColor, cosThetaD * 0.5f).x; + f = BRDF::F_Schlick(F0, cosThetaD * 0.5f).x; Fp = (1 - f) * (1 - f) * f; Tp = pow(abs(baseColor), 0.8 / cosThetaD); Np = exp(17 * cosPhi - 16.78); diff --git a/package/Shaders/Common/BRDF.hlsli b/package/Shaders/Common/BRDF.hlsli index 217b5fd893..f16a0679ee 100644 --- a/package/Shaders/Common/BRDF.hlsli +++ b/package/Shaders/Common/BRDF.hlsli @@ -97,10 +97,10 @@ namespace BRDF // Specular BRDFs // [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"] - float3 F_Schlick(float3 specularColor, float VdotH) + float3 F_Schlick(float3 F0, float VdotH) { float Fc = pow(1 - VdotH, 5); - return Fc + (1 - Fc) * specularColor; + return Fc + (1 - Fc) * F0; } float3 F_Schlick(float3 F0, float3 F90, float VdotH) diff --git a/package/Shaders/Common/LightingEval.hlsli b/package/Shaders/Common/LightingEval.hlsli index f24b1b6d55..b42f460d73 100644 --- a/package/Shaders/Common/LightingEval.hlsli +++ b/package/Shaders/Common/LightingEval.hlsli @@ -180,16 +180,17 @@ void EvaluateWetnessLighting(float3 wetnessNormal, DirectContext context, float float G = BRDF::Vis_SmithJointApprox(roughness, NdotV, NdotL); float3 F = BRDF::F_Schlick(wetnessF0, VdotH); - F *= wetnessStrength; + // Separate physical Fresnel from effective contribution weighted by strength + float3 wetnessF = F * wetnessStrength; - float3 wetnessSpecular = D * G * F * NdotL * lightColor; + float3 wetnessSpecular = D * G * wetnessF * NdotL * lightColor; # if !defined(TRUE_PBR) wetnessSpecular *= Color::PBRLightingCompensation * Color::PBRLightingScale; // Compensate for GGX on traditional specular # endif - lightingOutput.diffuse *= 1 - F; - lightingOutput.specular *= 1 - F; + lightingOutput.diffuse *= 1 - wetnessF; + lightingOutput.specular *= 1 - wetnessF; lightingOutput.specular += wetnessSpecular; } diff --git a/package/Shaders/Common/PBR.hlsli b/package/Shaders/Common/PBR.hlsli index dd8033f4d6..4f1d4dd911 100644 --- a/package/Shaders/Common/PBR.hlsli +++ b/package/Shaders/Common/PBR.hlsli @@ -12,7 +12,7 @@ namespace PBR { #if defined(GLINT) - float3 GetSpecularDirectLightMultiplierMicrofacetWithGlint(float noise, float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH, float glintH, + float3 SpecularMicrofacetWithGlint(float noise, float roughness, float3 F0, float NdotL, float NdotV, float NdotH, float VdotH, float glintH, float logDensity, float microfacetRoughness, float densityRandomization, Glints::GlintCachedVars glintCache, out float3 F) { @@ -23,7 +23,7 @@ namespace PBR D = Glints::SampleGlints2023NDF(noise, logDensity, microfacetRoughness, densityRandomization, glintCache, glintH, D, D_max).x; } float G = BRDF::Vis_SmithJointApprox(roughness, NdotV, NdotL); - F = BRDF::F_Schlick(specularColor, VdotH); + F = BRDF::F_Schlick(F0, VdotH); return D * G * F; } @@ -57,7 +57,7 @@ namespace PBR }; float hairIOR = HairIOR(); - float specularColor = IORToF0(hairIOR); + float F0 = IORToF0(hairIOR); float3 Tp; float Mp, Np, Fp, a, h, f; @@ -65,14 +65,14 @@ namespace PBR // R Mp = HairGaussian(B[0], ThetaH - Alpha[0]); Np = 0.25 * cosHalfPhi; - Fp = BRDF::F_Schlick(specularColor, sqrt(saturate(0.5 + 0.5 * VdotL))).x; + Fp = BRDF::F_Schlick(F0, sqrt(saturate(0.5 + 0.5 * VdotL))).x; S += (Mp * Np) * (Fp * lerp(1, backlit, saturate(-VdotL))); // TT Mp = HairGaussian(B[1], ThetaH - Alpha[1]); a = (1.55f / hairIOR) * rcp(n_prime); h = cosHalfPhi * (1 + a * (0.6 - 0.8 * cosPhi)); - f = BRDF::F_Schlick(specularColor, cosThetaD * sqrt(saturate(1 - h * h))).x; + f = BRDF::F_Schlick(F0, cosThetaD * sqrt(saturate(1 - h * h))).x; Fp = (1 - f) * (1 - f); Tp = pow(abs(material.BaseColor), 0.5 * sqrt(1 - (h * a) * (h * a)) / cosThetaD); Np = exp(-3.65 * cosPhi - 3.98); @@ -80,7 +80,7 @@ namespace PBR // TRT Mp = HairGaussian(B[2], ThetaH - Alpha[2]); - f = BRDF::F_Schlick(specularColor, cosThetaD * 0.5f).x; + f = BRDF::F_Schlick(F0, cosThetaD * 0.5f).x; Fp = (1 - f) * (1 - f) * f; Tp = pow(abs(material.BaseColor), 0.8 / cosThetaD); Np = exp(17 * cosPhi - 16.78); @@ -152,22 +152,22 @@ namespace PBR else #endif { - float3 F; + float3 F; // Fresnel reflectance at current (V,H) angle #if defined(GLINT) - float3 specular = GetSpecularDirectLightMultiplierMicrofacetWithGlint(material.Noise, material.Roughness, material.F0, satNdotL, satNdotV, satNdotH, satVdotH, mul(tbnTr, H).x, + float3 Fr = SpecularMicrofacetWithGlint(material.Noise, material.Roughness, material.F0, satNdotL, satNdotV, satNdotH, satVdotH, mul(tbnTr, H).x, material.GlintLogMicrofacetDensity, material.GlintMicrofacetRoughness, material.GlintDensityRandomization, material.GlintCache, F); #else - float3 specular = GetSpecularDirectLightMultiplierMicrofacet(material.Roughness, material.F0, satNdotL, satNdotV, satNdotH, satVdotH, F); + float3 Fr = SpecularMicrofacet(material.Roughness, material.F0, satNdotL, satNdotV, satNdotH, satVdotH, F); #endif float3 kD = 1 - F; lightingOutput.diffuse += detailedLightColor * satNdotL * BRDF::Diffuse_Lambert() * kD; - lightingOutput.specular += specular * detailedLightColor * satNdotL; + lightingOutput.specular += Fr * detailedLightColor * satNdotL; #if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) [branch] if ((PBRFlags & Flags::Fuzz) != 0) { - float3 fuzzSpecular = GetSpecularDirectLightMultiplierMicroflakes(material.Roughness, material.FuzzColor, satNdotL, satNdotV, satNdotH, satVdotH) * detailedLightColor * satNdotL; + float3 fuzzSpecular = SpecularMicroflakes(material.Roughness, material.FuzzColor, satNdotL, satNdotV, satNdotH, satVdotH) * detailedLightColor * satNdotL; lightingOutput.specular = lerp(lightingOutput.specular, fuzzSpecular, material.FuzzWeight); } @@ -194,14 +194,14 @@ namespace PBR } float3 coatF; - float3 coatSpecular = GetSpecularDirectLightMultiplierMicrofacet(material.CoatRoughness, material.CoatF0, coatNdotL, coatNdotV, coatNdotH, coatVdotH, coatF) * context.coatLightColor * coatNdotL; + float3 coatFr = SpecularMicrofacet(material.CoatRoughness, material.CoatF0, coatNdotL, coatNdotV, coatNdotH, coatVdotH, coatF); float3 layerAttenuation = 1 - coatF * material.CoatStrength; lightingOutput.diffuse *= layerAttenuation; lightingOutput.specular *= layerAttenuation; lightingOutput.coatDiffuse += context.coatLightColor * coatNdotL * BRDF::Diffuse_Lambert(); - lightingOutput.specular += coatSpecular * material.CoatStrength; + lightingOutput.specular += coatFr * context.coatLightColor * coatNdotL * material.CoatStrength; } #endif } @@ -242,8 +242,8 @@ namespace PBR float2 specularBRDF = BRDF::EnvBRDF(material.Roughness, NdotV); lobeWeights.specular = material.F0 * specularBRDF.x + specularBRDF.y; - float3 kD = 1 - lobeWeights.specular; - lobeWeights.diffuse *= kD; + // Energy conservation: diffuse receives only what specular does not reflect + lobeWeights.diffuse *= 1 - lobeWeights.specular; #if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) [branch] if ((PBRFlags & Flags::TwoLayer) != 0) diff --git a/package/Shaders/Common/PBRMath.hlsli b/package/Shaders/Common/PBRMath.hlsli index ea476ce663..b9ff73692f 100644 --- a/package/Shaders/Common/PBRMath.hlsli +++ b/package/Shaders/Common/PBRMath.hlsli @@ -57,37 +57,37 @@ namespace PBR static const uint LandTile5HasGlint = (1 << 17); } - /// @brief Calculate specular reflection using GGX microfacet model - /// @param roughness Surface roughness [0,1] - /// @param specularColor F0 reflectance at normal incidence + /// @brief Evaluate GGX microfacet specular BRDF (D * Vis * F) + /// @param roughness Perceptual roughness [0,1] + /// @param F0 Reflectance at normal incidence /// @param NdotL Dot product of normal and light direction /// @param NdotV Dot product of normal and view direction /// @param NdotH Dot product of normal and half vector /// @param VdotH Dot product of view and half vector - /// @param F Output Fresnel term - /// @return Specular BRDF term (D * G * F) - float3 GetSpecularDirectLightMultiplierMicrofacet(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH, out float3 F) + /// @param F Output Fresnel reflectance at current angle + /// @return Specular BRDF value (D * Vis * F) + float3 SpecularMicrofacet(float roughness, float3 F0, float NdotL, float NdotV, float NdotH, float VdotH, out float3 F) { float D = BRDF::D_GGX(roughness, NdotH); float G = BRDF::Vis_SmithJointApprox(roughness, NdotV, NdotL); - F = BRDF::F_Schlick(specularColor, VdotH); + F = BRDF::F_Schlick(F0, VdotH); return D * G * F; } - /// @brief Calculate specular reflection using Charlie microflake model (for sheen/fabric) - /// @param roughness Surface roughness [0,1] - /// @param specularColor F0 reflectance at normal incidence + /// @brief Evaluate Charlie microflake specular BRDF for sheen/fabric (D * Vis * F) + /// @param roughness Perceptual roughness [0,1] + /// @param F0 Reflectance at normal incidence /// @param NdotL Dot product of normal and light direction /// @param NdotV Dot product of normal and view direction /// @param NdotH Dot product of normal and half vector /// @param VdotH Dot product of view and half vector - /// @return Specular BRDF term (D * G * F) - float3 GetSpecularDirectLightMultiplierMicroflakes(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH) + /// @return Specular BRDF value (D * Vis * F) + float3 SpecularMicroflakes(float roughness, float3 F0, float NdotL, float NdotV, float NdotH, float VdotH) { float D = BRDF::D_Charlie(roughness, NdotH); float G = BRDF::Vis_Neubelt(NdotV, NdotL); - float3 F = BRDF::F_Schlick(specularColor, VdotH); + float3 F = BRDF::F_Schlick(F0, VdotH); return D * G * F; } @@ -132,7 +132,6 @@ namespace PBR /// @return Wetness specular color contribution float3 GetWetnessDirectLightSpecularInput(float3 N, float3 V, float3 L, float3 lightColor, float roughness) { - const float wetnessStrength = 1; const float wetnessF0 = 0.02; float3 H = normalize(V + L); @@ -141,10 +140,10 @@ namespace PBR float NdotH = saturate(dot(N, H)); float VdotH = saturate(dot(V, H)); - float3 wetnessF; - float3 wetnessSpecular = GetSpecularDirectLightMultiplierMicrofacet(roughness, wetnessF0, NdotL, NdotV, NdotH, VdotH, wetnessF) * lightColor * NdotL; + float3 F; + float3 Fr = SpecularMicrofacet(roughness, wetnessF0, NdotL, NdotV, NdotH, VdotH, F); - return wetnessSpecular * wetnessStrength; + return Fr * lightColor * NdotL; } /// @brief Calculate wetness specular lobe weight for indirect lighting @@ -154,14 +153,13 @@ namespace PBR /// @return Wetness specular lobe weight float3 GetWetnessIndirectSpecularLobeWeight(float3 N, float3 V, float roughness) { - const float wetnessStrength = 1; const float wetnessF0 = 0.02; float NdotV = saturate(abs(dot(N, V)) + EPSILON_DOT_CLAMP); float2 specularBRDF = BRDF::EnvBRDF(roughness, NdotV); float3 specularLobeWeight = wetnessF0 * specularBRDF.x + specularBRDF.y; - return specularLobeWeight * wetnessStrength; + return specularLobeWeight; } } diff --git a/package/Shaders/Tests/TestPBR.hlsl b/package/Shaders/Tests/TestPBR.hlsl index 62aaacfd11..34864cd6b2 100644 --- a/package/Shaders/Tests/TestPBR.hlsl +++ b/package/Shaders/Tests/TestPBR.hlsl @@ -98,15 +98,15 @@ { // Test 1: Basic calculation with typical values float roughness = 0.5f; - float3 specularColor = float3(0.04, 0.04, 0.04); // Dielectric F0 + float3 F0 = float3(0.04, 0.04, 0.04); // Dielectric F0 float NdotL = 0.8f; float NdotV = 0.7f; float NdotH = 0.9f; float VdotH = 0.85f; float3 F; - float3 result = PBR::GetSpecularDirectLightMultiplierMicrofacet( - roughness, specularColor, NdotL, NdotV, NdotH, VdotH, F); + float3 result = PBR::SpecularMicrofacet( + roughness, F0, NdotL, NdotV, NdotH, VdotH, F); // Test 2: Result should be non-negative (physical constraint) ASSERT(IsTrue, result.x >= 0.0f); @@ -114,9 +114,9 @@ ASSERT(IsTrue, result.z >= 0.0f); // Test 3: Fresnel should be >= F0 (increases toward grazing) - ASSERT(IsTrue, F.x >= specularColor.x); - ASSERT(IsTrue, F.y >= specularColor.y); - ASSERT(IsTrue, F.z >= specularColor.z); + ASSERT(IsTrue, F.x >= F0.x); + ASSERT(IsTrue, F.y >= F0.y); + ASSERT(IsTrue, F.z >= F0.z); // Test 4: Fresnel should be <= 1.0 ASSERT(IsTrue, F.x <= 1.0f); @@ -125,30 +125,30 @@ // Test 5: Grazing angle increases Fresnel (lower VdotH) float3 F_grazing; - PBR::GetSpecularDirectLightMultiplierMicrofacet( - roughness, specularColor, 0.1f, 0.1f, 0.5f, 0.1f, F_grazing); + PBR::SpecularMicrofacet( + roughness, F0, 0.1f, 0.1f, 0.5f, 0.1f, F_grazing); ASSERT(IsTrue, F_grazing.x > F.x); // Test 6: Roughness variation affects result float3 F2; - float3 resultSmooth = PBR::GetSpecularDirectLightMultiplierMicrofacet( - 0.1f, specularColor, NdotL, NdotV, NdotH, VdotH, F2); - float3 resultRough = PBR::GetSpecularDirectLightMultiplierMicrofacet( - 0.9f, specularColor, NdotL, NdotV, NdotH, VdotH, F2); + float3 resultSmooth = PBR::SpecularMicrofacet( + 0.1f, F0, NdotL, NdotV, NdotH, VdotH, F2); + float3 resultRough = PBR::SpecularMicrofacet( + 0.9f, F0, NdotL, NdotV, NdotH, VdotH, F2); // Roughness affects specular (smooth should generally be brighter at peak) ASSERT(IsTrue, resultSmooth.x >= 0.0f && resultRough.x >= 0.0f); // Test 7: Perfect alignment (NdotH=1) should give peak specular float3 F_aligned; - float3 result_aligned = PBR::GetSpecularDirectLightMultiplierMicrofacet( - roughness, specularColor, 1.0f, 1.0f, 1.0f, 1.0f, F_aligned); + float3 result_aligned = PBR::SpecularMicrofacet( + roughness, F0, 1.0f, 1.0f, 1.0f, 1.0f, F_aligned); ASSERT(IsTrue, result_aligned.x >= result.x); // Test 8: Metallic materials (higher F0) float3 metalF0 = float3(0.9f, 0.8f, 0.7f); float3 F_metal; - float3 result_metal = PBR::GetSpecularDirectLightMultiplierMicrofacet( + float3 result_metal = PBR::SpecularMicrofacet( roughness, metalF0, NdotL, NdotV, NdotH, VdotH, F_metal); ASSERT(IsTrue, result_metal.x >= 0.0f); @@ -159,14 +159,14 @@ [numthreads(1, 1, 1)] void TestGetSpecularMicroflakes() { // Test 1: Basic calculation (for fabric/sheen materials) float roughness = 0.3f; - float3 specularColor = float3(0.04, 0.04, 0.04); + float3 F0 = float3(0.04, 0.04, 0.04); float NdotL = 0.8f; float NdotV = 0.7f; float NdotH = 0.9f; float VdotH = 0.85f; - float3 result = PBR::GetSpecularDirectLightMultiplierMicroflakes( - roughness, specularColor, NdotL, NdotV, NdotH, VdotH); + float3 result = PBR::SpecularMicroflakes( + roughness, F0, NdotL, NdotV, NdotH, VdotH); // Test 2: Result should be non-negative ASSERT(IsTrue, result.x >= 0.0f); @@ -174,10 +174,10 @@ ASSERT(IsTrue, result.z >= 0.0f); // Test 3: Roughness variation should affect result - float3 resultSmooth = PBR::GetSpecularDirectLightMultiplierMicroflakes( - 0.1f, specularColor, NdotL, NdotV, NdotH, VdotH); - float3 resultRough = PBR::GetSpecularDirectLightMultiplierMicroflakes( - 0.9f, specularColor, NdotL, NdotV, NdotH, VdotH); + float3 resultSmooth = PBR::SpecularMicroflakes( + 0.1f, F0, NdotL, NdotV, NdotH, VdotH); + float3 resultRough = PBR::SpecularMicroflakes( + 0.9f, F0, NdotL, NdotV, NdotH, VdotH); // All results should be valid (Charlie has unique distribution) ASSERT(IsTrue, resultSmooth.x >= 0.0f && resultRough.x >= 0.0f); @@ -185,22 +185,22 @@ // Test 4: Charlie distribution has different behavior than GGX // Charlie peaks at grazing, so lower NdotH can give higher values - float3 result_peak = PBR::GetSpecularDirectLightMultiplierMicroflakes( - roughness, specularColor, NdotL, NdotV, 0.1f, VdotH); + float3 result_peak = PBR::SpecularMicroflakes( + roughness, F0, NdotL, NdotV, 0.1f, VdotH); ASSERT(IsTrue, result_peak.x >= 0.0f); // Test 5: Both microfacet models should produce valid results float3 F_ggx; - float3 result_ggx = PBR::GetSpecularDirectLightMultiplierMicrofacet( - roughness, specularColor, NdotL, NdotV, NdotH, VdotH, F_ggx); + float3 result_ggx = PBR::SpecularMicrofacet( + roughness, F0, NdotL, NdotV, NdotH, VdotH, F_ggx); // Both models should give valid positive results ASSERT(IsTrue, result_ggx.x >= 0.0f); // Test 6: Colored specular (tinted sheen/fabric) float3 coloredSpec = float3(0.1, 0.05, 0.02); - float3 result_colored = PBR::GetSpecularDirectLightMultiplierMicroflakes( + float3 result_colored = PBR::SpecularMicroflakes( roughness, coloredSpec, NdotL, NdotV, NdotH, VdotH); ASSERT(IsTrue, result_colored.x >= 0.0f); @@ -387,7 +387,7 @@ float3 F; // Test with typical dielectric material (production-safe ranges) - float3 result = PBR::GetSpecularDirectLightMultiplierMicrofacet( + float3 result = PBR::SpecularMicrofacet( 0.5f, float3(0.04, 0.04, 0.04), 0.8f, 0.7f, 0.9f, 0.85f, F); ASSERT(IsTrue, all(!isnan(result))); ASSERT(IsTrue, all(!isinf(result))); @@ -398,26 +398,26 @@ [numthreads(1, 1, 1)] void TestSpecularMicroflakesEdgeCases() { // Test with typical parameters - float3 result = PBR::GetSpecularDirectLightMultiplierMicroflakes( + float3 result = PBR::SpecularMicroflakes( 0.5f, float3(0.04, 0.04, 0.04), 0.8f, 0.7f, 0.9f, 0.85f); ASSERT(IsTrue, all(!isnan(result))); ASSERT(IsTrue, all(!isinf(result))); ASSERT(IsTrue, all(result >= 0.0f)); // Test with zero specular color - float3 result_black = PBR::GetSpecularDirectLightMultiplierMicroflakes( + float3 result_black = PBR::SpecularMicroflakes( 0.5f, float3(0.0, 0.0, 0.0), 0.8f, 0.7f, 0.9f, 0.85f); ASSERT(IsTrue, all(!isnan(result_black))); ASSERT(IsTrue, all(result_black >= 0.0f)); // Test with HDR specular color (colored sheen) - float3 result_hdr = PBR::GetSpecularDirectLightMultiplierMicroflakes( + float3 result_hdr = PBR::SpecularMicroflakes( 0.5f, float3(2.0, 1.5, 1.0), 0.8f, 0.7f, 0.9f, 0.85f); ASSERT(IsTrue, all(!isnan(result_hdr))); ASSERT(IsTrue, all(result_hdr >= 0.0f)); // Test with grazing angles (Charlie distribution favors grazing) - float3 result_grazing = PBR::GetSpecularDirectLightMultiplierMicroflakes( + float3 result_grazing = PBR::SpecularMicroflakes( 0.3f, float3(0.1, 0.1, 0.1), 0.1f, 0.1f, 0.1f, 0.05f); ASSERT(IsTrue, all(!isnan(result_grazing))); ASSERT(IsTrue, all(result_grazing >= 0.0f));