Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/HeightfieldLighting.cpp
tiago costa 7503b52da0 Updated most of distance field ambient occlusion codepath to use RDG.
- Resource state transitions handled by RDG.
- FDistanceFieldObjectBufferResource replaced by FDistanceFieldCulledObjectBufferParameters to use RDG.
- FTileIntersectionResources replaced by FTileIntersectionParameters to use RDG.
- FAOScreenGridResources replaced by FAOScreenGridParameters to use RDG.
- Don't store DFAO resources in View.State.
- Split FDeferredShadingSceneRenderer::RenderDistanceFieldAOScreenGrid() into multiple passes.
- Split BuildTileObjectLists() into multiple passes.

#rb daniel.wright

[CL 16660080 by tiago costa in ue5-main branch]
2021-06-14 12:45:34 -04:00

1229 lines
50 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
HeightfieldLighting.cpp
=============================================================================*/
#include "HeightfieldLighting.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "Materials/Material.h"
#include "GlobalShader.h"
#include "MaterialShaderType.h"
#include "MaterialShader.h"
#include "DistanceFieldLightingShared.h"
#include "ScreenRendering.h"
#include "DistanceFieldAmbientOcclusion.h"
#include "LightRendering.h"
#include "PipelineStateCache.h"
IMPLEMENT_TYPE_LAYOUT(FHeightfieldDescriptionParameters);
IMPLEMENT_TYPE_LAYOUT(FHeightfieldTextureParameters);
// Currently disabled because the bHasHeightfieldRepresentation GBuffer bit has been reallocated, and self-shadowing artifacts are too severe without that bit
int32 GAOHeightfieldOcclusion = 0;
FAutoConsoleVariableRef CVarAOHeightfieldOcclusion(
TEXT("r.AOHeightfieldOcclusion"),
GAOHeightfieldOcclusion,
TEXT("Whether to compute AO from heightfields (landscape)"),
ECVF_RenderThreadSafe
);
int32 GHeightfieldGlobalIllumination = 1;
FAutoConsoleVariableRef CVarHeightfieldGlobalIllumination(
TEXT("r.HeightfieldGlobalIllumination"),
GHeightfieldGlobalIllumination,
TEXT(""),
ECVF_RenderThreadSafe
);
float GHeightfieldInnerBounceDistance = 3000;
FAutoConsoleVariableRef CVarHeightfieldInnerBounceDistancer(
TEXT("r.HeightfieldInnerBounceDistance"),
GHeightfieldInnerBounceDistance,
TEXT(""),
ECVF_RenderThreadSafe
);
float GHeightfieldOuterBounceDistanceScale = 3;
FAutoConsoleVariableRef CVarHeightfieldOuterBounceDistanceScale(
TEXT("r.HeightfieldOuterBounceDistanceScale"),
GHeightfieldOuterBounceDistanceScale,
TEXT(""),
ECVF_RenderThreadSafe
);
float GetGHeightfieldBounceDistance()
{
return GHeightfieldInnerBounceDistance * GHeightfieldOuterBounceDistanceScale;
}
float GHeightfieldTargetUnitsPerTexel = 200;
FAutoConsoleVariableRef CVarHeightfieldTargetUnitsPerTexel(
TEXT("r.HeightfieldTargetUnitsPerTexel"),
GHeightfieldTargetUnitsPerTexel,
TEXT(""),
ECVF_RenderThreadSafe
);
void FHeightfieldLightingAtlas::InitDynamicRHI()
{
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
if (AtlasSize.GetMin() > 0)
{
FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(
AtlasSize,
PF_G16,
FClearValueBinding::Transparent,
TexCreate_None,
TexCreate_RenderTargetable,
false));
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, Height, TEXT("HeightAtlas"));
FPooledRenderTargetDesc Desc2(FPooledRenderTargetDesc::Create2DDesc(
AtlasSize,
PF_R8G8,
FClearValueBinding::Transparent,
TexCreate_None,
TexCreate_RenderTargetable,
false));
GRenderTargetPool.FindFreeElement(RHICmdList, Desc2, Normal, TEXT("NormalAtlas"));
FPooledRenderTargetDesc Desc3(FPooledRenderTargetDesc::Create2DDesc(
AtlasSize,
PF_R8G8B8A8,
FClearValueBinding::Transparent,
TexCreate_None,
TexCreate_RenderTargetable,
false));
GRenderTargetPool.FindFreeElement(RHICmdList, Desc3, DiffuseColor, TEXT("DiffuseColorAtlas"));
FPooledRenderTargetDesc Desc4(FPooledRenderTargetDesc::Create2DDesc(
AtlasSize,
PF_G8,
FClearValueBinding::White,
TexCreate_None,
TexCreate_RenderTargetable,
false));
GRenderTargetPool.FindFreeElement(RHICmdList, Desc4, DirectionalLightShadowing, TEXT("HeightfieldShadowingAtlas"));
FPooledRenderTargetDesc Desc5(FPooledRenderTargetDesc::Create2DDesc(
AtlasSize,
PF_FloatR11G11B10,
FClearValueBinding::Black,
TexCreate_None,
TexCreate_RenderTargetable,
false));
Desc5.AutoWritable = false;
GRenderTargetPool.FindFreeElement(RHICmdList, Desc5, Lighting, TEXT("HeightfieldLightingAtlas"));
}
}
void FHeightfieldLightingAtlas::ReleaseDynamicRHI()
{
GRenderTargetPool.FreeUnusedResource(Height);
GRenderTargetPool.FreeUnusedResource(Normal);
GRenderTargetPool.FreeUnusedResource(DiffuseColor);
GRenderTargetPool.FreeUnusedResource(DirectionalLightShadowing);
GRenderTargetPool.FreeUnusedResource(Lighting);
}
void FHeightfieldLightingAtlas::InitializeForSize(FIntPoint InAtlasSize)
{
if (InAtlasSize.X > AtlasSize.X || InAtlasSize.Y > AtlasSize.Y)
{
AtlasSize.X = FMath::Max(InAtlasSize.X, AtlasSize.X);
AtlasSize.Y = FMath::Max(InAtlasSize.Y, AtlasSize.Y);
if (IsInitialized())
{
UpdateRHI();
}
else
{
InitResource();
}
}
}
class FSubsectionHeightfieldDescriptionsResource : public FRenderResource
{
public:
FCPUUpdatedBuffer Data;
FSubsectionHeightfieldDescriptionsResource()
{
Data.Format = PF_A32B32G32R32F;
// In float4's, must match usf
Data.Stride = 4;
}
virtual void InitDynamicRHI() override
{
Data.Initialize();
}
virtual void ReleaseDynamicRHI() override
{
Data.Release();
}
};
TGlobalResource<FSubsectionHeightfieldDescriptionsResource> GSubsectionHeightfieldDescriptions;
class FSubsectionHeightfieldDescriptionParameters
{
DECLARE_INLINE_TYPE_LAYOUT(FSubsectionHeightfieldDescriptionParameters, NonVirtual);
public:
void Bind(const FShaderParameterMap& ParameterMap)
{
SubsectionHeightfieldDescriptions.Bind(ParameterMap, TEXT("SubsectionHeightfieldDescriptions"));
}
friend FArchive& operator<<(FArchive& Ar, FSubsectionHeightfieldDescriptionParameters& Parameters)
{
Ar << Parameters.SubsectionHeightfieldDescriptions;
return Ar;
}
template<typename ShaderRHIParamRef>
void Set(FRHICommandList& RHICmdList, const ShaderRHIParamRef ShaderRHI)
{
SetSRVParameter(RHICmdList, ShaderRHI, SubsectionHeightfieldDescriptions, GSubsectionHeightfieldDescriptions.Data.BufferSRV);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, SubsectionHeightfieldDescriptions)
};
int32 UploadSubsectionHeightfieldDescriptions(const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions, FVector2D InvLightingAtlasSize, float InvDownsampleFactor)
{
TArray<FVector4, SceneRenderingAllocator> HeightfieldDescriptionData;
HeightfieldDescriptionData.Empty(HeightfieldDescriptions.Num() * GSubsectionHeightfieldDescriptions.Data.Stride);
for (int32 DescriptionIndex = 0; DescriptionIndex < HeightfieldDescriptions.Num(); DescriptionIndex++)
{
const FHeightfieldComponentDescription& Description = HeightfieldDescriptions[DescriptionIndex];
for (int32 SubsectionY = 0; SubsectionY < Description.NumSubsections; SubsectionY++)
{
for (int32 SubsectionX = 0; SubsectionX < Description.NumSubsections; SubsectionX++)
{
HeightfieldDescriptionData.Add(FVector4(SubsectionX, SubsectionY, 0, 0));
HeightfieldDescriptionData.Add(Description.SubsectionScaleAndBias);
HeightfieldDescriptionData.Add(Description.HeightfieldScaleBias);
// GlobalUVScaleBias = SubsectionSizeQuads / AtlasSize, (SubsectionBase + SubsectionId * SubsectionSizeQuads - AtlasMin) / AtlasSize
const FVector4 GlobalUVScaleBias(
Description.SubsectionScaleAndBias.X * InvLightingAtlasSize.X * InvDownsampleFactor,
Description.SubsectionScaleAndBias.Y * InvLightingAtlasSize.Y * InvDownsampleFactor,
(Description.LightingAtlasLocation.X + SubsectionX * Description.SubsectionScaleAndBias.X * InvDownsampleFactor) * InvLightingAtlasSize.X,
(Description.LightingAtlasLocation.Y + SubsectionY * Description.SubsectionScaleAndBias.Y * InvDownsampleFactor) * InvLightingAtlasSize.Y);
HeightfieldDescriptionData.Add(GlobalUVScaleBias);
}
}
}
check(HeightfieldDescriptionData.Num() % GSubsectionHeightfieldDescriptions.Data.Stride == 0);
if (HeightfieldDescriptionData.Num() > GSubsectionHeightfieldDescriptions.Data.MaxElements)
{
GSubsectionHeightfieldDescriptions.Data.MaxElements = HeightfieldDescriptionData.Num() * 5 / 4;
GSubsectionHeightfieldDescriptions.Data.Release();
GSubsectionHeightfieldDescriptions.Data.Initialize();
}
void* LockedBuffer = RHILockBuffer(GSubsectionHeightfieldDescriptions.Data.Buffer, 0, GSubsectionHeightfieldDescriptions.Data.Buffer->GetSize(), RLM_WriteOnly);
const uint32 MemcpySize = HeightfieldDescriptionData.GetTypeSize() * HeightfieldDescriptionData.Num();
check(GSubsectionHeightfieldDescriptions.Data.Buffer->GetSize() >= MemcpySize);
FPlatformMemory::Memcpy(LockedBuffer, HeightfieldDescriptionData.GetData(), MemcpySize);
RHIUnlockBuffer(GSubsectionHeightfieldDescriptions.Data.Buffer);
return HeightfieldDescriptionData.Num() / GSubsectionHeightfieldDescriptions.Data.Stride;
}
bool DoesPlatformSupportDistanceFieldGI(EShaderPlatform Platform)
{
//@todo - remove remaining uses
return false;
}
class FHeightfieldSubsectionQuadVS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FHeightfieldSubsectionQuadVS, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Parameters.Platform);
}
/** Default constructor. */
FHeightfieldSubsectionQuadVS() {}
/** Initialization constructor. */
FHeightfieldSubsectionQuadVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
SubsectionHeightfieldParameters.Bind(Initializer.ParameterMap);
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View)
{
FRHIVertexShader* ShaderRHI = RHICmdList.GetBoundVertexShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
SubsectionHeightfieldParameters.Set(RHICmdList, ShaderRHI);
}
private:
LAYOUT_FIELD(FSubsectionHeightfieldDescriptionParameters, SubsectionHeightfieldParameters);
};
IMPLEMENT_SHADER_TYPE(, FHeightfieldSubsectionQuadVS, TEXT("/Engine/Private/HeightfieldLighting.usf"), TEXT("HeightfieldSubsectionQuadVS"), SF_Vertex);
class FInitializeHeightfieldsPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FInitializeHeightfieldsPS, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Parameters.Platform);
}
/** Default constructor. */
FInitializeHeightfieldsPS() {}
/** Initialization constructor. */
FInitializeHeightfieldsPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
HeightfieldTextureParameters.Bind(Initializer.ParameterMap);
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, UTexture2D* HeightfieldTextureValue, UTexture2D* DiffuseColorTextureValue)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
HeightfieldTextureParameters.Set(RHICmdList, ShaderRHI, HeightfieldTextureValue, DiffuseColorTextureValue, nullptr);
}
private:
LAYOUT_FIELD(FHeightfieldTextureParameters, HeightfieldTextureParameters);
};
IMPLEMENT_SHADER_TYPE(, FInitializeHeightfieldsPS, TEXT("/Engine/Private/HeightfieldLighting.usf"), TEXT("InitializeHeightfieldsPS"), SF_Pixel);
class FQuadVertexBuffer : public FVertexBuffer
{
public:
FQuadVertexBuffer()
{
}
virtual void InitRHI() override
{
const uint32 Size = 6 * sizeof(FScreenVertex);
FRHIResourceCreateInfo CreateInfo(TEXT("FQuadVertexBuffer"));
VertexBufferRHI = RHICreateBuffer(Size, BUF_Static | BUF_VertexBuffer, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
FScreenVertex* DestVertex = (FScreenVertex*)RHILockBuffer(VertexBufferRHI, 0, Size, RLM_WriteOnly);
DestVertex[0].Position = FVector2D(1, 1);
DestVertex[0].UV = FVector2D(1, 1);
DestVertex[1].Position = FVector2D(0, 1);
DestVertex[1].UV = FVector2D(0, 1);
DestVertex[2].Position = FVector2D(1, 0);
DestVertex[2].UV = FVector2D(1, 0);
DestVertex[3].Position = FVector2D(1, 0);
DestVertex[3].UV = FVector2D(1, 0);
DestVertex[4].Position = FVector2D(0, 1);
DestVertex[4].UV = FVector2D(0, 1);
DestVertex[5].Position = FVector2D(0, 0);
DestVertex[5].UV = FVector2D(0, 0);
RHIUnlockBuffer(VertexBufferRHI);
}
};
TGlobalResource<FQuadVertexBuffer> GQuadVertexBuffer;
bool SupportsHeightfieldLighting(ERHIFeatureLevel::Type FeatureLevel, EShaderPlatform ShaderPlatform)
{
return FeatureLevel >= ERHIFeatureLevel::SM5
&& DoesPlatformSupportDistanceFieldGI(ShaderPlatform);
}
bool AllowHeightfieldGI(const FViewInfo& View)
{
return GHeightfieldGlobalIllumination
&& View.State
//@todo - remove heightfield GI
&& false;
}
void FHeightfieldLightingViewInfo::SetupVisibleHeightfields(const FViewInfo& View, FRDGBuilder& GraphBuilder)
{
const FScene* Scene = (const FScene*)View.Family->Scene;
const int32 NumPrimitives = Scene->DistanceFieldSceneData.HeightfieldPrimitives.Num();
if ((!AllowHeightfieldGI(View) && !GAOHeightfieldOcclusion)
|| NumPrimitives == 0
|| !SupportsHeightfieldLighting(View.GetFeatureLevel(), View.GetShaderPlatform()))
{
return;
}
AddPass(GraphBuilder, [this, Scene, &View, NumPrimitives](FRHICommandListImmediate& RHICmdList)
{
const float MaxDistanceSquared = FMath::Square(GetMaxAOViewDistance() + GetGHeightfieldBounceDistance());
float LocalToWorldScale = 1;
for (int32 HeightfieldPrimitiveIndex = 0; HeightfieldPrimitiveIndex < NumPrimitives; HeightfieldPrimitiveIndex++)
{
const FPrimitiveSceneInfo* HeightfieldPrimitive = Scene->DistanceFieldSceneData.HeightfieldPrimitives[HeightfieldPrimitiveIndex];
const FBoxSphereBounds& PrimitiveBounds = HeightfieldPrimitive->Proxy->GetBounds();
const float DistanceToPrimitiveSq = (PrimitiveBounds.Origin - View.ViewMatrices.GetViewOrigin()).SizeSquared();
if (View.ViewFrustum.IntersectSphere(PrimitiveBounds.Origin, PrimitiveBounds.SphereRadius + GetGHeightfieldBounceDistance())
&& DistanceToPrimitiveSq < MaxDistanceSquared)
{
UTexture2D* HeightfieldTexture = NULL;
UTexture2D* DiffuseColorTexture = NULL;
UTexture2D* VisibilityTexture = NULL;
FHeightfieldComponentDescription NewComponentDescription(HeightfieldPrimitive->Proxy->GetLocalToWorld());
HeightfieldPrimitive->Proxy->GetHeightfieldRepresentation(HeightfieldTexture, DiffuseColorTexture, VisibilityTexture, NewComponentDescription);
if (HeightfieldTexture && HeightfieldTexture->GetResource()->TextureRHI)
{
const FIntPoint HeightfieldSize = NewComponentDescription.HeightfieldRect.Size();
if (Heightfield.Rect.Area() == 0)
{
Heightfield.Rect = NewComponentDescription.HeightfieldRect;
LocalToWorldScale = NewComponentDescription.LocalToWorld.GetScaleVector().X;
}
else
{
Heightfield.Rect.Union(NewComponentDescription.HeightfieldRect);
}
TArray<FHeightfieldComponentDescription>& ComponentDescriptions = Heightfield.ComponentDescriptions.FindOrAdd(FHeightfieldComponentTextures(HeightfieldTexture, DiffuseColorTexture, VisibilityTexture));
ComponentDescriptions.Add(NewComponentDescription);
}
}
}
if (AllowHeightfieldGI(View) && Heightfield.ComponentDescriptions.Num() > 0)
{
FSceneViewState* ViewState = (FSceneViewState*)View.State;
{
FHeightfieldLightingAtlas*& ExistingAtlas = ViewState->HeightfieldLightingAtlas;
if (!ExistingAtlas)
{
ExistingAtlas = new FHeightfieldLightingAtlas();
}
Heightfield.DownsampleFactor = FMath::Max(FMath::TruncToInt(GHeightfieldTargetUnitsPerTexel / LocalToWorldScale), 1);
Heightfield.DownsampledRect = Heightfield.Rect / Heightfield.DownsampleFactor;
Heightfield.Rect.Min = FIntPoint::DivideAndRoundDown(Heightfield.Rect.Min, Heightfield.DownsampleFactor) * Heightfield.DownsampleFactor;
ExistingAtlas->InitializeForSize(Heightfield.DownsampledRect.Size());
for (TMap<FHeightfieldComponentTextures, TArray<FHeightfieldComponentDescription>>::TIterator It(Heightfield.ComponentDescriptions); It; ++It)
{
TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions = It.Value();
for (int32 ComponentIndex = 0; ComponentIndex < HeightfieldDescriptions.Num(); ComponentIndex++)
{
FIntPoint RelativeAtlasOffset = HeightfieldDescriptions[ComponentIndex].HeightfieldRect.Min - Heightfield.Rect.Min;
HeightfieldDescriptions[ComponentIndex].LightingAtlasLocation = FVector2D(RelativeAtlasOffset.X, RelativeAtlasOffset.Y) / Heightfield.DownsampleFactor;
}
}
{
SCOPED_DRAW_EVENT(RHICmdList, InitializeHeightfield);
const FIntPoint LightingAtlasSize = ExistingAtlas->GetAtlasSize();
const FVector2D InvLightingAtlasSize(1.0f / LightingAtlasSize.X, 1.0f / LightingAtlasSize.Y);
FRHITexture* RenderTargets[3] =
{
ExistingAtlas->Height->GetRenderTargetItem().TargetableTexture,
ExistingAtlas->Normal->GetRenderTargetItem().TargetableTexture,
ExistingAtlas->DiffuseColor->GetRenderTargetItem().TargetableTexture
};
RHICmdList.SetViewport(0, 0, 0.0f, LightingAtlasSize.X, LightingAtlasSize.Y, 1.0f);
FRHIRenderPassInfo RPInfo(UE_ARRAY_COUNT(RenderTargets), RenderTargets, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("SetupHeightfields"));
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
RHICmdList.SetStreamSource(0, GQuadVertexBuffer.VertexBufferRHI, 0);
TShaderMapRef<FHeightfieldSubsectionQuadVS> VertexShader(View.ShaderMap);
TShaderMapRef<FInitializeHeightfieldsPS> PixelShader(View.ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
for (TMap<FHeightfieldComponentTextures, TArray<FHeightfieldComponentDescription>>::TConstIterator It(Heightfield.ComponentDescriptions); It; ++It)
{
const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions = It.Value();
if (HeightfieldDescriptions.Num() > 0)
{
const int32 NumQuads = UploadSubsectionHeightfieldDescriptions(HeightfieldDescriptions, InvLightingAtlasSize, 1.0f / Heightfield.DownsampleFactor);
VertexShader->SetParameters(RHICmdList, View);
PixelShader->SetParameters(RHICmdList, View, It.Key().HeightAndNormal, It.Key().DiffuseColor);
RHICmdList.DrawPrimitive(0, 2, NumQuads);
}
}
}
RHICmdList.EndRenderPass();
TArray<FRHITransitionInfo, TInlineAllocator<3>> SRVTransitions;
for (FRHITexture* RHITexture : RenderTargets)
{
SRVTransitions.Add(FRHITransitionInfo(RHITexture, ERHIAccess::Unknown, ERHIAccess::SRVMask));
}
RHICmdList.Transition(MakeArrayView(SRVTransitions.GetData(), SRVTransitions.Num()));
}
}
}
});
}
void FHeightfieldLightingViewInfo::SetupHeightfieldsForScene(const FScene& Scene)
{
const int32 NumPrimitives = Scene.DistanceFieldSceneData.HeightfieldPrimitives.Num();
if (NumPrimitives > 0
&& SupportsHeightfieldLighting(Scene.GetFeatureLevel(), Scene.GetShaderPlatform()))
{
const float MaxDistanceSquared = FMath::Square(GetMaxAOViewDistance() + GetGHeightfieldBounceDistance());
float LocalToWorldScale = 1;
for (int32 HeightfieldPrimitiveIndex = 0; HeightfieldPrimitiveIndex < NumPrimitives; HeightfieldPrimitiveIndex++)
{
const FPrimitiveSceneInfo* HeightfieldPrimitive = Scene.DistanceFieldSceneData.HeightfieldPrimitives[HeightfieldPrimitiveIndex];
UTexture2D* HeightfieldTexture = NULL;
UTexture2D* DiffuseColorTexture = NULL;
UTexture2D* VisibilityTexture = NULL;
FHeightfieldComponentDescription NewComponentDescription(HeightfieldPrimitive->Proxy->GetLocalToWorld());
HeightfieldPrimitive->Proxy->GetHeightfieldRepresentation(HeightfieldTexture, DiffuseColorTexture, VisibilityTexture, NewComponentDescription);
if (HeightfieldTexture && HeightfieldTexture->GetResource()->TextureRHI)
{
const FIntPoint HeightfieldSize = NewComponentDescription.HeightfieldRect.Size();
if (Heightfield.Rect.Area() == 0)
{
Heightfield.Rect = NewComponentDescription.HeightfieldRect;
LocalToWorldScale = NewComponentDescription.LocalToWorld.GetScaleVector().X;
}
else
{
Heightfield.Rect.Union(NewComponentDescription.HeightfieldRect);
}
TArray<FHeightfieldComponentDescription>& ComponentDescriptions = Heightfield.ComponentDescriptions.FindOrAdd(FHeightfieldComponentTextures(HeightfieldTexture, DiffuseColorTexture, VisibilityTexture));
ComponentDescriptions.Add(NewComponentDescription);
}
}
}
}
class FHeightfieldDescriptionsResource : public FRenderResource
{
public:
FCPUUpdatedBuffer Data;
FHeightfieldDescriptionsResource()
{
// In float4's, must match usf
const int32 HEIGHTFIELD_DATA_STRIDE = 12;
Data.Format = PF_A32B32G32R32F;
Data.Stride = HEIGHTFIELD_DATA_STRIDE;
}
virtual void InitDynamicRHI() override
{
Data.Initialize();
}
virtual void ReleaseDynamicRHI() override
{
Data.Release();
}
};
TGlobalResource<FHeightfieldDescriptionsResource> GHeightfieldDescriptions;
FRHIShaderResourceView* GetHeightfieldDescriptionsSRV()
{
return GHeightfieldDescriptions.Data.BufferSRV;
}
void FillHeightfieldDescriptionData(const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions,
FVector2D InvLightingAtlasSize,
float InvDownsampleFactor,
TArray<FVector4, SceneRenderingAllocator>& HeightfieldDescriptionData)
{
HeightfieldDescriptionData.Empty(HeightfieldDescriptions.Num() * GHeightfieldDescriptions.Data.Stride);
for (int32 DescriptionIndex = 0; DescriptionIndex < HeightfieldDescriptions.Num(); DescriptionIndex++)
{
const FHeightfieldComponentDescription& Description = HeightfieldDescriptions[DescriptionIndex];
FVector4 HeightfieldScaleBias = Description.HeightfieldScaleBias;
check(HeightfieldScaleBias.X > 0);
// CalculateHeightfieldOcclusionCS needs to be fixed up if other values are ever supported
check(Description.NumSubsections == 1 || Description.NumSubsections == 2);
// Store the presence of subsections in the sign bit
HeightfieldScaleBias.X *= Description.NumSubsections > 1 ? -1 : 1;
HeightfieldDescriptionData.Add(HeightfieldScaleBias);
HeightfieldDescriptionData.Add(Description.MinMaxUV);
const FVector4 LightingUVScaleBias(
InvLightingAtlasSize.X * InvDownsampleFactor,
InvLightingAtlasSize.Y * InvDownsampleFactor,
Description.LightingAtlasLocation.X * InvLightingAtlasSize.X,
Description.LightingAtlasLocation.Y * InvLightingAtlasSize.Y);
HeightfieldDescriptionData.Add(LightingUVScaleBias);
HeightfieldDescriptionData.Add(FVector4(Description.HeightfieldRect.Size().X, Description.HeightfieldRect.Size().Y, 1.f / Description.HeightfieldRect.Size().X, 1.f / Description.HeightfieldRect.Size().Y));
HeightfieldDescriptionData.Add(FVector4(InvLightingAtlasSize.X, InvLightingAtlasSize.Y, 0.f, 0.f));
const FMatrix LocalToWorldT = Description.LocalToWorld.GetTransposed();
const FMatrix WorldToLocalT = Description.LocalToWorld.Inverse().GetTransposed();
HeightfieldDescriptionData.Add(*(FVector4*)&WorldToLocalT.M[0]);
HeightfieldDescriptionData.Add(*(FVector4*)&WorldToLocalT.M[1]);
HeightfieldDescriptionData.Add(*(FVector4*)&WorldToLocalT.M[2]);
HeightfieldDescriptionData.Add(*(FVector4*)&LocalToWorldT.M[0]);
HeightfieldDescriptionData.Add(*(FVector4*)&LocalToWorldT.M[1]);
HeightfieldDescriptionData.Add(*(FVector4*)&LocalToWorldT.M[2]);
FVector4 ChannelMask(0.f, 0.f, 0.f, 0.f);
if (Description.VisibilityChannel >= 0 && Description.VisibilityChannel < 4)
{
ChannelMask.Component(Description.VisibilityChannel) = 1.f;
}
HeightfieldDescriptionData.Add(ChannelMask);
}
check(HeightfieldDescriptionData.Num() % GHeightfieldDescriptions.Data.Stride == 0);
}
void UploadHeightfieldDescriptions(const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions, FVector2D InvLightingAtlasSize, float InvDownsampleFactor)
{
TArray<FVector4, SceneRenderingAllocator> HeightfieldDescriptionData;
FillHeightfieldDescriptionData(HeightfieldDescriptions,
InvLightingAtlasSize,
InvDownsampleFactor,
/*out*/ HeightfieldDescriptionData);
if (HeightfieldDescriptionData.Num() > GHeightfieldDescriptions.Data.MaxElements)
{
GHeightfieldDescriptions.Data.MaxElements = HeightfieldDescriptionData.Num() * 5 / 4;
GHeightfieldDescriptions.Data.Release();
GHeightfieldDescriptions.Data.Initialize();
}
void* LockedBuffer = RHILockBuffer(GHeightfieldDescriptions.Data.Buffer, 0, GHeightfieldDescriptions.Data.Buffer->GetSize(), RLM_WriteOnly);
const uint32 MemcpySize = HeightfieldDescriptionData.GetTypeSize() * HeightfieldDescriptionData.Num();
check(GHeightfieldDescriptions.Data.Buffer->GetSize() >= MemcpySize);
FPlatformMemory::Memcpy(LockedBuffer, HeightfieldDescriptionData.GetData(), MemcpySize);
RHIUnlockBuffer(GHeightfieldDescriptions.Data.Buffer);
}
FRDGBufferRef UploadHeightfieldDescriptions(FRDGBuilder& GraphBuilder, const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions, FVector2D InvLightingAtlasSize, float InvDownsampleFactor)
{
const uint32 BufferStride = GHeightfieldDescriptions.Data.Stride;
TArray<FVector4, SceneRenderingAllocator> HeightfieldDescriptionData;
FillHeightfieldDescriptionData(HeightfieldDescriptions,
InvLightingAtlasSize,
InvDownsampleFactor,
/*out*/ HeightfieldDescriptionData);
FRDGBufferRef HeightfieldDescriptionsBuffer =
CreateUploadBuffer(GraphBuilder, TEXT("HeightfieldDescriptionsBuffer"),
sizeof(FVector4), FMath::RoundUpToPowerOfTwo(FMath::Max(HeightfieldDescriptionData.Num(), 1)),
HeightfieldDescriptionData.GetData(), HeightfieldDescriptionData.Num() * HeightfieldDescriptionData.GetTypeSize());
return HeightfieldDescriptionsBuffer;
}
class FGlobalHeightfieldParameters
{
DECLARE_INLINE_TYPE_LAYOUT(FGlobalHeightfieldParameters, NonVirtual);
public:
void Bind(const FShaderParameterMap& ParameterMap)
{
GlobalHeightfieldTexture.Bind(ParameterMap, TEXT("GlobalHeightfieldTexture"));
GlobalNormalTexture.Bind(ParameterMap, TEXT("GlobalNormalTexture"));
GlobalDiffuseColorTexture.Bind(ParameterMap, TEXT("GlobalDiffuseColorTexture"));
GlobalHeightfieldSampler.Bind(ParameterMap, TEXT("GlobalHeightfieldSampler"));
}
friend FArchive& operator<<(FArchive& Ar, FGlobalHeightfieldParameters& Parameters)
{
Ar << Parameters.GlobalHeightfieldTexture;
Ar << Parameters.GlobalNormalTexture;
Ar << Parameters.GlobalDiffuseColorTexture;
Ar << Parameters.GlobalHeightfieldSampler;
return Ar;
}
template<typename ShaderRHIParamRef>
void Set(FRHICommandList& RHICmdList, const ShaderRHIParamRef ShaderRHI, const FHeightfieldLightingAtlas& Atlas)
{
SetTextureParameter(RHICmdList, ShaderRHI, GlobalHeightfieldTexture, GlobalHeightfieldSampler, TStaticSamplerState<SF_Bilinear>::GetRHI(), Atlas.Height->GetRenderTargetItem().ShaderResourceTexture);
SetTextureParameter(RHICmdList, ShaderRHI, GlobalNormalTexture, GlobalHeightfieldSampler, TStaticSamplerState<SF_Bilinear>::GetRHI(), Atlas.Normal->GetRenderTargetItem().ShaderResourceTexture);
SetTextureParameter(RHICmdList, ShaderRHI, GlobalDiffuseColorTexture, GlobalHeightfieldSampler, TStaticSamplerState<SF_Bilinear>::GetRHI(), Atlas.DiffuseColor->GetRenderTargetItem().ShaderResourceTexture);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, GlobalHeightfieldTexture)
LAYOUT_FIELD(FShaderResourceParameter, GlobalNormalTexture)
LAYOUT_FIELD(FShaderResourceParameter, GlobalDiffuseColorTexture)
LAYOUT_FIELD(FShaderResourceParameter, GlobalHeightfieldSampler)
};
class FHeightfieldComponentQuadVS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FHeightfieldComponentQuadVS, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Parameters.Platform);
}
/** Default constructor. */
FHeightfieldComponentQuadVS() {}
/** Initialization constructor. */
FHeightfieldComponentQuadVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
HeightfieldDescriptionParameters.Bind(Initializer.ParameterMap);
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, int32 NumHeightfieldsValue)
{
FRHIVertexShader* ShaderRHI = RHICmdList.GetBoundVertexShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
HeightfieldDescriptionParameters.Set(RHICmdList, ShaderRHI, GetHeightfieldDescriptionsSRV(), NumHeightfieldsValue);
}
private:
LAYOUT_FIELD(FHeightfieldDescriptionParameters, HeightfieldDescriptionParameters)
};
IMPLEMENT_SHADER_TYPE(, FHeightfieldComponentQuadVS, TEXT("/Engine/Private/HeightfieldLighting.usf"), TEXT("HeightfieldComponentQuadVS"), SF_Vertex);
class FShadowHeightfieldsPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FShadowHeightfieldsPS, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Parameters.Platform);
}
/** Default constructor. */
FShadowHeightfieldsPS() {}
/** Initialization constructor. */
FShadowHeightfieldsPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
HeightfieldDescriptionParameters.Bind(Initializer.ParameterMap);
GlobalHeightfieldParameters.Bind(Initializer.ParameterMap);
WorldToShadow.Bind(Initializer.ParameterMap, TEXT("WorldToShadow"));
ShadowmapMinMax.Bind(Initializer.ParameterMap, TEXT("ShadowmapMinMax"));
ShadowDepthBias.Bind(Initializer.ParameterMap, TEXT("ShadowDepthBias"));
CascadeDepthMinMax.Bind(Initializer.ParameterMap, TEXT("CascadeDepthMinMax"));
ShadowDepthTexture.Bind(Initializer.ParameterMap, TEXT("ShadowDepthTexture"));
ShadowDepthTextureSampler.Bind(Initializer.ParameterMap, TEXT("ShadowDepthTextureSampler"));
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, const FProjectedShadowInfo* ProjectedShadowInfo, int32 NumHeightfieldsValue, const FHeightfieldLightingAtlas& Atlas)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
HeightfieldDescriptionParameters.Set(RHICmdList, ShaderRHI, GetHeightfieldDescriptionsSRV(), NumHeightfieldsValue);
GlobalHeightfieldParameters.Set(RHICmdList, ShaderRHI, Atlas);
FVector4 ShadowmapMinMaxValue;
FMatrix44f WorldToShadowMatrixValue = ProjectedShadowInfo->GetWorldToShadowMatrix(ShadowmapMinMaxValue);
SetShaderValue(RHICmdList, ShaderRHI, WorldToShadow, WorldToShadowMatrixValue);
SetShaderValue(RHICmdList, ShaderRHI, ShadowmapMinMax, ShadowmapMinMaxValue);
SetShaderValue(RHICmdList, ShaderRHI, ShadowDepthBias, ProjectedShadowInfo->GetShaderDepthBias());
SetShaderValue(RHICmdList, ShaderRHI, CascadeDepthMinMax, FVector2D(ProjectedShadowInfo->CascadeSettings.SplitNear, ProjectedShadowInfo->CascadeSettings.SplitFar));
FRHITexture* ShadowDepthTextureValue = ProjectedShadowInfo->RenderTargets.DepthTarget->GetRenderTargetItem().ShaderResourceTexture.GetReference();
FRHISamplerState* DepthSamplerState = TStaticSamplerState<SF_Point, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
SetTextureParameter(RHICmdList, ShaderRHI, ShadowDepthTexture, ShadowDepthTextureSampler, DepthSamplerState, ShadowDepthTextureValue);
}
private:
LAYOUT_FIELD(FHeightfieldDescriptionParameters, HeightfieldDescriptionParameters);
LAYOUT_FIELD(FGlobalHeightfieldParameters, GlobalHeightfieldParameters);
LAYOUT_FIELD(FShaderParameter, WorldToShadow);
LAYOUT_FIELD(FShaderParameter, ShadowmapMinMax);
LAYOUT_FIELD(FShaderParameter, ShadowDepthBias);
LAYOUT_FIELD(FShaderParameter, CascadeDepthMinMax);
LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTexture);
LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTextureSampler);
};
IMPLEMENT_SHADER_TYPE(, FShadowHeightfieldsPS, TEXT("/Engine/Private/HeightfieldLighting.usf"), TEXT("ShadowHeightfieldsPS"), SF_Pixel);
void FHeightfieldLightingViewInfo::ClearShadowing(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FLightSceneInfo& LightSceneInfo) const
{
if (AllowHeightfieldGI(View)
&& SupportsHeightfieldLighting(View.GetFeatureLevel(), View.GetShaderPlatform())
&& Heightfield.ComponentDescriptions.Num() > 0
&& LightSceneInfo.Proxy->GetLightType() == LightType_Directional
&& LightSceneInfo.Proxy->CastsDynamicShadow())
{
// TODO(RDG): Port this to RDG.
AddPass(GraphBuilder, [this, &View, &LightSceneInfo](FRHICommandList& RHICmdList)
{
FSceneViewState* ViewState = (FSceneViewState*)View.State;
const FHeightfieldLightingAtlas& Atlas = *ViewState->HeightfieldLightingAtlas;
FRHIRenderPassInfo RPInfo(Atlas.DirectionalLightShadowing->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("ClearShadowing"));
RHICmdList.EndRenderPass();
});
}
}
void FHeightfieldLightingViewInfo::ComputeShadowMapShadowing(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FProjectedShadowInfo* ProjectedShadowInfo) const
{
if (AllowHeightfieldGI(View)
&& SupportsHeightfieldLighting(View.GetFeatureLevel(), View.GetShaderPlatform())
&& Heightfield.ComponentDescriptions.Num() > 0
&& ProjectedShadowInfo->IsWholeSceneDirectionalShadow()
&& ProjectedShadowInfo->DependentView == &View
&& !ProjectedShadowInfo->bRayTracedDistanceField)
{
// TODO(RDG): Port this to RDG.
AddPass(GraphBuilder, RDG_EVENT_NAME("HeightfieldShadowMapShadowingForGI"), [this, &View, ProjectedShadowInfo](FRHICommandListImmediate& RHICmdList)
{
FSceneViewState* ViewState = (FSceneViewState*)View.State;
const FHeightfieldLightingAtlas& Atlas = *ViewState->HeightfieldLightingAtlas;
const FIntPoint LightingAtlasSize = Atlas.GetAtlasSize();
const FVector2D InvLightingAtlasSize(1.0f / LightingAtlasSize.X, 1.0f / LightingAtlasSize.Y);
FRHIRenderPassInfo RPInfo(Atlas.DirectionalLightShadowing->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Load_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("ComputeShadowMapShadowing"));
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
RHICmdList.SetViewport(0, 0, 0.0f, LightingAtlasSize.X, LightingAtlasSize.Y, 1.0f);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
// Combine with other shadow types with min (ray traced)
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RED, BO_Min, BF_One, BF_One>::GetRHI();
RHICmdList.SetStreamSource(0, GQuadVertexBuffer.VertexBufferRHI, 0);
TShaderMapRef<FHeightfieldComponentQuadVS> VertexShader(View.ShaderMap);
TShaderMapRef<FShadowHeightfieldsPS> PixelShader(View.ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
for (TMap<FHeightfieldComponentTextures, TArray<FHeightfieldComponentDescription>>::TConstIterator It(Heightfield.ComponentDescriptions); It; ++It)
{
const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions = It.Value();
if (HeightfieldDescriptions.Num() > 0)
{
//@todo - cull heightfield tiles with shadow bounds
UploadHeightfieldDescriptions(HeightfieldDescriptions, InvLightingAtlasSize, 1.0f / Heightfield.DownsampleFactor);
VertexShader->SetParameters(RHICmdList, View, HeightfieldDescriptions.Num());
PixelShader->SetParameters(RHICmdList, View, ProjectedShadowInfo, HeightfieldDescriptions.Num(), Atlas);
RHICmdList.DrawPrimitive(0, 2, HeightfieldDescriptions.Num());
}
}
}
RHICmdList.EndRenderPass();
});
}
}
class FLightHeightfieldsPS : public FMaterialShader
{
DECLARE_SHADER_TYPE(FLightHeightfieldsPS, Material);
public:
static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters)
{
return Parameters.MaterialParameters.MaterialDomain == MD_LightFunction && IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FMaterialShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("APPLY_LIGHT_FUNCTION"), 1);
}
/** Default constructor. */
FLightHeightfieldsPS() {}
/** Initialization constructor. */
FLightHeightfieldsPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
HeightfieldDescriptionParameters.Bind(Initializer.ParameterMap);
GlobalHeightfieldParameters.Bind(Initializer.ParameterMap);
LightDirection.Bind(Initializer.ParameterMap, TEXT("LightDirection"));
LightColor.Bind(Initializer.ParameterMap, TEXT("LightColor"));
SkyLightIndirectScale.Bind(Initializer.ParameterMap, TEXT("SkyLightIndirectScale"));
HeightfieldShadowing.Bind(Initializer.ParameterMap, TEXT("HeightfieldShadowing"));
HeightfieldShadowingSampler.Bind(Initializer.ParameterMap, TEXT("HeightfieldShadowingSampler"));
WorldToLight.Bind(Initializer.ParameterMap, TEXT("WorldToLight"));
LightFunctionParameters.Bind(Initializer.ParameterMap);
}
void SetParameters(
FRHICommandList& RHICmdList,
const FSceneView& View,
const FLightSceneInfo& LightSceneInfo,
const FMaterialRenderProxy* MaterialProxy,
int32 NumHeightfieldsValue,
const FHeightfieldLightingAtlas& Atlas,
float SkyLightIndirectScaleValue)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
FMaterialShader::SetViewParameters(RHICmdList, ShaderRHI, View, View.ViewUniformBuffer);
FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, Material, View);
HeightfieldDescriptionParameters.Set(RHICmdList, ShaderRHI, GetHeightfieldDescriptionsSRV(), NumHeightfieldsValue);
GlobalHeightfieldParameters.Set(RHICmdList, ShaderRHI, Atlas);
SetShaderValue(RHICmdList, ShaderRHI, LightDirection, FVector3f(LightSceneInfo.Proxy->GetDirection()));
SetShaderValue(RHICmdList, ShaderRHI, LightColor, LightSceneInfo.Proxy->GetColor() * LightSceneInfo.Proxy->GetIndirectLightingScale());
SetShaderValue(RHICmdList, ShaderRHI, SkyLightIndirectScale, SkyLightIndirectScaleValue);
const FSceneViewState* ViewState = (const FSceneViewState*)View.State;
SetTextureParameter(RHICmdList, ShaderRHI, HeightfieldShadowing, HeightfieldShadowingSampler, TStaticSamplerState<SF_Bilinear>::GetRHI(), Atlas.DirectionalLightShadowing->GetRenderTargetItem().ShaderResourceTexture);
const FVector Scale = LightSceneInfo.Proxy->GetLightFunctionScale();
// Switch x and z so that z of the user specified scale affects the distance along the light direction
const FVector InverseScale = FVector(1.f / Scale.Z, 1.f / Scale.Y, 1.f / Scale.X);
const FMatrix44f WorldToLightValue = LightSceneInfo.Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale));
SetShaderValue(RHICmdList, ShaderRHI, WorldToLight, WorldToLightValue);
LightFunctionParameters.Set(RHICmdList, ShaderRHI, &LightSceneInfo, 1);
}
private:
LAYOUT_FIELD(FHeightfieldDescriptionParameters, HeightfieldDescriptionParameters);
LAYOUT_FIELD(FGlobalHeightfieldParameters, GlobalHeightfieldParameters);
LAYOUT_FIELD(FShaderParameter, LightDirection);
LAYOUT_FIELD(FShaderParameter, LightColor);
LAYOUT_FIELD(FShaderParameter, SkyLightIndirectScale);
LAYOUT_FIELD(FShaderResourceParameter, HeightfieldShadowing);
LAYOUT_FIELD(FShaderResourceParameter, HeightfieldShadowingSampler);
LAYOUT_FIELD(FShaderParameter, WorldToLight);
LAYOUT_FIELD(FLightFunctionSharedParameters, LightFunctionParameters);
};
IMPLEMENT_MATERIAL_SHADER_TYPE(, FLightHeightfieldsPS, TEXT("/Engine/Private/HeightfieldLighting.usf"), TEXT("LightHeightfieldsPS"), SF_Pixel);
void FHeightfieldLightingViewInfo::ComputeLighting(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FLightSceneInfo& LightSceneInfo) const
{
const ERHIFeatureLevel::Type FeatureLevel = View.GetFeatureLevel();
if (!AllowHeightfieldGI(View)
|| !SupportsHeightfieldLighting(FeatureLevel, View.GetShaderPlatform())
//@todo - handle local lights
|| LightSceneInfo.Proxy->GetLightType() != LightType_Directional
|| !LightSceneInfo.Proxy->CastsDynamicShadow()
|| Heightfield.ComponentDescriptions.Num() == 0)
{
return;
}
AddPass(GraphBuilder, RDG_EVENT_NAME("HeightfieldLightingForGI"), [this, &View, &LightSceneInfo, FeatureLevel](FRHICommandListImmediate& RHICmdList)
{
FSceneViewState* ViewState = (FSceneViewState*)View.State;
const FHeightfieldLightingAtlas& Atlas = *ViewState->HeightfieldLightingAtlas;
const FIntPoint LightingAtlasSize = Atlas.GetAtlasSize();
const FVector2D InvLightingAtlasSize(1.0f / LightingAtlasSize.X, 1.0f / LightingAtlasSize.Y);
FRHIRenderPassInfo RPInfo(Atlas.Lighting->GetRenderTargetItem().TargetableTexture, ERenderTargetActions::Clear_Store);
RHICmdList.BeginRenderPass(RPInfo, TEXT("ComputeHeightfieldLighting"));
{
const bool bApplyLightFunction = (View.Family->EngineShowFlags.LightFunctions &&
LightSceneInfo.Proxy->GetLightFunctionMaterial() &&
LightSceneInfo.Proxy->GetLightFunctionMaterial()->GetIncompleteMaterialWithFallback(FeatureLevel).IsLightFunction());
const FMaterialRenderProxy* MaterialProxy = bApplyLightFunction ?
LightSceneInfo.Proxy->GetLightFunctionMaterial() :
UMaterial::GetDefaultMaterial(MD_LightFunction)->GetRenderProxy();
const FScene* Scene = (const FScene*)View.Family->Scene;
const float SkyLightIndirectScale = ShouldRenderDeferredDynamicSkyLight(Scene, *View.Family) ? Scene->SkyLight->IndirectLightingIntensity : 0;
// Skip rendering if the DefaultLightFunctionMaterial isn't compiled yet
if (MaterialProxy->GetIncompleteMaterialWithFallback(FeatureLevel).IsLightFunction())
{
RHICmdList.SetViewport(0, 0, 0.0f, LightingAtlasSize.X, LightingAtlasSize.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
RHICmdList.SetStreamSource(0, GQuadVertexBuffer.VertexBufferRHI, 0);
TShaderMapRef<FHeightfieldComponentQuadVS> VertexShader(View.ShaderMap);
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback(FeatureLevel, MaterialProxy);
const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap();
TShaderRef<FLightHeightfieldsPS> PixelShader = MaterialShaderMap->GetShader<FLightHeightfieldsPS>();
for (TMap<FHeightfieldComponentTextures, TArray<FHeightfieldComponentDescription>>::TConstIterator It(Heightfield.ComponentDescriptions); It; ++It)
{
const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions = It.Value();
if (HeightfieldDescriptions.Num() > 0)
{
UploadHeightfieldDescriptions(HeightfieldDescriptions, InvLightingAtlasSize, 1.0f / Heightfield.DownsampleFactor);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GScreenVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
VertexShader->SetParameters(RHICmdList, View, HeightfieldDescriptions.Num());
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, HeightfieldDescriptions.Num(), Atlas, SkyLightIndirectScale);
RHICmdList.DrawPrimitive(0, 2, HeightfieldDescriptions.Num());
}
}
}
}
RHICmdList.EndRenderPass();
});
}
const int32 GHeightfieldOcclusionDispatchSize = 8;
class FCalculateHeightfieldOcclusionScreenGridCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCalculateHeightfieldOcclusionScreenGridCS);
public:
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FAOScreenGridParameters, AOScreenGridParameters)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
RDG_TEXTURE_ACCESS(DistanceFieldNormal, ERHIAccess::SRVCompute)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportDistanceFieldGI(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("HEIGHTFIELD_OCCLUSION_DISPATCH_SIZEX"), GHeightfieldOcclusionDispatchSize);
extern int32 GConeTraceDownsampleFactor;
OutEnvironment.SetDefine(TEXT("TRACE_DOWNSAMPLE_FACTOR"), GConeTraceDownsampleFactor);
// To reduce shader compile time of compute shaders with shared memory, doesn't have an impact on generated code with current compiler (June 2010 DX SDK)
OutEnvironment.CompilerFlags.Add(CFLAG_StandardOptimization);
}
FCalculateHeightfieldOcclusionScreenGridCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
BindForLegacyShaderParameters<FParameters>(this, Initializer.PermutationId, Initializer.ParameterMap, false);
AOParameters.Bind(Initializer.ParameterMap);
ScreenGridParameters.Bind(Initializer.ParameterMap);
HeightfieldDescriptionParameters.Bind(Initializer.ParameterMap);
HeightfieldTextureParameters.Bind(Initializer.ParameterMap);
TanConeHalfAngle.Bind(Initializer.ParameterMap, TEXT("TanConeHalfAngle"));
}
FCalculateHeightfieldOcclusionScreenGridCS()
{
}
void SetParameters(
FRHICommandList& RHICmdList,
const FViewInfo& View,
UTexture2D* HeightfieldTextureValue,
int32 NumHeightfieldsValue,
FRHITexture* DistanceFieldNormal,
const FDistanceFieldAOParameters& Parameters)
{
FRHIComputeShader* ShaderRHI = RHICmdList.GetBoundComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
AOParameters.Set(RHICmdList, ShaderRHI, Parameters);
ScreenGridParameters.Set(RHICmdList, ShaderRHI, View, DistanceFieldNormal);
HeightfieldDescriptionParameters.Set(RHICmdList, ShaderRHI, GetHeightfieldDescriptionsSRV(), NumHeightfieldsValue);
HeightfieldTextureParameters.Set(RHICmdList, ShaderRHI, HeightfieldTextureValue, nullptr, nullptr);
FAOSampleData2 AOSampleData;
TArray<FVector, TInlineAllocator<9> > SampleDirections;
GetSpacedVectors(View.Family->FrameNumber, SampleDirections);
for (int32 SampleIndex = 0; SampleIndex < NumConeSampleDirections; SampleIndex++)
{
AOSampleData.SampleDirections[SampleIndex] = FVector4(SampleDirections[SampleIndex]);
}
SetUniformBufferParameterImmediate(RHICmdList, ShaderRHI, GetUniformBufferParameter<FAOSampleData2>(), AOSampleData);
extern float GAOConeHalfAngle;
SetShaderValue(RHICmdList, ShaderRHI, TanConeHalfAngle, FMath::Tan(GAOConeHalfAngle));
}
private:
LAYOUT_FIELD(FAOParameters, AOParameters);
LAYOUT_FIELD(FScreenGridParameters, ScreenGridParameters);
LAYOUT_FIELD(FHeightfieldDescriptionParameters, HeightfieldDescriptionParameters);
LAYOUT_FIELD(FHeightfieldTextureParameters, HeightfieldTextureParameters);
LAYOUT_FIELD(FShaderParameter, TanConeHalfAngle);
};
IMPLEMENT_GLOBAL_SHADER(FCalculateHeightfieldOcclusionScreenGridCS, "/Engine/Private/HeightfieldLighting.usf", "CalculateHeightfieldOcclusionScreenGridCS", SF_Compute);
void FHeightfieldLightingViewInfo::ComputeOcclusionForScreenGrid(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FSceneTextures& SceneTextures,
FRDGTextureRef DistanceFieldNormal,
const FAOScreenGridParameters& AOScreenGridParameters,
const FDistanceFieldAOParameters& Parameters) const
{
int32 DownsampleFactor = Heightfield.DownsampleFactor;
if (Heightfield.ComponentDescriptions.Num() > 0 && GAOHeightfieldOcclusion)
{
for (TMap<FHeightfieldComponentTextures, TArray<FHeightfieldComponentDescription>>::TConstIterator It(Heightfield.ComponentDescriptions); It; ++It)
{
UTexture2D* HeightfieldTexture = It.Key().HeightAndNormal;
const TArray<FHeightfieldComponentDescription>& HeightfieldDescriptions = It.Value();
if (HeightfieldDescriptions.Num() > 0)
{
auto* PassParameters = GraphBuilder.AllocParameters<FCalculateHeightfieldOcclusionScreenGridCS::FParameters>();
PassParameters->AOScreenGridParameters = AOScreenGridParameters;
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
PassParameters->DistanceFieldNormal = DistanceFieldNormal;
auto ComputeShader = View.ShaderMap->GetShader<FCalculateHeightfieldOcclusionScreenGridCS>();
ClearUnusedGraphResources(ComputeShader, PassParameters);
GraphBuilder.AddPass(
RDG_EVENT_NAME("HeightfieldOcclusion"),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, ComputeShader, &View, Parameters, DistanceFieldNormal, &HeightfieldDescriptions, HeightfieldTexture, DownsampleFactor](FRHICommandListImmediate& RHICmdList)
{
UploadHeightfieldDescriptions(HeightfieldDescriptions, FVector2D(1, 1), 1.0f / DownsampleFactor);
const uint32 GroupSizeX = FMath::DivideAndRoundUp(View.ViewRect.Size().X / GAODownsampleFactor, GHeightfieldOcclusionDispatchSize);
const uint32 GroupSizeY = FMath::DivideAndRoundUp(View.ViewRect.Size().Y / GAODownsampleFactor, GHeightfieldOcclusionDispatchSize);
RHICmdList.SetComputeShader(ComputeShader.GetComputeShader());
ComputeShader->SetParameters(RHICmdList, View, HeightfieldTexture, HeightfieldDescriptions.Num(), DistanceFieldNormal->GetRHI(), Parameters);
SetShaderParameters(RHICmdList, ComputeShader, ComputeShader.GetComputeShader(), *PassParameters);
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), GroupSizeX, GroupSizeY, 1);
UnsetShaderUAVs(RHICmdList, ComputeShader, ComputeShader.GetComputeShader());
});
}
}
}
}