#ifndef LIGHTWEIGHT_LIGHTING_ADVANCED_INCLUDED #define LIGHTWEIGHT_LIGHTING_ADVANCED_INCLUDED //#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" //#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl" //#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" /////////////////////////////////////////////////////////////////////////////// // BRDF Functions // /////////////////////////////////////////////////////////////////////////////// #ifdef _CLEARCOAT #define CLEAR_COAT_IOR 1.5h #define CLEAR_COAT_IETA (1.0h / CLEAR_COAT_IOR) // IETA is the inverse eta which is the ratio of IOR of two interface #endif half3 f0ClearCoatToSurface(half3 f0) { // Approximation of iorTof0(f0ToIor(f0), 1.5) // This assumes that the clear coat layer has an IOR of 1.5 #if defined(SHADER_API_MOBILE) return saturate(f0 * (f0 * 0.526868h + 0.529324h) - 0.0482256h); #else return saturate(f0 * (f0 * (0.941892h - 0.263008h * f0) + 0.346479h) - 0.0285998h); #endif } struct BRDFDataAdvanced { half3 diffuse; half3 specular; 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² - 1.0 #ifdef _CLEARCOAT half clearCoat; half perceptualClearCoatRoughness; half clearCoatRoughness; half clearCoatRoughness2; half clearCoatRoughness2MinusOne; #endif }; inline void InitializeBRDFDataAdvanced(SurfaceDataAdvanced surfaceData, out BRDFDataAdvanced outBRDFData) { #ifdef _SPECULAR_SETUP half reflectivity = ReflectivitySpecular(surfaceData.specular); half oneMinusReflectivity = 1.0 - reflectivity; outBRDFData.diffuse = surfaceData.albedo * (half3(1.0h, 1.0h, 1.0h) - surfaceData.specular); half3 f0 = surfaceData.specular; #else half oneMinusReflectivity = OneMinusReflectivityMetallic(surfaceData.metallic); half reflectivity = 1.0 - oneMinusReflectivity; outBRDFData.diffuse = surfaceData.albedo * oneMinusReflectivity; half3 f0 = kDieletricSpec.rgb; #endif outBRDFData.grazingTerm = saturate(surfaceData.smoothness + reflectivity); outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surfaceData.smoothness); outBRDFData.roughness = PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness); outBRDFData.roughness2 = outBRDFData.roughness * outBRDFData.roughness; #ifdef _CLEARCOAT // Calculate Roughness of Clear Coat layer outBRDFData.clearCoat = surfaceData.clearCoat; outBRDFData.perceptualClearCoatRoughness = PerceptualSmoothnessToPerceptualRoughness(surfaceData.clearCoatSmoothness); outBRDFData.clearCoatRoughness = PerceptualRoughnessToRoughness(outBRDFData.perceptualClearCoatRoughness); outBRDFData.clearCoatRoughness2 = outBRDFData.clearCoatRoughness * outBRDFData.clearCoatRoughness; outBRDFData.clearCoatRoughness2MinusOne = outBRDFData.clearCoatRoughness2 - 1.0h; // Modify Roughness of base layer half ieta = lerp(1.0h, CLEAR_COAT_IETA, outBRDFData.clearCoat); half coatRoughnessScale = Sq(ieta); half sigma = RoughnessToVariance(PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness)); outBRDFData.perceptualRoughness = RoughnessToPerceptualRoughness(VarianceToRoughness(sigma * coatRoughnessScale)); f0 = lerp(f0, f0ClearCoatToSurface(f0), outBRDFData.clearCoat); #endif #ifdef _SPECULAR_SETUP outBRDFData.specular = f0; #else outBRDFData.specular = lerp(f0, surfaceData.albedo, surfaceData.metallic); #endif outBRDFData.normalizationTerm = outBRDFData.roughness * 4.0h + 2.0h; outBRDFData.roughness2MinusOne = outBRDFData.roughness2 - 1.0h; #ifdef _ALPHAPREMULTIPLY_ON outBRDFData.diffuse *= surfaceData.alpha; surfaceData.alpha = surfaceData.alpha * oneMinusReflectivity + reflectivity; #endif } // Kelemen 2001, "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling" // TODO - Move to Core or switch Visibility term? real V_Kelemen(real LoH) { real x = 0.25 / (LoH * LoH); #if defined (SHADER_API_MOBILE) return min(x, 65504.0); #else return x; #endif } #ifdef _CLEARCOAT half ClearCoat(BRDFDataAdvanced brdfData, half3 halfDir, half NoH, half LoH, half LoH2) { half D = NoH * NoH * brdfData.clearCoatRoughness2MinusOne + 1.00001h; half specularTerm = brdfData.clearCoatRoughness2 / ((D * D) * max(0.1h, LoH2) * (brdfData.clearCoatRoughness * 4.0 + 2.0)) * brdfData.clearCoat; half attenuation = 1 - LoH * brdfData.clearCoat; #if defined (SHADER_API_MOBILE) specularTerm = specularTerm - HALF_MIN; specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles #endif return specularTerm * attenuation; } #endif // 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 DirectBDRFAdvanced(BRDFDataAdvanced brdfData, InputDataAdvanced inputData, half3 lightDirectionWS) { #ifndef _SPECULARHIGHLIGHTS_OFF half3 halfDir = SafeNormalize(lightDirectionWS + inputData.viewDirectionWS); half NoH = saturate(dot(inputData.normalWS, halfDir)); half LoH = saturate(dot(lightDirectionWS, halfDir)); // GGX Distribution multiplied by combined approximation of Visibility and Fresnel // BRDFspec = (D * V * F) / 4.0 // D = roughness² / ( NoH² * (roughness² - 1) + 1 )² // V * F = 1.0 / ( LoH² * (roughness + 0.5) ) // See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course // https://community.arm.com/events/1155 // Final BRDFspec = roughness² / ( NoH² * (roughness² - 1) + 1 )² * (LoH² * (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. half d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001h; half LoH2 = LoH * LoH; half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * brdfData.normalizationTerm); half3 diffuseTerm = brdfData.diffuse; // on mobiles (where half actually means something) denominator have 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) specularTerm = specularTerm - HALF_MIN; specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles #endif half3 color = specularTerm * brdfData.specular + diffuseTerm; #ifdef _CLEARCOAT color += ClearCoat(brdfData, halfDir, NoH, LoH, LoH2); #endif return color; #else return brdfData.diffuse; #endif } /////////////////////////////////////////////////////////////////////////////// // Global Illumination // /////////////////////////////////////////////////////////////////////////////// #ifdef _CLEARCOAT void GlobalIlluminationClearCoat(BRDFDataAdvanced brdfData, half3 reflectVector, half fresnelTerm, half occlusion, inout half3 indirectDiffuse, inout half3 indirectSpecular) { fresnelTerm *= brdfData.clearCoat; float attenuation = 1 - fresnelTerm; indirectDiffuse *= attenuation; indirectSpecular *= attenuation * attenuation; indirectSpecular += GlossyEnvironmentReflection(reflectVector, brdfData.perceptualClearCoatRoughness, occlusion) * fresnelTerm; } #endif half3 GlobalIlluminationAdvanced(BRDFDataAdvanced brdfData, InputDataAdvanced inputData, half occlusion) { half3 reflectVector = reflect(-inputData.viewDirectionWS, inputData.normalWS); half fresnelTerm = Pow4(1.0 - saturate(dot(inputData.normalWS, inputData.viewDirectionWS))); half3 indirectDiffuse = inputData.bakedGI * occlusion * brdfData.diffuse; half3 reflection = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion); float surfaceReduction = 1.0 / (brdfData.roughness2 + 1.0); half3 indirectSpecular = surfaceReduction * reflection * lerp(brdfData.specular, brdfData.grazingTerm, fresnelTerm); #ifdef _CLEARCOAT GlobalIlluminationClearCoat(brdfData, reflectVector, fresnelTerm, occlusion, indirectDiffuse, indirectSpecular); #endif return indirectDiffuse + indirectSpecular; } /////////////////////////////////////////////////////////////////////////////// // Lighting Functions // /////////////////////////////////////////////////////////////////////////////// half3 LightingAdvanced(BRDFDataAdvanced brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, InputDataAdvanced inputData) { half NdotL = saturate(dot(inputData.normalWS, lightDirectionWS)); half3 radiance = lightColor * (lightAttenuation * NdotL); return DirectBDRFAdvanced(brdfData, inputData, lightDirectionWS) * radiance; } half3 LightingAdvanced(BRDFDataAdvanced brdfData, Light light, InputDataAdvanced inputData) { return LightingAdvanced(brdfData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, inputData); } /////////////////////////////////////////////////////////////////////////////// // Fragment Functions // // Used by ShaderGraph and others builtin renderers // /////////////////////////////////////////////////////////////////////////////// half4 LightweightFragmentAdvanced(InputDataAdvanced inputData, SurfaceDataAdvanced surfaceData) { BRDFDataAdvanced brdfData; InitializeBRDFDataAdvanced(surfaceData, brdfData); Light mainLight = GetMainLight(inputData.shadowCoord); MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0)); half3 color = GlobalIlluminationAdvanced(brdfData, inputData, surfaceData.occlusion); color += LightingAdvanced(brdfData, mainLight, inputData); #ifdef _ADDITIONAL_LIGHTS int pixelLightCount = GetAdditionalLightsCount(); for (int i = 0; i < pixelLightCount; ++i) { Light light = GetAdditionalLight(i, inputData.positionWS); color += LightingAdvanced(brdfData, light, inputData); } #endif #ifdef _ADDITIONAL_LIGHTS_VERTEX color += inputData.vertexLighting * brdfData.diffuse; #endif color += surfaceData.emission; return half4(color, surfaceData.alpha); } #endif