IES灯光计算方法:
void UpdateLightDataColor(inout FDeferredLightData LightData, FInputParams InputParams, FDerivedParams DerivedParams)
{
const float Attenuation = ComputeLightProfileMultiplier(DerivedParams.TranslatedWorldPosition, GetDeferredLightTranslatedWorldPosition(), -DeferredLightUniforms_Direction, DeferredLightUniforms_Tangent);
const float3 AttenuationRGB = Attenuation * GetAtmosphereAndCloudAttenuation(GetStableTranslatedLargeWorldPosition(InputParams.PixelPos, LookupDeviceZ(InputParams.ScreenUV)), InputParams.ScreenUV);
LightData.Color *= AttenuationRGB;
}
Texture2D IESTexture;
SamplerState IESTextureSampler;
float ComputeLightProfileMultiplier(float3 WorldPosition, float3 LightPosition, float3 LightDirection, float3 LightTangent)
{
float3 LightBitangent = normalize( cross( LightTangent, LightDirection ) );
float4x4 LightTransform = float4x4( float4(LightDirection.xyz, 0), float4(LightBitangent.xyz, 0), float4(LightTangent.xyz, 0), float4(0, 0, 0, 1) );
float4x4 InvLightTransform = transpose(LightTransform);
float3 ToLight = normalize(LightPosition - WorldPosition);
float3 LocalToLight = mul(float4(ToLight.xyz, 0), InvLightTransform).xyz;
float DotProd = dot(ToLight, LightDirection);
float Angle = asin(DotProd);
float NormAngle = Angle / PI + 0.5f;
float TangentAngle = atan2( -LocalToLight.z, -LocalToLight.y );
float NormTangentAngle = TangentAngle / (PI * 2.f) + 0.5f;
return Texture2DSampleLevel(IESTexture, IESTextureSampler, float2(NormAngle, NormTangentAngle), 0).r;
}
灯光衰减算法:
float GetLocalLightAttenuation(
float3 TranslatedWorldPosition,
FDeferredLightData LightData,
inout float3 ToLight,
inout float3 L)
{
ToLight = LightData.TranslatedWorldPosition - TranslatedWorldPosition;
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 )
{
LightMask = dot( LightData.Direction, L ) < 0 ? 0 : LightMask;
}
return LightMask;
}
float SpotAttenuationMask(float3 L, float3 SpotDirection, float2 SpotAngles)
{
return saturate((dot(L, -SpotDirection) - SpotAngles.x) * SpotAngles.y);
}
float SpotAttenuation(float3 L, float3 SpotDirection, float2 SpotAngles)
{
float ConeAngleFalloff = Square(SpotAttenuationMask(L, SpotDirection, SpotAngles));
return ConeAngleFalloff;
}
float CalcSceneDepth(float2 ScreenUV)
{
return ConvertFromDeviceZ(Texture2DSampleLevel(SceneTexturesStruct_SceneDepthTexture, SceneTexturesStruct_PointClampSampler , ScreenUV, 0).r);
}
float ConvertFromDeviceZ(float DeviceZ)
{
return DeviceZ * View_InvDeviceZToWorldZTransform[0] + View_InvDeviceZToWorldZTransform[1] + 1.0f / (DeviceZ * View_InvDeviceZToWorldZTransform[2] - View_InvDeviceZToWorldZTransform[3]);
}
FDerivedParams GetDerivedParams(in FInputParams Input, in float SceneDepth)
{
FDerivedParams Out;
float2 ClipPosition = Input.ScreenPosition.xy / Input.ScreenPosition.w * (View_ViewToClip[3][3] < 1.0f ? SceneDepth : 1.0f);
Out.TranslatedWorldPosition = mul(float4(ClipPosition, SceneDepth, 1), View_ScreenToTranslatedWorld).xyz;
Out.CameraVector = normalize(Out.TranslatedWorldPosition - View_TranslatedWorldCameraOrigin);
return Out;
}
void DeferredLightPixelMain(
float4 InScreenPosition : TEXCOORD0,
float4 SVPos : SV_POSITION,
out float4 OutColor : SV_Target0
)
{
FInputParams InputParams = (FInputParams)0;
InputParams.PixelPos = SVPos.xy;
InputParams.ScreenPosition = InScreenPosition;
InputParams.ScreenUV = InScreenPosition.xy / InScreenPosition.w *View_ScreenPositionScaleBias.xy + View_ScreenPositionScaleBias.wz;
InputParams.ScreenVector = 0;
FScreenSpaceData ScreenSpaceData = GetScreenSpaceData(InputParams.ScreenUV);
[branch] if (ScreenSpaceData.GBuffer.ShadingModelID > 0)
{
const float SceneDepth = CalcSceneDepth(InputParams.ScreenUV);
const FDerivedParams DerivedParams = GetDerivedParams(InputParams, SceneDepth);
FDeferredLightData LightData = SetupLightDataForStandardDeferred();
UpdateLightDataColor(LightData, InputParams, DerivedParams);
float Dither = InterleavedGradientNoise(InputParams.PixelPos, View_StateFrameIndexMod8);
float SurfaceShadow = 1.0f;
float4 LightAttenuation = GetLightAttenuationFromShadow(InputParams, SceneDepth);
float4 Radiance = GetDynamicLighting(DerivedParams.TranslatedWorldPosition, DerivedParams.CameraVector, ScreenSpaceData.GBuffer, ScreenSpaceData.AmbientOcclusion, ScreenSpaceData.GBuffer.ShadingModelID, LightData, LightAttenuation, Dither, uint2(InputParams.PixelPos), SurfaceShadow);
OutColor += Radiance;
}
OutColor.rgba *= GetExposure();
}
FScreenSpaceData GetScreenSpaceData(float2 UV, bool bGetNormalizedNormal = true)
{
FScreenSpaceData Out;
Out.GBuffer = GetGBufferData(UV, bGetNormalizedNormal);
float4 ScreenSpaceAO = Texture2DSampleLevel(SceneTexturesStruct_ScreenSpaceAOTexture, SceneTexturesStruct_PointClampSampler , UV, 0);
Out.AmbientOcclusion = ScreenSpaceAO.r;
return Out;
}
FGBufferData GetGBufferData(float2 UV, bool bGetNormalizedNormal = true)
{
return DecodeGBufferDataUV(UV,bGetNormalizedNormal);
}
FGBufferData DecodeGBufferDataUV(float2 UV, bool bGetNormalizedNormal = true)
{
float CustomNativeDepth = Texture2DSampleLevel(SceneTexturesStruct_CustomDepthTexture, SceneTexturesStruct_PointClampSampler , UV, 0).r;
int2 IntUV = (int2)trunc(UV * View_BufferSizeAndInvSize.xy * View_BufferToSceneTextureScale.xy);
uint CustomStencil = SceneTexturesStruct_CustomStencilTexture.Load(int3(IntUV, 0)) .g ;
float SceneDepth = CalcSceneDepth(UV);
float4 AnisotropicData = Texture2DSampleLevel(SceneTexturesStruct_GBufferFTexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
float4 InMRT1 = Texture2DSampleLevel(SceneTexturesStruct_GBufferATexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
float4 InMRT2 = Texture2DSampleLevel(SceneTexturesStruct_GBufferBTexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
float4 InMRT3 = Texture2DSampleLevel(SceneTexturesStruct_GBufferCTexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
float4 InMRT4 = Texture2DSampleLevel(SceneTexturesStruct_GBufferVelocityTexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
float4 InMRT5 = Texture2DSampleLevel(SceneTexturesStruct_GBufferDTexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
float4 InMRT6 = Texture2DSampleLevel(SceneTexturesStruct_GBufferETexture, SceneTexturesStruct_PointClampSampler , UV, 0).xyzw;
FGBufferData Ret = DecodeGBufferDataDirect(InMRT1,
InMRT2,
InMRT3,
InMRT4,
InMRT5,
InMRT6,
CustomNativeDepth,
AnisotropicData,
CustomStencil,
SceneDepth,
bGetNormalizedNormal,
CheckerFromSceneColorUV(UV));
return Ret;
}
FDeferredLightData SetupLightDataForStandardDeferred()
{
FDeferredLightData LightData;
LightData.TranslatedWorldPosition = GetDeferredLightTranslatedWorldPosition();
LightData.InvRadius = DeferredLightUniforms_InvRadius;
LightData.Color = DeferredLightUniforms_Color;
LightData.FalloffExponent = DeferredLightUniforms_FalloffExponent;
LightData.Direction = DeferredLightUniforms_Direction;
LightData.Tangent = DeferredLightUniforms_Tangent;
LightData.SpotAngles = DeferredLightUniforms_SpotAngles;
LightData.SourceRadius = DeferredLightUniforms_SourceRadius;
LightData.SourceLength = 1 > 0 ? DeferredLightUniforms_SourceLength : 0;
LightData.SoftSourceRadius = DeferredLightUniforms_SoftSourceRadius;
LightData.SpecularScale = DeferredLightUniforms_SpecularScale;
LightData.ContactShadowLength = abs(DeferredLightUniforms_ContactShadowLength);
LightData.ContactShadowLengthInWS = DeferredLightUniforms_ContactShadowLength < 0.0f;
LightData.ContactShadowNonShadowCastingIntensity = DeferredLightUniforms_ContactShadowNonShadowCastingIntensity;
LightData.DistanceFadeMAD = DeferredLightUniforms_DistanceFadeMAD;
LightData.ShadowMapChannelMask = DeferredLightUniforms_ShadowMapChannelMask;
LightData.ShadowedBits = DeferredLightUniforms_ShadowedBits;
LightData.bInverseSquared = 1 > 0 && DeferredLightUniforms_FalloffExponent == 0;
LightData.bRadialLight = 1 > 0;
LightData.bSpotLight = 1 > 0;
LightData.bRectLight = 1 == 2;
LightData.RectLightBarnCosAngle = DeferredLightUniforms_RectLightBarnCosAngle;
LightData.RectLightBarnLength = DeferredLightUniforms_RectLightBarnLength;
LightData.RectLightAtlasMaxLevel = DeferredLightUniforms_RectLightAtlasMaxLevel;
LightData.RectLightAtlasUVOffset = DeferredLightUniforms_RectLightAtlasUVOffset;
LightData.RectLightAtlasUVScale = DeferredLightUniforms_RectLightAtlasUVScale;
LightData.HairTransmittance = InitHairTransmittanceData();
return LightData;
}
FGBufferData DecodeGBufferDataDirect(float4 InMRT1,
float4 InMRT2,
float4 InMRT3,
float4 InMRT4,
float4 InMRT5,
float4 InMRT6,
float CustomNativeDepth,
float4 AnisotropicData,
uint CustomStencil,
float SceneDepth,
bool bGetNormalizedNormal,
bool bChecker)
{
FGBufferData Ret = (FGBufferData)0;
float3 WorldNormal_Compressed = 0.0f;
WorldNormal_Compressed.x = InMRT1.x;
WorldNormal_Compressed.y = InMRT1.y;
WorldNormal_Compressed.z = InMRT1.z;
Ret.PerObjectGBufferData.x = InMRT1.w;
Ret.Metallic.x = InMRT2.x;
Ret.Specular.x = InMRT2.y;
Ret.Roughness.x = InMRT2.z;
Ret.ShadingModelID.x = (((uint((float(InMRT2.w) * 255.0f) + .5f) >> 0) & 0x0f) << 0);
Ret.SelectiveOutputMask.x = (((uint((float(InMRT2.w) * 255.0f) + .5f) >> 4) & 0x0f) << 0);
Ret.BaseColor.x = InMRT3.x;
Ret.BaseColor.y = InMRT3.y;
Ret.BaseColor.z = InMRT3.z;
Ret.GenericAO.x = InMRT3.w;
Ret.Velocity.x = InMRT4.x;
Ret.Velocity.y = InMRT4.y;
Ret.Velocity.z = InMRT4.z;
Ret.Velocity.w = InMRT4.w;
Ret.PrecomputedShadowFactors.x = InMRT6.x;
Ret.PrecomputedShadowFactors.y = InMRT6.y;
Ret.PrecomputedShadowFactors.z = InMRT6.z;
Ret.PrecomputedShadowFactors.w = InMRT6.w;
Ret.CustomData.x = InMRT5.x;
Ret.CustomData.y = InMRT5.y;
Ret.CustomData.z = InMRT5.z;
Ret.CustomData.w = InMRT5.w;
Ret.WorldNormal = DecodeNormalHelper(WorldNormal_Compressed);
Ret.WorldTangent = AnisotropicData.xyz;
Ret.Anisotropy = AnisotropicData.w;
GBufferPostDecode(Ret,bChecker,bGetNormalizedNormal);
Ret.CustomDepth = ConvertFromDeviceZ(CustomNativeDepth);
Ret.CustomStencil = CustomStencil;
Ret.Depth = SceneDepth;
return Ret;
}
FLightAccumulator AccumulateDynamicLighting(
float3 TranslatedWorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID,
FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos,
inout float SurfaceShadow)
{
FLightAccumulator LightAccumulator = (FLightAccumulator)0;
float3 V = -CameraVector;
float3 N = GBuffer.WorldNormal;
[branch] if( GBuffer.ShadingModelID == 4 && 0 )
{
const float2 oct1 = ((float2(GBuffer.CustomData.a, GBuffer.CustomData.z) * 4) - (512.0/255.0)) + UnitVectorToOctahedron(GBuffer.WorldNormal);
N = OctahedronToUnitVector(oct1);
}
float3 L = LightData.Direction;
float3 ToLight = L;
float3 MaskedLightColor = LightData.Color;
float LightMask = 1;
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation( TranslatedWorldPosition, LightData, ToLight, L );
MaskedLightColor *= LightMask;
}
LightAccumulator.EstimatedCost += 0.3f;
[branch]
if( LightMask > 0 )
{
FShadowTerms Shadow;
Shadow.SurfaceShadow = AmbientOcclusion;
Shadow.TransmissionShadow = 1;
Shadow.TransmissionThickness = 1;
Shadow.HairTransmittance.OpaqueVisibility = 1;
const float ContactShadowOpacity = GBuffer.CustomData.a;
GetShadowTerms(GBuffer.Depth, GBuffer.PrecomputedShadowFactors, GBuffer.ShadingModelID, ContactShadowOpacity,
LightData, TranslatedWorldPosition, L, LightAttenuation, Dither, Shadow);
SurfaceShadow = Shadow.SurfaceShadow;
LightAccumulator.EstimatedCost += 0.3f;
[branch]
if( Shadow.SurfaceShadow + Shadow.TransmissionShadow > 0 )
{
const bool bNeedsSeparateSubsurfaceLightAccumulation = UseSubsurfaceProfile(GBuffer.ShadingModelID);
FDirectLighting Lighting;
if (LightData.bRectLight)
{
FRect Rect = GetRect( ToLight, LightData );
const FRectTexture SourceTexture = InitRectTexture(LightData);
Lighting = IntegrateBxDF( GBuffer, N, V, Rect, Shadow, SourceTexture);
}
else
{
FCapsuleLight Capsule = GetCapsule( ToLight, LightData );
Lighting = IntegrateBxDF( GBuffer, N, V, Capsule, Shadow, LightData.bInverseSquared );
}
Lighting.Specular *= LightData.SpecularScale;
LightAccumulator_AddSplit( LightAccumulator, Lighting.Diffuse, Lighting.Specular, Lighting.Diffuse, MaskedLightColor * Shadow.SurfaceShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator_AddSplit( LightAccumulator, Lighting.Transmission, 0.0f, Lighting.Transmission, MaskedLightColor * Shadow.TransmissionShadow, bNeedsSeparateSubsurfaceLightAccumulation );
LightAccumulator.EstimatedCost += 0.4f;
}
}
return LightAccumulator;
}
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;
}
FDirectLighting IntegrateBxDF( FGBufferData GBuffer, float3 N, float3 V, FCapsuleLight Capsule, FShadowTerms Shadow, bool bInverseSquared )
{
GBuffer.Roughness = max( GBuffer.Roughness, View_MinRoughness );
FAreaLightIntegrateContext Context = CreateCapsuleIntegrateContext(GBuffer.Roughness, N, V, Capsule, bInverseSquared);
return IntegrateBxDF( GBuffer, N, V, Context.L, Context.Falloff, Context.NoL, Context.AreaLight, Shadow );
}
FAreaLightIntegrateContext CreateCapsuleIntegrateContext(float Roughness, float3 N, float3 V, FCapsuleLight Capsule, bool bInverseSquared )
{
FAreaLightIntegrateContext Out = InitAreaLightIntegrateContext();
float NoL;
float Falloff;
float LineCosSubtended = 1;
[branch]
if( Capsule.Length > 0 )
{
LineIrradiance( N, Capsule.LightPos[0], Capsule.LightPos[1], Capsule.DistBiasSqr, LineCosSubtended, Falloff, NoL );
}
else
{
float DistSqr = dot( Capsule.LightPos[0], Capsule.LightPos[0] );
Falloff = rcp( DistSqr + Capsule.DistBiasSqr );
float3 L = Capsule.LightPos[0] * rsqrt( DistSqr );
NoL = dot( N, L );
}
if( Capsule.Radius > 0 )
{
float SinAlphaSqr = saturate( Pow2( Capsule.Radius ) * Falloff );
NoL = SphereHorizonCosWrap( NoL, SinAlphaSqr );
}
NoL = saturate( NoL );
Falloff = bInverseSquared ? Falloff : 1;
float3 ToLight = Capsule.LightPos[0];
if( Capsule.Length > 0 )
{
float3 R = reflect( -V, N );
ToLight = ClosestPointLineToRay( Capsule.LightPos[0], Capsule.LightPos[1], Capsule.Length, R );
}
float DistSqr = dot( ToLight, ToLight );
float InvDist = rsqrt( DistSqr );
float3 L = ToLight * InvDist;
Roughness = max( Roughness, View_MinRoughness );
float a = Pow2( Roughness );
const float SizeFadesOutDiffuseMicroRefl = 20.0;
Out.AreaLight.SphereSinAlpha = saturate( Capsule.Radius * InvDist * (1 - a) );
Out.AreaLight.SphereSinAlphaSoft = saturate( Capsule.SoftRadius * InvDist );
Out.AreaLight.LineCosSubtended = LineCosSubtended;
Out.AreaLight.FalloffColor = 1;
Out.AreaLight.Rect = (FRect)0;
Out.AreaLight.Texture = InitRectTexture();
Out.AreaLight.IsRectAndDiffuseMicroReflWeight = 0;
SetIsRectLight(Out.AreaLight, false);
SetAreaLightDiffuseMicroReflWeight(Out.AreaLight, saturate(1.0f - max(Capsule.Length, Capsule.Radius) / SizeFadesOutDiffuseMicroRefl));
Out.NoL = NoL;
Out.Falloff = Falloff;
Out.L = L;
return Out;
}
FDirectLighting IntegrateBxDF( FGBufferData GBuffer, float3 N, float3 V, float3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow )
{
switch( GBuffer.ShadingModelID )
{
case 1 :
case 10 :
case 11 :
return DefaultLitBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 2 :
return SubsurfaceBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 3 :
return PreintegratedSkinBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 4 :
return ClearCoatBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 5 :
return SubsurfaceProfileBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 6 :
return TwoSidedBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 7 :
return HairBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 8 :
return ClothBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
case 9 :
return EyeBxDF( GBuffer, N, V, L, Falloff, NoL, AreaLight, Shadow );
default:
return (FDirectLighting)0;
}
}
FDirectLighting DefaultLitBxDF( FGBufferData GBuffer, float3 N, float3 V, float3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow )
{
BxDFContext Context;
FDirectLighting Lighting;
bool bHasAnisotropy = false;
float NoV, VoH, NoH;
[branch]
if (bHasAnisotropy)
{
float3 X = GBuffer.WorldTangent;
float3 Y = normalize(cross(N, X));
Init(Context, N, X, Y, V, L);
NoV = Context.NoV;
VoH = Context.VoH;
NoH = Context.NoH;
}
else
{
Init(Context, N, V, L);
NoV = Context.NoV;
VoH = Context.VoH;
NoH = Context.NoH;
SphereMaxNoH(Context, AreaLight.SphereSinAlpha, true);
}
Context.NoV = saturate(abs( Context.NoV ) + 1e-5);
Lighting.Diffuse = Diffuse_Lambert(GBuffer.DiffuseColor);
Lighting.Diffuse *= AreaLight.FalloffColor * (Falloff * NoL);
[branch]
if (bHasAnisotropy)
{
Lighting.Specular = AreaLight.FalloffColor * (Falloff * NoL) * SpecularGGX(GBuffer.Roughness, GBuffer.Anisotropy, GBuffer.SpecularColor, Context, NoL, AreaLight);
}
else
{
if( IsRectLight(AreaLight) )
{
Lighting.Specular = RectGGXApproxLTC(GBuffer.Roughness, GBuffer.SpecularColor, N, V, AreaLight.Rect, AreaLight.Texture);
}
else
{
Lighting.Specular = AreaLight.FalloffColor * (Falloff * NoL) * SpecularGGX(GBuffer.Roughness, GBuffer.SpecularColor, Context, NoL, AreaLight);
}
}
FBxDFEnergyTermsRGB EnergyTerms = ComputeGGXSpecEnergyTermsRGB (GBuffer.Roughness, Context.NoV, GBuffer.SpecularColor);
Lighting.Diffuse *= ComputeEnergyPreservation(EnergyTerms);
Lighting.Specular *= ComputeEnergyConservation(EnergyTerms);
Lighting.Transmission = 0;
return Lighting;
}
float3 SpecularGGX( float Roughness, float3 SpecularColor, BxDFContext Context, float NoL, FAreaLight AreaLight )
{
float a2 = Pow4( Roughness );
float Energy = EnergyNormalization( a2, Context.VoH, AreaLight );
float D = D_GGX( a2, Context.NoH ) * Energy;
float Vis = Vis_SmithJointApprox( a2, Context.NoV, NoL );
float3 F = F_Schlick( SpecularColor, Context.VoH );
return (D * Vis) * F;
}