123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- #ifndef UNIVERSAL_BRDF_INCLUDED
- #define UNIVERSAL_BRDF_INCLUDED
- #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/BSDF.hlsl"
- #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
- #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Deprecated.hlsl"
- #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceData.hlsl"
- #define kDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%)
- struct BRDFData
- {
- half3 albedo;
- half3 diffuse;
- half3 specular;
- half reflectivity;
- half perceptualRoughness;
- half roughness;
- half roughness2;
- half grazingTerm;
- // We save some light invariant BRDF terms so we don't have to recompute
- // them in the light loop. Take a look at DirectBRDF function for detailed explaination.
- half normalizationTerm; // roughness * 4.0 + 2.0
- half roughness2MinusOne; // roughness^2 - 1.0
- };
- half ReflectivitySpecular(half3 specular)
- {
- #if defined(SHADER_API_GLES)
- return specular.r; // Red channel - because most metals are either monocrhome or with redish/yellowish tint
- #else
- return Max3(specular.r, specular.g, specular.b);
- #endif
- }
- half OneMinusReflectivityMetallic(half metallic)
- {
- // We'll need oneMinusReflectivity, so
- // 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
- // store (1-dielectricSpec) in kDielectricSpec.a, then
- // 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
- // = alpha - metallic * alpha
- half oneMinusDielectricSpec = kDielectricSpec.a;
- return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
- }
- half MetallicFromReflectivity(half reflectivity)
- {
- half oneMinusDielectricSpec = kDielectricSpec.a;
- return (reflectivity - kDielectricSpec.r) / oneMinusDielectricSpec;
- }
- inline void InitializeBRDFDataDirect(half3 albedo, half3 diffuse, half3 specular, half reflectivity, half oneMinusReflectivity, half smoothness, inout half alpha, out BRDFData outBRDFData)
- {
- outBRDFData = (BRDFData)0;
- outBRDFData.albedo = albedo;
- outBRDFData.diffuse = diffuse;
- outBRDFData.specular = specular;
- outBRDFData.reflectivity = reflectivity;
- outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness);
- outBRDFData.roughness = max(PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN_SQRT);
- outBRDFData.roughness2 = max(outBRDFData.roughness * outBRDFData.roughness, HALF_MIN);
- outBRDFData.grazingTerm = saturate(smoothness + reflectivity);
- outBRDFData.normalizationTerm = outBRDFData.roughness * half(4.0) + half(2.0);
- outBRDFData.roughness2MinusOne = outBRDFData.roughness2 - half(1.0);
- #ifdef _ALPHAPREMULTIPLY_ON
- outBRDFData.diffuse *= alpha;
- alpha = alpha * oneMinusReflectivity + reflectivity; // NOTE: alpha modified and propagated up.
- #endif
- }
- // Legacy: do not call, will not correctly initialize albedo property.
- inline void InitializeBRDFDataDirect(half3 diffuse, half3 specular, half reflectivity, half oneMinusReflectivity, half smoothness, inout half alpha, out BRDFData outBRDFData)
- {
- InitializeBRDFDataDirect(half3(0.0, 0.0, 0.0), diffuse, specular, reflectivity, oneMinusReflectivity, smoothness, alpha, outBRDFData);
- }
- // Initialize BRDFData for material, managing both specular and metallic setup using shader keyword _SPECULAR_SETUP.
- inline void InitializeBRDFData(half3 albedo, half metallic, half3 specular, half smoothness, inout half alpha, out BRDFData outBRDFData)
- {
- #ifdef _SPECULAR_SETUP
- half reflectivity = ReflectivitySpecular(specular);
- half oneMinusReflectivity = half(1.0) - reflectivity;
- half3 brdfDiffuse = albedo * (half3(1.0, 1.0, 1.0) - specular);
- half3 brdfSpecular = specular;
- #else
- half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic);
- half reflectivity = half(1.0) - oneMinusReflectivity;
- half3 brdfDiffuse = albedo * oneMinusReflectivity;
- half3 brdfSpecular = lerp(kDieletricSpec.rgb, albedo, metallic);
- #endif
- InitializeBRDFDataDirect(albedo, brdfDiffuse, brdfSpecular, reflectivity, oneMinusReflectivity, smoothness, alpha, outBRDFData);
- }
- inline void InitializeBRDFData(inout SurfaceData surfaceData, out BRDFData brdfData)
- {
- InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData);
- }
- half3 ConvertF0ForClearCoat15(half3 f0)
- {
- #if defined(SHADER_API_MOBILE)
- return ConvertF0ForAirInterfaceToF0ForClearCoat15Fast(f0);
- #else
- return ConvertF0ForAirInterfaceToF0ForClearCoat15(f0);
- #endif
- }
- inline void InitializeBRDFDataClearCoat(half clearCoatMask, half clearCoatSmoothness, inout BRDFData baseBRDFData, out BRDFData outBRDFData)
- {
- outBRDFData = (BRDFData)0;
- outBRDFData.albedo = half(1.0);
- // Calculate Roughness of Clear Coat layer
- outBRDFData.diffuse = kDielectricSpec.aaa; // 1 - kDielectricSpec
- outBRDFData.specular = kDielectricSpec.rgb;
- outBRDFData.reflectivity = kDielectricSpec.r;
- outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(clearCoatSmoothness);
- outBRDFData.roughness = max(PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN_SQRT);
- outBRDFData.roughness2 = max(outBRDFData.roughness * outBRDFData.roughness, HALF_MIN);
- outBRDFData.normalizationTerm = outBRDFData.roughness * half(4.0) + half(2.0);
- outBRDFData.roughness2MinusOne = outBRDFData.roughness2 - half(1.0);
- outBRDFData.grazingTerm = saturate(clearCoatSmoothness + kDielectricSpec.x);
- // Relatively small effect, cut it for lower quality
- #if !defined(SHADER_API_MOBILE)
- // Modify Roughness of base layer using coat IOR
- half ieta = lerp(1.0h, CLEAR_COAT_IETA, clearCoatMask);
- half coatRoughnessScale = Sq(ieta);
- half sigma = RoughnessToVariance(PerceptualRoughnessToRoughness(baseBRDFData.perceptualRoughness));
- baseBRDFData.perceptualRoughness = RoughnessToPerceptualRoughness(VarianceToRoughness(sigma * coatRoughnessScale));
- // Recompute base material for new roughness, previous computation should be eliminated by the compiler (as it's unused)
- baseBRDFData.roughness = max(PerceptualRoughnessToRoughness(baseBRDFData.perceptualRoughness), HALF_MIN_SQRT);
- baseBRDFData.roughness2 = max(baseBRDFData.roughness * baseBRDFData.roughness, HALF_MIN);
- baseBRDFData.normalizationTerm = baseBRDFData.roughness * 4.0h + 2.0h;
- baseBRDFData.roughness2MinusOne = baseBRDFData.roughness2 - 1.0h;
- #endif
- // Darken/saturate base layer using coat to surface reflectance (vs. air to surface)
- baseBRDFData.specular = lerp(baseBRDFData.specular, ConvertF0ForClearCoat15(baseBRDFData.specular), clearCoatMask);
- // TODO: what about diffuse? at least in specular workflow diffuse should be recalculated as it directly depends on it.
- }
- BRDFData CreateClearCoatBRDFData(SurfaceData surfaceData, inout BRDFData brdfData)
- {
- BRDFData brdfDataClearCoat = (BRDFData)0;
- #if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
- // base brdfData is modified here, rely on the compiler to eliminate dead computation by InitializeBRDFData()
- InitializeBRDFDataClearCoat(surfaceData.clearCoatMask, surfaceData.clearCoatSmoothness, brdfData, brdfDataClearCoat);
- #endif
- return brdfDataClearCoat;
- }
- // Computes the specular term for EnvironmentBRDF
- half3 EnvironmentBRDFSpecular(BRDFData brdfData, half fresnelTerm)
- {
- float surfaceReduction = 1.0 / (brdfData.roughness2 + 1.0);
- return half3(surfaceReduction * lerp(brdfData.specular, brdfData.grazingTerm, fresnelTerm));
- }
- half3 EnvironmentBRDF(BRDFData brdfData, half3 indirectDiffuse, half3 indirectSpecular, half fresnelTerm)
- {
- half3 c = indirectDiffuse * brdfData.diffuse;
- c += indirectSpecular * EnvironmentBRDFSpecular(brdfData, fresnelTerm);
- return c;
- }
- // Environment BRDF without diffuse for clear coat
- half3 EnvironmentBRDFClearCoat(BRDFData brdfData, half clearCoatMask, half3 indirectSpecular, half fresnelTerm)
- {
- float surfaceReduction = 1.0 / (brdfData.roughness2 + 1.0);
- return indirectSpecular * EnvironmentBRDFSpecular(brdfData, fresnelTerm) * clearCoatMask;
- }
- // Computes the scalar specular term for Minimalist CookTorrance BRDF
- // NOTE: needs to be multiplied with reflectance f0, i.e. specular color to complete
- half DirectBRDFSpecular(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
- {
- float3 lightDirectionWSFloat3 = float3(lightDirectionWS);
- float3 halfDir = SafeNormalize(lightDirectionWSFloat3 + float3(viewDirectionWS));
- float NoH = saturate(dot(float3(normalWS), halfDir));
- half LoH = half(saturate(dot(lightDirectionWSFloat3, halfDir)));
- // GGX Distribution multiplied by combined approximation of Visibility and Fresnel
- // BRDFspec = (D * V * F) / 4.0
- // D = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2
- // V * F = 1.0 / ( LoH^2 * (roughness + 0.5) )
- // See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
- // https://community.arm.com/events/1155
- // Final BRDFspec = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2 * (LoH^2 * (roughness + 0.5) * 4.0)
- // We further optimize a few light invariant terms
- // brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.
- float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;
- half d2 = half(d * d);
- half LoH2 = LoH * LoH;
- half specularTerm = brdfData.roughness2 / (d2 * max(half(0.1), LoH2) * brdfData.normalizationTerm);
- // On platforms where half actually means something, the denominator has a risk of overflow
- // clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
- // sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
- #if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
- specularTerm = specularTerm - HALF_MIN;
- specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
- #endif
- return specularTerm;
- }
- // Based on Minimalist CookTorrance BRDF
- // Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
- //
- // * NDF [Modified] GGX
- // * Modified Kelemen and Szirmay-Kalos for Visibility term
- // * Fresnel approximated with 1/LdotH
- half3 DirectBDRF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS, bool specularHighlightsOff)
- {
- // Can still do compile-time optimisation.
- // If no compile-time optimized, extra overhead if branch taken is around +2.5% on some untethered platforms, -10% if not taken.
- [branch] if (!specularHighlightsOff)
- {
- half specularTerm = DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS);
- half3 color = brdfData.diffuse + specularTerm * brdfData.specular;
- return color;
- }
- else
- return brdfData.diffuse;
- }
- // Based on Minimalist CookTorrance BRDF
- // Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
- //
- // * NDF [Modified] GGX
- // * Modified Kelemen and Szirmay-Kalos for Visibility term
- // * Fresnel approximated with 1/LdotH
- half3 DirectBRDF(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
- {
- #ifndef _SPECULARHIGHLIGHTS_OFF
- return brdfData.diffuse + DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * brdfData.specular;
- #else
- return brdfData.diffuse;
- #endif
- }
- #endif
|