// Copyright Epic Games, Inc. All Rights Reserved. #pragma once uint UseShortRangeAO; float IrradianceBounce0Rescale(float AO) { return AO * (1 + (1 - AO) / (2 * sqrt(sqrt(1 - AO)))); } float IrradianceBounce1Rescale(float AO) { float A = 27.576937094210385f; float B = 3.3364392003423804f; return A * AO * pow(1 - AO, 1.5f) * exp(-B * sqrt(sqrt(AO))); } // From "2018 Patapom - Improved Ambient Occlusion" float3 DistantIlluminationRescale(float3 DiffuseColor, float AO) { float F0 = IrradianceBounce0Rescale(min(AO, .999f)); float F1 = IrradianceBounce1Rescale(min(AO, .999f)); float Tau = 1 - F1 / max(1 - F0, 0.00001f); return F0 + DiffuseColor / max(1 - DiffuseColor * Tau, 0.00001f) * F1; } float ApproximateConeConeIntersection(float ArcLength0, float ArcLength1, float AngleBetweenCones) { float AngleDifference = abs(ArcLength0 - ArcLength1); float Intersection = smoothstep( 0, 1.0, 1.0 - saturate((AngleBetweenCones - AngleDifference) / (ArcLength0 + ArcLength1 - AngleDifference))); return Intersection; } float CalculateSpecularOcclusion(float3 N, float Roughness, float AO, float3 V, float3 BentNormalOcclusion) { float ReflectionConeAngle = max(Roughness, .1f) * PI; float UnoccludedAngle = AO * PI; float3 ReflectionVector = reflect(-V, N); float AngleBetween = acosFast(clamp(dot(BentNormalOcclusion, ReflectionVector) / max(AO, .001f), -1, 1)); float SpecularOcclusion = ApproximateConeConeIntersection(ReflectionConeAngle, UnoccludedAngle, AngleBetween); // Can't rely on the direction of the bent normal when close to fully occluded, lerp to shadowed SpecularOcclusion = lerp(0, SpecularOcclusion, saturate((UnoccludedAngle - .1f) / .2f)); return SpecularOcclusion; } uint PackScreenBentNormal(float3 BentNormal) { return PackRGB111110(BentNormal * 0.5f + 0.5f); } float3 UnpackScreenBentNormal(uint PackedBentNormal) { return UnpackRGB111110(PackedBentNormal) * 2.0f - 1.0f; }