Files
UnrealEngineUWP/Engine/Shaders/Private/MobileLocalLightsBuffer.usf
florian penzkofer ac347875a6 Add support for light functions to mobile foward renderer.
Light function materials are rendered as additional draws with additive blending to the prepass/postprocess targets.
For the prepass codepath (that requires a depth prepass) the directional render target is updated in the main pass, only the color target is updated per light function.

Light functions of non-directional lights are currently not supported.

#rb Florin.Pascu, Dmitriy.Dyomin, Charles.deRousiers

[CL 27764934 by florian penzkofer in ue5-main branch]
2023-09-11 14:52:36 -04:00

241 lines
7.1 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
#define SUPPORT_CONTACT_SHADOWS 0
#if !COMPUTE_SHADER
#include "Common.ush"
#include "SceneTextureParameters.ush"
#include "DeferredLightingCommon.ush"
#endif
#if LIGHT_FUNCTION
#include "/Engine/Generated/Material.ush"
#include "LightDataUniforms.ush"
#include "LightFunctionCommon.ush"
#else
#include "LightGridCommon.ush"
#endif
#if COMPUTE_SHADER
uint2 GroupSize;
RWBuffer<int> RWTileInfo;
[numthreads(THREADGROUP_SIZEX, 1, 1)]
void MainCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
if(DispatchThreadId.x > (GroupSize.x * GroupSize.y))
{
return;
}
const uint GridX = DispatchThreadId.x % GroupSize.x;
const uint GridY = DispatchThreadId.x / GroupSize.x;
const uint EyeIndex = 0;
const FLightGridData GridData = GetLightGridData(EyeIndex);
uint LocalNumLights = 0;
LOOP
for (int SliceIt = 0; SliceIt < GridData.CulledGridSize.z; SliceIt++)
{
const uint GridIndex = ComputeLightGridCellIndex(uint3(GridX, GridY, SliceIt), EyeIndex);
const FCulledLightsGridData CulledLightsGrid = GetCulledLightsGrid(GridIndex, EyeIndex);
LocalNumLights = CulledLightsGrid.NumLocalLights;
if(LocalNumLights > 0)
{
break;
}
}
if(LocalNumLights > 0)
{
RWTileInfo[2 * DispatchThreadId.x] = GridX;
RWTileInfo[2 * DispatchThreadId.x + 1] = GridY;
}
else
{
RWTileInfo[2 * DispatchThreadId.x] = -1;
RWTileInfo[2 * DispatchThreadId.x + 1] = -1;
}
}
#else // COMPUTE_SHADER
#if !LIGHT_FUNCTION
int LightGridPixelSize;
Buffer<int> TileInfo;
void MainVS(
float2 TexCoord : ATTRIBUTE0,
uint VertexId : SV_VertexID,
uint InstanceId : SV_InstanceID,
out float4 OutPosition : SV_POSITION)
{
float YPos = TileInfo[2 * InstanceId + 1] + TexCoord.y;
float XPos = TileInfo[2 * InstanceId] + TexCoord.x;
float2 ScreenUV = float2(XPos, YPos) * LightGridPixelSize * View.BufferSizeAndInvSize.zw;
float2 ScreenPosition = (ScreenUV.xy - View.ScreenPositionScaleBias.wz) / View.ScreenPositionScaleBias.xy;
OutPosition = float4(ScreenPosition, 0, 1);
if(TileInfo[2 * InstanceId] < 0)
{
OutPosition.xy = 0;
}
}
void Main(float4 SvPosition : SV_POSITION
, out float3 OutColorA : SV_Target0
#if POST_PROCESS_LOCAL_LIGHTS == 0
, out float4 OutColorB : SV_Target1
#endif
)
{
const uint2 PixelPos = SvPosition.xy;
const float2 ScreenPosition = SvPositionToScreenPosition(SvPosition).xy;
const uint EyeIndex = 0;
float2 ScreenUV = ScreenPosition * View.ScreenPositionScaleBias.xy + View.ScreenPositionScaleBias.wz;
float SceneDepth = CalcSceneDepth(ScreenUV);
float3 TranslatedWorldPosition = mul(float4(ScreenPosition * SceneDepth, SceneDepth, 1), PrimaryView.ScreenToTranslatedWorld).xyz;
float3 CameraVector = normalize(TranslatedWorldPosition);
uint GridIndex = ComputeLightGridCellIndex(uint2(PixelPos.x, PixelPos.y), SceneDepth);
const FCulledLightsGridData CulledLightGridData = GetCulledLightsGrid(GridIndex, EyeIndex);
uint NumLocalLights = min(CulledLightGridData.NumLocalLights, GetMaxLightsPerCell(EyeIndex));
float3 Color = uint3(0.f, 0.f , 0.f);
float3 SavedL = float3(0,0,0);
float TotalWeight = 0;
LOOP
for (uint LocalLightListIndex = 0; LocalLightListIndex < NumLocalLights; LocalLightListIndex++)
{
const FLocalLightData LocalLight = GetLocalLightData(CulledLightGridData.DataStartIndex + LocalLightListIndex, EyeIndex);
// extra-early out since we know light grid is sloppy and all lights in list are radial (have a range)
// appears useless
float InvLightRadiusSq = LocalLight.LightPositionAndInvRadius.w * LocalLight.LightPositionAndInvRadius.w;
float DistLight = length2(TranslatedWorldPosition - LocalLight.LightPositionAndInvRadius.xyz) * InvLightRadiusSq;
if (DistLight > 1.0f)
{
continue;
}
FDeferredLightData LightData = ConvertToDeferredLight_Mobile(LocalLight);
float3 L = LightData.Direction; // Already normalized
float3 ToLight = L;
float3 MaskedLightColor = LightData.Color;
float LightMask = 1;
if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation( TranslatedWorldPosition, LightData, ToLight, L );
MaskedLightColor *= LightMask;
}
float WeightLight = 1 - DistLight;
if( LightMask > 0)
{
TotalWeight += WeightLight;
// For lights with light functions we only only use the compute the direction (OutColorB)
// The Color is added in additional draws using MainLightFunction()
if (!UnpackHasLightFunction(LocalLight))
{
Color += MaskedLightColor;
}
SavedL += L * WeightLight;
}
}
#if POST_PROCESS_LOCAL_LIGHTS == 0
if (TotalWeight > 0)
{
SavedL = (SavedL / TotalWeight);
OutColorA = float3(Color.x, Color.y, Color.z);
OutColorB = 0.5f * float4(SavedL.x, SavedL.y, SavedL.z, 0) + float4(0.5f, 0.5f, 0.5f, 0.5f);
}
else
{
OutColorA = float3(0, 0, 0);
OutColorB = float4(0.5,0.5,0.5, 0.5);
}
#else
if (TotalWeight > 0)
{
SavedL = (SavedL / TotalWeight);
float3 N = normalize(cross(ddx(TranslatedWorldPosition.xyz), ddy(TranslatedWorldPosition.xyz)));
float3 L = SavedL; // Already normalized
float NoL = max(0, dot(N, L));
float3 Diffuse = (1 / PI) * NoL * Color;
OutColorA = 1 + Diffuse;
}
else
{
OutColorA = float3(1, 1, 1);
}
#endif
}
#else // !LIGHT_FUNCTION
float4x4 SvPositionToLight;
/** Fade distance in x, disabled brightness in y */
float2 LightFunctionParameters2;
void MainLightFunction(
float4 InScreenPosition : TEXCOORD0,
float4 SvPosition : SV_POSITION,
out float3 OutColorA : SV_Target0
)
{
float2 ScreenUV = SvPositionToBufferUV(SvPosition);
SvPosition.z = LookupDeviceZ(ScreenUV);
float3 TranslatedWorldPosition = SvPositionToTranslatedWorld(SvPosition);
float ViewDistance = length(GetPrimaryView().TranslatedWorldCameraOrigin - TranslatedWorldPosition);
float GreyScale;
{
float4 Hom = mul(float4(SvPosition.xyz, 1), SvPositionToLight);
float3 LightVector = Hom.xyz / Hom.w;
float3 Color = GetLightFunctionColor(LightVector, TranslatedWorldPosition);
GreyScale = dot(Color, .3333f);
}
float DistanceFadeAlpha = saturate((LightFunctionParameters2.x - ViewDistance) / (LightFunctionParameters2.x * .2f));
GreyScale = lerp(LightFunctionParameters2.y, GreyScale, DistanceFadeAlpha);
GreyScale = lerp(LightFunctionParameters2.y, GreyScale, LightFunctionParameters.y);
float EncodedLightAttenuation = EncodeLightAttenuation(GreyScale);
FDeferredLightData LightData = InitDeferredLightFromUniforms();
float3 L = LightData.Direction; // Already normalized
float3 ToLight = LightData.TranslatedWorldPosition - TranslatedWorldPosition;
float LightMask = 1;
// At the moment directional lights are not supported
// if (LightData.bRadialLight)
{
LightMask = GetLocalLightAttenuation( TranslatedWorldPosition, LightData, ToLight, L );
}
float3 Color = LightData.Color * LightMask * EncodedLightAttenuation;
#if POST_PROCESS_LOCAL_LIGHTS == 0
OutColorA = Color;
#else
float3 N = normalize(cross(ddx(TranslatedWorldPosition.xyz), ddy(TranslatedWorldPosition.xyz)));
float NoL = max(0, dot(N, L));
float3 Diffuse = (1 / PI) * NoL * Color;
OutColorA = Diffuse;
#endif
}
#endif // !LIGHT_FUNCTION
#endif // COMPUTE_SHADER