Files
UnrealEngineUWP/Engine/Shaders/Private/DeferredLightingCommon.ush
Ryan Vance 35eb0041ab Merging //UE4/Dev-Main to Dev-VR (//UE4/Dev-VR)
#rb integration

[CL 5387703 by Ryan Vance in Dev-VR branch]
2019-03-13 15:19:08 -04:00

419 lines
15 KiB
Plaintext

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
DeferredLightingCommon.usf: Common definitions for deferred lighting.
=============================================================================*/
#ifndef __DEFERRED_LIGHTING_COMMON__
#define __DEFERRED_LIGHTING_COMMON__
#include "DeferredShadingCommon.ush"
#include "DynamicLightingCommon.ush"
#include "IESLightProfilesCommon.ush"
#include "CapsuleLightIntegrate.ush"
#include "RectLightIntegrate.ush"
/**
* Data about a single light.
* Putting the light data in this struct allows the same lighting code to be used between standard deferred,
* Where many light properties are known at compile time, and tiled deferred, where all light properties have to be fetched from a buffer.
*/
// TODO: inherit or compose FLightShaderParameters
struct FDeferredLightData
{
float3 Position;
float InvRadius;
float3 Color;
float FalloffExponent;
float3 Direction;
float3 Tangent;
float SoftSourceRadius;
float2 SpotAngles;
float SourceRadius;
float SourceLength;
float SpecularScale;
float ContactShadowLength;
float2 DistanceFadeMAD;
float4 ShadowMapChannelMask;
/** Whether ContactShadowLength is in World Space or in Screen Space. */
bool ContactShadowLengthInWS;
/** Whether to use inverse squared falloff. */
bool bInverseSquared;
/** Whether this is a light with radial attenuation, aka point or spot light. */
bool bRadialLight;
/** Whether this light needs spotlight attenuation. */
bool bSpotLight;
bool bRectLight;
/** Whether the light should apply shadowing. */
uint ShadowedBits;
float RectLightBarnCosAngle;
float RectLightBarnLength;
};
/** Data about a single light to be shaded with the simple shading model, designed for speed and limited feature set. */
struct FSimpleDeferredLightData
{
float3 Position;
float InvRadius;
float3 Color;
float FalloffExponent;
/** Whether to use inverse squared falloff. */
bool bInverseSquared;
};
#define REFERENCE_QUALITY 0
/** Returns 0 for positions closer than the fade near distance from the camera, and 1 for positions further than the fade far distance. */
float DistanceFromCameraFade(float SceneDepth, FDeferredLightData LightData, float3 WorldPosition, float3 CameraPosition)
{
// depth (non radial) based fading over distance
float Fade = saturate(SceneDepth * LightData.DistanceFadeMAD.x + LightData.DistanceFadeMAD.y);
return Fade * Fade;
}
float ShadowRayCast(
float3 RayOriginTranslatedWorld, float3 RayDirection, float RayLength,
int NumSteps, float StepOffset
)
{
float4 RayStartClip = mul( float4( RayOriginTranslatedWorld, 1 ), View.TranslatedWorldToClip );
float4 RayDirClip = mul( float4( RayDirection * RayLength, 0 ), View.TranslatedWorldToClip );
float4 RayEndClip = RayStartClip + RayDirClip;
float3 RayStartScreen = RayStartClip.xyz / RayStartClip.w;
float3 RayEndScreen = RayEndClip.xyz / RayEndClip.w;
float3 RayStepScreen = RayEndScreen - RayStartScreen;
float3 RayStartUVz = float3( RayStartScreen.xy * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz, RayStartScreen.z );
float3 RayStepUVz = float3( RayStepScreen.xy * View.ScreenPositionScaleBias.xy, RayStepScreen.z );
float4 RayDepthClip = RayStartClip + mul( float4( 0, 0, RayLength, 0 ), View.ViewToClip );
float3 RayDepthScreen = RayDepthClip.xyz / RayDepthClip.w;
const float Step = 1.0 / NumSteps;
// *2 to get less moire pattern in extreme cases, larger values make object appear not grounded in reflections
const float CompareTolerance = abs( RayDepthScreen.z - RayStartScreen.z ) * Step * 2;
float SampleTime = StepOffset * Step + Step;
float FirstHitTime = -1.0;
UNROLL
for( int i = 0; i < NumSteps; i++ )
{
float3 SampleUVz = RayStartUVz + RayStepUVz * SampleTime;
float SampleDepth = SceneTexturesStruct.SceneDepthTexture.SampleLevel( SceneTexturesStruct.SceneDepthTextureSampler, SampleUVz.xy, 0 ).r;
float DepthDiff = SampleUVz.z - SampleDepth;
bool Hit = abs( DepthDiff + CompareTolerance ) < CompareTolerance;
FirstHitTime = (Hit && FirstHitTime < 0.0) ? SampleTime : FirstHitTime;
SampleTime += Step;
}
float Shadow = FirstHitTime > 0.0 ? 1.0 : 0.0;
// Off screen masking
float2 Vignette = max(6.0 * abs(RayStartScreen.xy + RayStepScreen.xy * FirstHitTime) - 5.0, 0.0);
Shadow *= saturate( 1.0 - dot( Vignette, Vignette ) );
return 1 - Shadow;
}
#ifndef SUPPORT_CONTACT_SHADOWS
#error "Must set SUPPORT_CONTACT_SHADOWS"
#endif
void GetShadowTerms(FGBufferData GBuffer, FDeferredLightData LightData, float3 WorldPosition, float3 L, float4 LightAttenuation, float Dither, inout FShadowTerms Shadow)
{
float ContactShadowLength = 0.0f;
const float ContactShadowLengthScreenScale = View.ClipToView[1][1] * GBuffer.Depth;
BRANCH
if (LightData.ShadowedBits)
{
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
// LightAttenuation: Light function + per-object shadows in z, per-object SSS shadowing in w,
// Whole scene directional light shadows in x, whole scene directional light SSS shadows in y
// Get static shadowing from the appropriate GBuffer channel
float UsesStaticShadowMap = dot(LightData.ShadowMapChannelMask, float4(1, 1, 1, 1));
float StaticShadowing = lerp(1, dot(GBuffer.PrecomputedShadowFactors, LightData.ShadowMapChannelMask), UsesStaticShadowMap);
if (LightData.bRadialLight)
{
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
Shadow.SurfaceShadow = LightAttenuation.z * StaticShadowing;
// SSS uses a separate shadowing term that allows light to penetrate the surface
//@todo - how to do static shadowing of SSS correctly?
Shadow.TransmissionShadow = LightAttenuation.w * StaticShadowing;
Shadow.TransmissionThickness = LightAttenuation.w;
}
else
{
// Remapping the light attenuation buffer (see ShadowRendering.cpp)
// Also fix up the fade between dynamic and static shadows
// to work with plane splits rather than spheres.
float DynamicShadowFraction = DistanceFromCameraFade(GBuffer.Depth, LightData, WorldPosition, View.WorldCameraOrigin);
// For a directional light, fade between static shadowing and the whole scene dynamic shadowing based on distance + per object shadows
Shadow.SurfaceShadow = lerp(LightAttenuation.x, StaticShadowing, DynamicShadowFraction);
// Fade between SSS dynamic shadowing and static shadowing based on distance
Shadow.TransmissionShadow = min(lerp(LightAttenuation.y, StaticShadowing, DynamicShadowFraction), LightAttenuation.w);
Shadow.SurfaceShadow *= LightAttenuation.z;
Shadow.TransmissionShadow *= LightAttenuation.z;
// Need this min or backscattering will leak when in shadow which cast by non perobject shadow(Only for directional light)
Shadow.TransmissionThickness = min(LightAttenuation.y, LightAttenuation.w);
}
FLATTEN
if (LightData.ShadowedBits > 1 && LightData.ContactShadowLength > 0)
{
ContactShadowLength = LightData.ContactShadowLength * (LightData.ContactShadowLengthInWS ? 1.0f : ContactShadowLengthScreenScale);
}
}
#if SUPPORT_CONTACT_SHADOWS
if ((LightData.ShadowedBits < 2 && (GBuffer.ShadingModelID == SHADINGMODELID_HAIR))
|| GBuffer.ShadingModelID == SHADINGMODELID_EYE)
{
ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
}
#if MATERIAL_CONTACT_SHADOWS
ContactShadowLength = 0.2 * ContactShadowLengthScreenScale;
#endif
BRANCH
if (ContactShadowLength > 0.0)
{
float StepOffset = Dither - 0.5;
float ContactShadow = ShadowRayCast( WorldPosition + View.PreViewTranslation, L, ContactShadowLength, 8, StepOffset );
Shadow.SurfaceShadow *= ContactShadow;
FLATTEN
if( GBuffer.ShadingModelID == SHADINGMODELID_HAIR )
Shadow.TransmissionShadow *= ContactShadow;
else if( GBuffer.ShadingModelID != SHADINGMODELID_EYE )
Shadow.TransmissionShadow *= ContactShadow * 0.5 + 0.5;
}
#endif
}
float GetLocalLightAttenuation(
float3 WorldPosition,
FDeferredLightData LightData,
inout float3 ToLight,
inout float3 L)
{
ToLight = LightData.Position - WorldPosition;
float DistanceSqr = dot( ToLight, ToLight );
L = ToLight * rsqrt( DistanceSqr );
float LightMask;
if (LightData.bInverseSquared)
{
LightMask = Square( saturate( 1 - Square( DistanceSqr * Square(LightData.InvRadius) ) ) );
}
else
{
LightMask = RadialAttenuation(ToLight * LightData.InvRadius, LightData.FalloffExponent);
}
if (LightData.bSpotLight)
{
LightMask *= SpotAttenuation(L, -LightData.Direction, LightData.SpotAngles);
}
if( LightData.bRectLight )
{
// Rect normal points away from point
LightMask = dot( LightData.Direction, L ) < 0 ? 0 : LightMask;
}
return LightMask;
}
#define RECLIGHT_BARNDOOR 1
// Wrapper for FDeferredLightData for computing visible rect light (i.e., unoccluded by barn doors)
FRect GetRect(float3 ToLight, FDeferredLightData LightData)
{
return GetRect(
ToLight,
LightData.Direction,
LightData.Tangent,
LightData.SourceRadius,
LightData.SourceLength,
LightData.RectLightBarnCosAngle,
LightData.RectLightBarnLength,
RECLIGHT_BARNDOOR);
}
FCapsuleLight GetCapsule( float3 ToLight, FDeferredLightData LightData )
{
FCapsuleLight Capsule;
Capsule.Length = LightData.SourceLength;
Capsule.Radius = LightData.SourceRadius;
Capsule.SoftRadius = LightData.SoftSourceRadius;
Capsule.DistBiasSqr = 1;
Capsule.LightPos[0] = ToLight - 0.5 * Capsule.Length * LightData.Tangent;
Capsule.LightPos[1] = ToLight + 0.5 * Capsule.Length * LightData.Tangent;
return Capsule;
}
/** Calculates lighting for a given position, normal, etc with a fully featured lighting model designed for quality. */
float4 GetDynamicLighting(float3 WorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID, FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos, FRectTexture SourceTexture)
{
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
float3 V = -CameraVector;
float3 N = GBuffer.WorldNormal;
BRANCH if( GBuffer.ShadingModelID == SHADINGMODELID_CLEAR_COAT && CLEAR_COAT_BOTTOM_NORMAL)
{
const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 2) - (256.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
N = OctahedronToUnitVector(oct1);
}
float3 L = LightData.Direction; // Already normalized
float3 ToLight = L;
float LightMask = 1;
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation( WorldPosition, LightData, ToLight, L );
}
LightAccumulator.EstimatedCost += 0.3f; // running the PixelShader at all has a cost
BRANCH
if( LightMask > 0 )
{
FShadowTerms Shadow;
Shadow.SurfaceShadow = AmbientOcclusion;
Shadow.TransmissionShadow = 1;
Shadow.TransmissionThickness = 1;
GetShadowTerms(GBuffer, LightData, WorldPosition, L, LightAttenuation, Dither, Shadow);
LightAccumulator.EstimatedCost += 0.3f; // add the cost of getting the shadow terms
BRANCH
if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 )
{
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
float3 LightColor = LightData.Color;
#if NON_DIRECTIONAL_DIRECT_LIGHTING
float Lighting;
if( LightData.bRectLight )
{
FRect Rect = GetRect( ToLight, LightData );
Lighting = IntegrateLight( Rect, SourceTexture);
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
Lighting = IntegrateLight( Capsule, LightData.bInverseSquared );
}
float3 LightingDiffuse = Diffuse_Lambert( GBuffer.DiffuseColor ) * Lighting;
LightAccumulator_Add(LightAccumulator, LightingDiffuse, 0, LightColor * LightMask * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation);
#else
FDirectLighting Lighting;
if (LightData.bRectLight)
{
FRect Rect = GetRect( ToLight, LightData );
#if REFERENCE_QUALITY
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture, SVPos );
#else
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture);
#endif
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
#if REFERENCE_QUALITY
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, SVPos );
#else
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
#endif
}
Lighting.Specular *= LightData.SpecularScale;
LightAccumulator_Add( LightAccumulator, Lighting.Diffuse + Lighting.Specular, Lighting.Diffuse, LightColor * LightMask * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator_Add( LightAccumulator, Lighting.Transmission, Lighting.Transmission, LightColor * LightMask * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator.EstimatedCost += 0.4f; // add the cost of the lighting computations (should sum up to 1 form one light)
#endif
}
}
return LightAccumulator_GetResult(LightAccumulator);
}
/**
* Calculates lighting for a given position, normal, etc with a simple lighting model designed for speed.
* All lights rendered through this method are unshadowed point lights with no shadowing or light function or IES.
* A cheap specular is used instead of the more correct area specular, no fresnel.
*/
float3 GetSimpleDynamicLighting(float3 WorldPosition, float3 CameraVector, float3 WorldNormal, float AmbientOcclusion, float3 DiffuseColor, float3 SpecularColor, float Roughness, FSimpleDeferredLightData LightData)
{
float3 V = -CameraVector;
float3 N = WorldNormal;
float3 ToLight = LightData.Position - WorldPosition;
float DistanceAttenuation = 1;
float DistanceSqr = dot( ToLight, ToLight );
float3 L = ToLight * rsqrt( DistanceSqr );
float NoL = saturate( dot( N, L ) );
if (LightData.bInverseSquared)
{
// Sphere falloff (technically just 1/d2 but this avoids inf)
DistanceAttenuation = 1 / ( DistanceSqr + 1 );
float LightRadiusMask = Square( saturate( 1 - Square( DistanceSqr * Square(LightData.InvRadius) ) ) );
DistanceAttenuation *= LightRadiusMask;
}
else
{
DistanceAttenuation = RadialAttenuation(ToLight * LightData.InvRadius, LightData.FalloffExponent);
}
float3 OutLighting = 0;
BRANCH
if (DistanceAttenuation > 0)
{
const float3 LightColor = LightData.Color;
// Apply SSAO to the direct lighting since we're not going to have any other shadowing
float Attenuation = DistanceAttenuation * AmbientOcclusion;
#if NON_DIRECTIONAL_DIRECT_LIGHTING
float3 VolumeLighting = Diffuse_Lambert(DiffuseColor);
OutLighting += LightColor * Attenuation * VolumeLighting;
#else
OutLighting += LightColor * (NoL * Attenuation) * SimpleShading(DiffuseColor, SpecularColor, max(Roughness, .04f), L, V, N);
#endif
}
return OutLighting;
}
#endif