You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- r.TranslucencyLightingVolume.Batch 1 is enabled by default - For lights that don't require per-light CPU bindings, we put them in a path that loops on the GPU to cut down on draw calls and CPU overhead - Currently loops over the whole light grid, so the GPU performance is roughly the same as the old path. There are opportunities to accelerate this more in the future, but right now the big win is on the CPU. - Added a more accurate RectLight integration (to both paths). This is required for rect lights to be batched since the approximate logic is CPU-based. Enabled by default since the quality is superior and the performance difference is minimal in practice even with many rect lights. Can be disabled with r.TranslucencyLightingVolume.AccurateRectLights 0. - Various refactors to increase code sharing and reduce duplication between the two paths and other lighting code in the engine. Equivalent or much faster on the CPU. Up to ~3ms in scenes with hundreds of lights. Roughly equivalent speed on the GPU dependig on content and platform. #rb Charles.deRousiers #jira none [CL 35900364 by andrew lauritzen in ue5-main branch]
419 lines
15 KiB
Plaintext
419 lines
15 KiB
Plaintext
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/**
|
|
* TranslucentLightInjectionShaders.usf: Shaders for calculating lighting in a volume to use on translucency
|
|
*/
|
|
|
|
#include "Common.ush"
|
|
#include "SHCommon.ush"
|
|
|
|
#define TREAT_MAXDEPTH_UNSHADOWED 1
|
|
|
|
#if INJECTION_PIXEL_SHADER
|
|
#include "/Engine/Generated/Material.ush"
|
|
#include "VolumeLightingCommon.ush"
|
|
#endif
|
|
|
|
#include "DynamicLightingCommon.ush"
|
|
|
|
#define SUPPORT_CONTACT_SHADOWS 0
|
|
#include "DeferredLightingCommon.ush"
|
|
|
|
uint VolumeCascadeIndex;
|
|
float4 SimpleLightPositionAndRadius;
|
|
float4 SimpleLightColorAndExponent;
|
|
|
|
#if RADIAL_ATTENUATION==0
|
|
#define USE_CLOUD_TRANSMITTANCE 1
|
|
#include "VolumetricCloudCommon.ush"
|
|
uint VolumetricCloudShadowEnabled;
|
|
|
|
#include "/Engine/Private/SkyAtmosphereCommon.ush"
|
|
uint AtmospherePerPixelTransmittanceEnabled;
|
|
#endif
|
|
|
|
#if VIRTUAL_SHADOW_MAP
|
|
#include "VirtualShadowMaps/VirtualShadowMapProjectionCommon.ush"
|
|
#endif
|
|
|
|
#ifndef ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
#define ADAPTIVE_VOLUMETRIC_SHADOW_MAP 0
|
|
#endif // ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
|
|
#if ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
#include "HeterogeneousVolumes/HeterogeneousVolumesAdaptiveVolumetricShadowMapSampling.ush"
|
|
#endif // ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
|
|
float3 GetTranslatedWorldPosition(float2 UV, uint LayerIndex)
|
|
{
|
|
float ZPosition = View.TranslucencyLightingVolumeMin[VolumeCascadeIndex].z + (LayerIndex + .5f) * View.TranslucencyLightingVolumeInvSize[VolumeCascadeIndex].w;
|
|
float3 TranslatedWorldPosition = float3(View.TranslucencyLightingVolumeMin[VolumeCascadeIndex].xy + UV / View.TranslucencyLightingVolumeInvSize[VolumeCascadeIndex].xy - .5f * View.TranslucencyLightingVolumeInvSize[VolumeCascadeIndex].w, ZPosition);
|
|
return TranslatedWorldPosition;
|
|
}
|
|
|
|
void AccumulateSHLighting(
|
|
float3 NormalizedLightVector,
|
|
float3 Lighting,
|
|
float DirectionalLightContribution,
|
|
inout float4 OutColor0,
|
|
inout float4 OutColor1)
|
|
{
|
|
FTwoBandSHVectorRGB SHLighting = MulSH(SHBasisFunction(NormalizedLightVector), Lighting);
|
|
|
|
// Directional light contribution in w
|
|
OutColor0 += float4(SHLighting.R.V.x, SHLighting.G.V.x, SHLighting.B.V.x, DirectionalLightContribution);
|
|
|
|
float3 LuminanceWeights = LuminanceFactors();
|
|
float3 Coefficient0 = float3(SHLighting.R.V.y, SHLighting.G.V.y, SHLighting.B.V.y);
|
|
float3 Coefficient1 = float3(SHLighting.R.V.z, SHLighting.G.V.z, SHLighting.B.V.z);
|
|
float3 Coefficient2 = float3(SHLighting.R.V.w, SHLighting.G.V.w, SHLighting.B.V.w);
|
|
OutColor1 += float4(dot(Coefficient0, LuminanceWeights), dot(Coefficient1, LuminanceWeights), dot(Coefficient2, LuminanceWeights), 0);
|
|
}
|
|
|
|
float CalcSimpleLightAttenuation(float3 TranslatedWorldPosition)
|
|
{
|
|
float3 WorldLightVector = SimpleLightPositionAndRadius.xyz - TranslatedWorldPosition;
|
|
|
|
float Attenuation = 1;
|
|
|
|
if (SimpleLightColorAndExponent.w == 0)
|
|
{
|
|
float DistanceSqr = dot( WorldLightVector, WorldLightVector );
|
|
|
|
// Sphere falloff (technically just 1/d2 but this avoids inf)
|
|
Attenuation = 1 / ( DistanceSqr + 1 );
|
|
|
|
float LightRadiusMask = Square(saturate(1 - Square(DistanceSqr / (SimpleLightPositionAndRadius.w * SimpleLightPositionAndRadius.w))));
|
|
Attenuation *= LightRadiusMask;
|
|
}
|
|
else
|
|
{
|
|
Attenuation = RadialAttenuation(WorldLightVector / SimpleLightPositionAndRadius.w, SimpleLightColorAndExponent.w);
|
|
}
|
|
|
|
return Attenuation;
|
|
}
|
|
|
|
/** Pixel shader that calculates direct lighting for a simple light (unshadowed point light) for all the affected voxels of a volume texture. */
|
|
void SimpleLightInjectMainPS(
|
|
FWriteToSliceGeometryOutput Input,
|
|
out float4 OutColor0 : SV_Target0,
|
|
out float4 OutColor1 : SV_Target1
|
|
)
|
|
{
|
|
OutColor0 = 0;
|
|
OutColor1 = 0;
|
|
|
|
float3 TranslatedWorldPosition = GetTranslatedWorldPosition(Input.Vertex.UV, Input.LayerIndex);
|
|
float3 NormalizedLightVector = normalize(SimpleLightPositionAndRadius.xyz - TranslatedWorldPosition);
|
|
|
|
float VoxelSize = View.TranslucencyLightingVolumeInvSize[VolumeCascadeIndex].w;
|
|
float3 TranslatedWorldPositionForLighting = TranslatedWorldPosition + 1 * GetBoxPushout(NormalizedLightVector, .5f * VoxelSize);
|
|
|
|
float Attenuation = CalcSimpleLightAttenuation(TranslatedWorldPositionForLighting);
|
|
float3 Lighting = SimpleLightColorAndExponent.rgb / PI * Attenuation;
|
|
|
|
float DirectionalLightContribution = 0.0f;
|
|
AccumulateSHLighting(NormalizedLightVector, Lighting, DirectionalLightContribution,
|
|
OutColor0, OutColor1);
|
|
}
|
|
|
|
float GetBorderLerpFactor(float2 UV, uint LayerIndex)
|
|
{
|
|
// Larger values result in a shorter transition distance
|
|
float TransitionScale = 10;
|
|
|
|
float3 VolumeUVs = float3(UV, (LayerIndex + .5f) * View.TranslucencyLightingVolumeMin[VolumeCascadeIndex].w);
|
|
|
|
// Rescale the UVs to make the fade go to 0 before the edge of the volume
|
|
float3 FadeUVs = VolumeUVs * (1 + 4 * View.TranslucencyLightingVolumeMin[VolumeCascadeIndex].w) - 2 * View.TranslucencyLightingVolumeMin[VolumeCascadeIndex].w;
|
|
// Setup a 3d lerp factor going to 0 near the edge of the outer volume
|
|
float3 LerpFactors = saturate((.5f - abs(FadeUVs - .5f)) * TransitionScale);
|
|
float FinalLerpFactor = LerpFactors.x * LerpFactors.y * LerpFactors.z;
|
|
|
|
return FinalLerpFactor;
|
|
}
|
|
|
|
float3 GetTranslatedWorldPositionForLighting(float3 TranslatedWorldPosition, float3 NormalizedLightVector)
|
|
{
|
|
float VoxelSize = View.TranslucencyLightingVolumeInvSize[VolumeCascadeIndex].w;
|
|
float3 LightingOffset = 1 * GetBoxPushout(NormalizedLightVector, .5f * VoxelSize);
|
|
return TranslatedWorldPosition + LightingOffset;
|
|
}
|
|
|
|
float GetTranslucencyVolumeLocalLightAttenuation(float3 TranslatedWorldPosition, FDeferredLightData LightData)
|
|
{
|
|
float3 ToLight, L;
|
|
float Attenuation = GetLocalLightAttenuation(TranslatedWorldPosition, LightData, ToLight, L);
|
|
|
|
if (LightData.bRectLight)
|
|
{
|
|
if (Attenuation > 0.0f)
|
|
{
|
|
// Use a more accurate rect light approximation
|
|
FRect Rect = GetRect(ToLight, LightData);
|
|
Attenuation *= IntegrateLight(Rect);
|
|
}
|
|
}
|
|
else if (LightData.bInverseSquared)
|
|
{
|
|
// This path still uses the legacy local light attenuation, so add that back in
|
|
float DistanceSqr = dot(ToLight, ToLight);
|
|
Attenuation *= 1.0f / (DistanceSqr + 1.0f);
|
|
}
|
|
|
|
return Attenuation;
|
|
}
|
|
|
|
#if INJECTION_PIXEL_SHADER
|
|
|
|
#include "LightDataUniforms.ush"
|
|
#include "LightFunctionCommon.ush"
|
|
|
|
// WorldSpace planes to clip the cascade for ShadoewMethod1
|
|
float4 ClippingPlanes[2];
|
|
/** 1 if the light is a spotlight, 0 otherwise. */
|
|
float SpotlightMask;
|
|
|
|
float GetLightFunctionShadowFactor(float3 TranslatedWorldPositionForLighting)
|
|
{
|
|
float ShadowFactor = 1;
|
|
|
|
// Apply light function after edge fading, so that a black light function at the edges can cause distant translucency to also be black
|
|
#if APPLY_LIGHT_FUNCTION
|
|
float4 LightVector = mul(float4(TranslatedWorldPositionForLighting, 1),LightFunctionTranslatedWorldToLight);
|
|
LightVector.xyz /= LightVector.w;
|
|
float3 LightFunction = GetLightFunctionColor(LightVector.xyz, TranslatedWorldPositionForLighting);
|
|
|
|
// We only suport monochrome light functions
|
|
ShadowFactor = dot(LightFunction, .3333f).x;
|
|
#endif
|
|
|
|
return ShadowFactor;
|
|
}
|
|
|
|
int VirtualShadowMapId;
|
|
|
|
/** Pixel shader that calculates direct lighting for all the affected voxels of a volume texture. */
|
|
void InjectMainPS(
|
|
FWriteToSliceGeometryOutput Input,
|
|
out float4 OutColor0 : SV_Target0,
|
|
out float4 OutColor1 : SV_Target1
|
|
)
|
|
{
|
|
OutColor0 = 0;
|
|
OutColor1 = 0;
|
|
|
|
float3 TranslatedWorldPosition = GetTranslatedWorldPosition(Input.Vertex.UV, Input.LayerIndex);
|
|
|
|
FDeferredLightData LightData = InitDeferredLightFromUniforms();
|
|
float3 LightVector = LightData.Direction;
|
|
|
|
// 0: no contribution, 1:full contribution
|
|
float Masking = 1.0f;
|
|
#if RADIAL_ATTENUATION
|
|
{
|
|
// cull voxels outside the light radius (value is < 0)
|
|
LightVector = LightData.TranslatedWorldPosition - TranslatedWorldPosition;
|
|
clip(1.0f / (LightData.InvRadius * LightData.InvRadius) - dot(LightVector, LightVector));
|
|
}
|
|
#else
|
|
{
|
|
// directional light
|
|
float DistToNear = -dot(ClippingPlanes[0], float4(TranslatedWorldPosition, 1));
|
|
float DistToFar = -dot(ClippingPlanes[1], float4(TranslatedWorldPosition, 1));
|
|
|
|
// cull volumes outside the cascade (value is < 0)
|
|
clip(DistToNear);
|
|
clip(DistToFar);
|
|
|
|
// fade cascade transition regions (additivebly blended so it does a cross fade)
|
|
Masking *= saturate(DistToNear * ShadowInjectParams.x);
|
|
Masking *= saturate(DistToFar * ShadowInjectParams.y);
|
|
}
|
|
#endif
|
|
|
|
float3 NormalizedLightVector = normalize(LightVector);
|
|
float3 TranslatedWorldPositionForLighting = GetTranslatedWorldPositionForLighting(TranslatedWorldPosition, NormalizedLightVector);
|
|
|
|
{
|
|
float Attenuation = 1.0f;
|
|
float ShadowFactor = 1.0f;
|
|
|
|
#if RADIAL_ATTENUATION
|
|
Attenuation *= GetTranslucencyVolumeLocalLightAttenuation(TranslatedWorldPositionForLighting, LightData);
|
|
#endif
|
|
|
|
{
|
|
bool bUnused = false;
|
|
ShadowFactor *= ComputeVolumeShadowing(
|
|
TranslatedWorldPositionForLighting,
|
|
// Maintain the legacy logic of "spotlight" vs "everything else"
|
|
!LightData.bSpotLight,
|
|
LightData.bSpotLight,
|
|
bUnused);
|
|
}
|
|
|
|
#if VIRTUAL_SHADOW_MAP
|
|
FVirtualShadowMapSampleResult VirtualShadowMapSample = SampleVirtualShadowMapTranslatedWorld(VirtualShadowMapId, TranslatedWorldPositionForLighting);
|
|
ShadowFactor *= VirtualShadowMapSample.ShadowFactor;
|
|
#endif
|
|
|
|
#if ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
ShadowFactor *= AVSM_SampleTransmittance(TranslatedWorldPositionForLighting, LightData.TranslatedWorldPosition);
|
|
#endif // ADAPTIVE_VOLUMETRIC_SHADOW_MAP
|
|
|
|
// Apply light function (it will also fade out at the edge of the volume so overall dimming of light using light function is not a recommended workflow)
|
|
ShadowFactor *= GetLightFunctionShadowFactor(TranslatedWorldPositionForLighting);
|
|
|
|
#if RADIAL_ATTENUATION==0
|
|
// Apply cloud shadow for atmosphere directional lights only if needed
|
|
if (VolumetricCloudShadowEnabled > 0)
|
|
{
|
|
float OutOpticalDepth = 0.0f;
|
|
ShadowFactor *= lerp(1.0f, GetCloudVolumetricShadow(TranslatedWorldPositionForLighting, CloudShadowmapTranslatedWorldToLightClipMatrix, CloudShadowmapFarDepthKm, CloudShadowmapTexture, CloudShadowmapSampler, OutOpticalDepth), CloudShadowmapStrength);
|
|
}
|
|
#endif
|
|
|
|
if (VolumeCascadeIndex == 1)
|
|
{
|
|
float FinalLerpFactor = GetBorderLerpFactor(Input.Vertex.UV, Input.LayerIndex);
|
|
|
|
#if RADIAL_ATTENUATION
|
|
// For local lights, fade attenuation to 0 for the border voxels
|
|
Attenuation = lerp(0, Attenuation, FinalLerpFactor);
|
|
ShadowFactor = lerp(0.0f, ShadowFactor, FinalLerpFactor);
|
|
#else
|
|
// Fade out shadowing for the border voxels
|
|
// The border voxels are used to light all translucency outside of both lighting volumes
|
|
ShadowFactor = lerp(1.0f, ShadowFactor, FinalLerpFactor);
|
|
#endif
|
|
}
|
|
|
|
float3 Lighting = LightData.Color / PI * Attenuation * ShadowFactor;
|
|
|
|
#if RADIAL_ATTENUATION==0
|
|
// Apply color atmosphere transmittance for atmosphere directional lights only if ndeeded
|
|
if (AtmospherePerPixelTransmittanceEnabled > 0)
|
|
{
|
|
const float3 TranslatedWorldPlanetCenterToPos = (TranslatedWorldPositionForLighting - View.SkyPlanetTranslatedWorldCenterAndViewHeight.xyz) * CM_TO_SKY_UNIT;
|
|
Lighting *= GetAtmosphereTransmittance(TranslatedWorldPlanetCenterToPos, NormalizedLightVector, View.SkyAtmosphereBottomRadiusKm, View.SkyAtmosphereTopRadiusKm, View.TransmittanceLutTexture, View.TransmittanceLutTextureSampler);
|
|
}
|
|
#endif
|
|
|
|
float DirectionalLightContribution = 0;
|
|
#if !RADIAL_ATTENUATION
|
|
DirectionalLightContribution = Attenuation * ShadowFactor;
|
|
#endif
|
|
AccumulateSHLighting(NormalizedLightVector, Lighting, DirectionalLightContribution,
|
|
OutColor0, OutColor1);
|
|
}
|
|
|
|
// debug, make inner cascase green
|
|
// if(VolumeCascadeIndex == 0) OutColor0 = float4(0,1,0,1);
|
|
|
|
OutColor0 *= Masking;
|
|
OutColor1 *= Masking;
|
|
}
|
|
|
|
#endif // #if INJECTION_PIXEL_SHADER
|
|
|
|
#if INJECTION_BATCH_PIXEL_SHADER
|
|
|
|
#include "LightGridCommon.ush"
|
|
#include "LightData.ush"
|
|
|
|
//int VirtualShadowMapId;
|
|
StructuredBuffer<uint> BatchedLocalLights;
|
|
uint MaxBatchedLocalLights;
|
|
|
|
/** Pixel shader that calculates direct lighting for all the affected voxels of a volume texture. */
|
|
void InjectBatchMainPS(
|
|
FWriteToSliceGeometryOutput Input,
|
|
out float4 RTOutColor0 : SV_Target0,
|
|
out float4 RTOutColor1 : SV_Target1
|
|
)
|
|
{
|
|
float4 OutColor0 = 0;
|
|
float4 OutColor1 = 0;
|
|
|
|
float3 TranslatedWorldPosition = GetTranslatedWorldPosition(Input.Vertex.UV, Input.LayerIndex);
|
|
|
|
const uint NumUints = (MaxBatchedLocalLights + 31U) / 32U;
|
|
for (uint UintIndex = 0; UintIndex < NumUints; ++UintIndex)
|
|
{
|
|
uint CurrentMask = BatchedLocalLights[UintIndex];
|
|
while (CurrentMask != 0)
|
|
{
|
|
int BitIndex = firstbitlow(CurrentMask);
|
|
CurrentMask = CurrentMask & ~(1U << BitIndex);
|
|
uint LocalLightIndex = 32U * UintIndex + BitIndex;
|
|
|
|
{
|
|
FLocalLightData LocalLightData = GetLocalLightDataNonStereo(LocalLightIndex);
|
|
FDeferredLightData LightData = ConvertToDeferredLight(LocalLightData);
|
|
|
|
float3 LightVector = LightData.TranslatedWorldPosition - TranslatedWorldPosition;
|
|
if (rcp(Square(LightData.InvRadius)) > dot(LightVector, LightVector))
|
|
{
|
|
float3 NormalizedLightVector = normalize(LightVector);
|
|
float3 TranslatedWorldPositionForLighting = GetTranslatedWorldPositionForLighting(TranslatedWorldPosition, NormalizedLightVector);
|
|
|
|
float Attenuation = GetTranslucencyVolumeLocalLightAttenuation(TranslatedWorldPositionForLighting, LightData);
|
|
float ShadowFactor = 1.0f;
|
|
|
|
if (Attenuation > 0.0f)
|
|
{
|
|
#if VIRTUAL_SHADOW_MAP
|
|
if (LocalLightData.VirtualShadowMapId != INDEX_NONE)
|
|
{
|
|
FVirtualShadowMapSampleResult VirtualShadowMapSample = SampleVirtualShadowMapTranslatedWorld(LocalLightData.VirtualShadowMapId, TranslatedWorldPositionForLighting);
|
|
ShadowFactor *= VirtualShadowMapSample.ShadowFactor;
|
|
}
|
|
#endif
|
|
if (VolumeCascadeIndex == 1)
|
|
{
|
|
float FinalLerpFactor = GetBorderLerpFactor(Input.Vertex.UV, Input.LayerIndex);
|
|
// For local lights, fade attenuation to 0 for the border voxels
|
|
Attenuation = lerp(0, Attenuation, FinalLerpFactor);
|
|
ShadowFactor = lerp(0.0f, ShadowFactor, FinalLerpFactor);
|
|
}
|
|
|
|
float3 Lighting = LightData.Color / PI * Attenuation * ShadowFactor;
|
|
|
|
float DirectionalLightContribution = 0;
|
|
AccumulateSHLighting(NormalizedLightVector, Lighting, DirectionalLightContribution,
|
|
OutColor0, OutColor1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RTOutColor0 = OutColor0;
|
|
RTOutColor1 = OutColor1;
|
|
}
|
|
|
|
#endif // #if INJECTION_BATCH_PIXEL_SHADER
|
|
|
|
|
|
#if CLEAR_COMPUTE_SHADER
|
|
RWTexture3D<float4> RWAmbient0;
|
|
RWTexture3D<float4> RWDirectional0;
|
|
RWTexture3D<float4> RWAmbient1;
|
|
RWTexture3D<float4> RWDirectional1;
|
|
|
|
[numthreads(CLEAR_BLOCK_SIZE, CLEAR_BLOCK_SIZE, CLEAR_BLOCK_SIZE)]
|
|
void ClearTranslucentLightingVolumeCS(
|
|
uint3 GroupId : SV_GroupID,
|
|
uint3 DispatchThreadId : SV_DispatchThreadID,
|
|
uint3 GroupThreadId : SV_GroupThreadID)
|
|
{
|
|
|
|
uint3 Index = uint3(GroupId.x * CLEAR_BLOCK_SIZE + GroupThreadId.x, GroupId.y * CLEAR_BLOCK_SIZE + GroupThreadId.y, GroupId.z * CLEAR_BLOCK_SIZE + GroupThreadId.z);
|
|
RWAmbient0[Index.xyz] = float4(0.0, 0.0, 0.0, 0.0);
|
|
RWDirectional0[Index.xyz] = float4(0.0, 0.0, 0.0, 0.0);
|
|
RWAmbient1[Index.xyz] = float4(0.0, 0.0, 0.0, 0.0);
|
|
RWDirectional1[Index.xyz] = float4(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
#endif |