Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/VolumetricFogLightFunction.cpp
2025-05-18 13:04:45 +08:00

264 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VolumetricFogLightFunction.cpp
=============================================================================*/
#include "VolumetricFog.h"
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneUtils.h"
#include "LightFunctionRendering.h"
#include "LightRendering.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessing.h"
#include "PipelineStateCache.h"
#include "ShadowRendering.h"
float GVolumetricFogLightFunctionDirectionalLightSupersampleScale = 2.0f;
FAutoConsoleVariableRef CVarVolumetricFogLightFunctionSupersampleScale(
TEXT("r.VolumetricFog.LightFunction.DirectionalLightSupersampleScale"),
GVolumetricFogLightFunctionDirectionalLightSupersampleScale,
TEXT("Scales the slice depth distribution."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
extern int GVolumetricFogLightFunction;
static bool inline LocalLightLighFunctionsEnabled()
{
return GVolumetricFogLightFunction > 0;
}
class FVolumetricFogLightFunctionPS : public FMaterialShader
{
DECLARE_SHADER_TYPE(FVolumetricFogLightFunctionPS, Material);
class FLightType : SHADER_PERMUTATION_INT("LIGHT_TYPE", 4);
using FPermutationDomain = TShaderPermutationDomain< FLightType >;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FMatrix44f, LightFunctionTranslatedWorldToLight)
SHADER_PARAMETER(FMatrix44f, ShadowToTranslatedWorld)
SHADER_PARAMETER(FVector4f, LightFunctionParameters)
SHADER_PARAMETER(FVector3f, LightFunctionParameters2)
SHADER_PARAMETER(FVector3f, LightTranslatedWorldPosition)
SHADER_PARAMETER(FVector2f, LightFunctionTexelSize)
SHADER_PARAMETER(FVector3f, CameraRelativeLightPosition)
END_SHADER_PARAMETER_STRUCT()
FVolumetricFogLightFunctionPS() {}
FVolumetricFogLightFunctionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
Bindings.BindForLegacyShaderParameters(
this,
Initializer.PermutationId,
Initializer.ParameterMap,
*FParameters::FTypeInfo::GetStructMetadata(),
// Don't require full bindings, we use FMaterialShader::SetParameters
false);
}
static bool ShouldCompilePermutation(const FMaterialShaderPermutationParameters& Parameters)
{
return Parameters.MaterialParameters.MaterialDomain == MD_LightFunction;
}
void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FSceneView& View, const FMaterialRenderProxy* MaterialProxy)
{
const FMaterial& Material = MaterialProxy->GetMaterialWithFallback(View.GetFeatureLevel(), MaterialProxy);
FMaterialShader::SetViewParameters(BatchedParameters, View, View.ViewUniformBuffer);
FMaterialShader::SetParameters(BatchedParameters, MaterialProxy, Material, View);
}
FParameters GetParameters(
const FSceneView& View,
const FLightSceneInfo* LightSceneInfo,
const FVector2D& LightFunctionTexelSizeValue,
const FMatrix44f& ShadowToTranslatedWorldValue)
{
FParameters PS;
PS.LightFunctionParameters = FLightFunctionSharedParameters::GetLightFunctionSharedParameters(LightSceneInfo, 1.0f);
PS.LightFunctionParameters2 = FVector3f(
LightSceneInfo->Proxy->GetLightFunctionFadeDistance(),
LightSceneInfo->Proxy->GetLightFunctionDisabledBrightness(),
0.0f);
{
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 FMatrix WorldToLight = LightSceneInfo->Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale));
const FMatrix TranslatedWorldToWorld = FTranslationMatrix(-View.ViewMatrices.GetPreViewTranslation());
PS.LightFunctionTranslatedWorldToLight = FMatrix44f(TranslatedWorldToWorld * WorldToLight);
}
PS.LightFunctionTexelSize = FVector2f(LightFunctionTexelSizeValue);
PS.ShadowToTranslatedWorld = ShadowToTranslatedWorldValue;
PS.LightTranslatedWorldPosition = FVector4f(LightSceneInfo->Proxy->GetPosition() + View.ViewMatrices.GetPreViewTranslation());
PS.CameraRelativeLightPosition = GetCamRelativeLightPosition(View.ViewMatrices, *LightSceneInfo);
return PS;
}
};
IMPLEMENT_MATERIAL_SHADER_TYPE(, FVolumetricFogLightFunctionPS, TEXT("/Engine/Private/VolumetricFogLightFunction.usf"), TEXT("Main"), SF_Pixel);
BEGIN_SHADER_PARAMETER_STRUCT(FVolumetricFogLightFunctionParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void FSceneRenderer::RenderLightFunctionForVolumetricFog(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
const FSceneTextures& SceneTextures,
FIntVector VolumetricFogGridSize,
float VolumetricFogMaxDistance,
FLightSceneInfo* DirectionalLightSceneInfo,
FMatrix44f& OutDirectionalLightFunctionTranslatedWorldToShadow,
FRDGTexture*& OutDirectionalLightFunctionTexture)
{
RDG_EVENT_SCOPE(GraphBuilder, "DirectionalLightFunction");
// Estimate the resolution and the projection matrix.
FProjectedShadowInfo ProjectedShadowInfo;
FIntPoint LightFunctionResolution;
{
const FVector ViewForward = View.ViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(2);
const FVector ViewUp = View.ViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(1);
const FVector ViewRight = View.ViewMatrices.GetOverriddenTranslatedViewMatrix().GetColumn(0);
const FVector LightDirection = DirectionalLightSceneInfo->Proxy->GetDirection().GetSafeNormal();
FVector AxisWeights;
AxisWeights.X = FMath::Abs(LightDirection | ViewRight) * VolumetricFogGridSize.X;
AxisWeights.Y = FMath::Abs(LightDirection | ViewUp) * VolumetricFogGridSize.Y;
AxisWeights.Z = FMath::Abs(LightDirection | ViewForward) * VolumetricFogGridSize.Z;
const float VolumeResolutionEstimate = FMath::Max(AxisWeights.X, FMath::Max(AxisWeights.Y, AxisWeights.Z)) * GVolumetricFogLightFunctionDirectionalLightSupersampleScale;
LightFunctionResolution = FIntPoint(FMath::TruncToInt(VolumeResolutionEstimate), FMath::TruncToInt(VolumeResolutionEstimate));
// Snap the resolution to allow render target pool hits most of the time
const int32 ResolutionSnapFactor = 32;
LightFunctionResolution.X = FMath::DivideAndRoundUp(LightFunctionResolution.X, ResolutionSnapFactor) * ResolutionSnapFactor;
LightFunctionResolution.Y = FMath::DivideAndRoundUp(LightFunctionResolution.Y, ResolutionSnapFactor) * ResolutionSnapFactor;
// Guard against invalid resolutions
const uint32 LiFuncResXU32 = (uint32)LightFunctionResolution.X;
const uint32 LiFuncResYU32 = (uint32)LightFunctionResolution.Y;
const uint32 MaxTextureResU32 = GMaxTextureDimensions;
if (LiFuncResXU32 > MaxTextureResU32 || LiFuncResYU32 > MaxTextureResU32)
{
#if !(UE_BUILD_SHIPPING)
UE_LOG(LogRenderer, Error,
TEXT("Invalid LightFunctionResolution %dx%d, View={ %s}, LightDirection={ %s }"),
LightFunctionResolution.X,
LightFunctionResolution.Y,
*View.ViewMatrices.GetOverriddenTranslatedViewMatrix().ToString(),
*LightDirection.ToString());
#endif
const uint32 ClampedRes = FMath::Max(FMath::Min3(LiFuncResXU32, LiFuncResYU32, MaxTextureResU32), (uint32)ResolutionSnapFactor);
LightFunctionResolution.X = ClampedRes;
LightFunctionResolution.Y = ClampedRes;
}
FWholeSceneProjectedShadowInitializer ShadowInitializer;
check(VolumetricFogMaxDistance > 0);
FSphere Bounds = DirectionalLightSceneInfo->Proxy->GetShadowSplitBoundsDepthRange(View, View.ViewMatrices.GetViewOrigin(), 0, VolumetricFogMaxDistance, NULL);
check(Bounds.W > 0);
const float ShadowExtent = Bounds.W / FMath::Sqrt(3.0f);
const FBoxSphereBounds SubjectBounds(Bounds.Center, FVector(ShadowExtent, ShadowExtent, ShadowExtent), Bounds.W);
ShadowInitializer.PreShadowTranslation = -Bounds.Center;
ShadowInitializer.WorldToLight = FInverseRotationMatrix(LightDirection.Rotation());
ShadowInitializer.Scales = FVector2D(1.0f / Bounds.W, 1.0f / Bounds.W);
ShadowInitializer.SubjectBounds = FBoxSphereBounds(FVector::ZeroVector, SubjectBounds.BoxExtent, SubjectBounds.SphereRadius);
ShadowInitializer.WAxis = FVector4(0, 0, 0, 1);
ShadowInitializer.MinLightW = -HALF_WORLD_MAX;
// Reduce casting distance on a directional light
// This is necessary to improve floating point precision in several places, especially when deriving frustum verts from InvReceiverMatrix
ShadowInitializer.MaxDistanceToCastInLightW = HALF_WORLD_MAX / 32.0f;
ShadowInitializer.bRayTracedDistanceField = false;
ShadowInitializer.CascadeSettings.bFarShadowCascade = false;
ProjectedShadowInfo.SetupWholeSceneProjection(
DirectionalLightSceneInfo,
&View,
ShadowInitializer,
LightFunctionResolution.X,
LightFunctionResolution.Y,
LightFunctionResolution.X,
LightFunctionResolution.Y,
0
);
OutDirectionalLightFunctionTranslatedWorldToShadow = ProjectedShadowInfo.TranslatedWorldToClipInnerMatrix;
}
// Now render the texture
{
FRDGTextureDesc LightFunctionTextureDesc = FRDGTextureDesc::Create2D(LightFunctionResolution, PF_G8, FClearValueBinding::None, TexCreate_ShaderResource | TexCreate_RenderTargetable);
LightFunctionTextureDesc.Flags |= GFastVRamConfig.VolumetricFog;
FRDGTexture* LightFunctionTexture = GraphBuilder.CreateTexture(LightFunctionTextureDesc, TEXT("VolumetricFog.LightFunction"));
OutDirectionalLightFunctionTexture = LightFunctionTexture;
const FMaterialRenderProxy* MaterialProxyForRendering = DirectionalLightSceneInfo->Proxy->GetLightFunctionMaterial();
const FMaterial& Material = MaterialProxyForRendering->GetMaterialWithFallback(Scene->GetFeatureLevel(), MaterialProxyForRendering);
FVolumetricFogLightFunctionParameters* PassParameters = GraphBuilder.AllocParameters<FVolumetricFogLightFunctionParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(LightFunctionTexture, ERenderTargetLoadAction::ENoAction);
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
FMatrix44f LightFunctionTranslatedWorldToShadowMatrix = OutDirectionalLightFunctionTranslatedWorldToShadow;
GraphBuilder.AddPass(
RDG_EVENT_NAME("LightFunction %ux%u Material=%s", LightFunctionResolution.X, LightFunctionResolution.Y, *(Material.GetFriendlyName())),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, MaterialProxyForRendering, &Material, LightFunctionResolution, DirectionalLightSceneInfo, LightFunctionTranslatedWorldToShadowMatrix](FRDGAsyncTask, FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(0.f, 0.f, 0.f, LightFunctionResolution.X, LightFunctionResolution.Y, 1.f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
const FMaterialShaderMap* MaterialShaderMap = Material.GetRenderingThreadShaderMap();
TShaderMapRef<FPostProcessVS> VertexShader(View.ShaderMap);
check(DirectionalLightSceneInfo->Proxy->GetLightType() == LightType_Directional)
FVolumetricFogLightFunctionPS::FPermutationDomain PermutationVector;
PermutationVector.Set<FVolumetricFogLightFunctionPS::FLightType>(3);
TShaderRef<FVolumetricFogLightFunctionPS> PixelShader = MaterialShaderMap->GetShader<FVolumetricFogLightFunctionPS>(PermutationVector);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FVolumetricFogLightFunctionPS::FParameters PS = PixelShader->GetParameters(View, DirectionalLightSceneInfo, FVector2D(1.0f / LightFunctionResolution.X, 1.0f / LightFunctionResolution.Y), LightFunctionTranslatedWorldToShadowMatrix.Inverse());
ClearUnusedGraphResources(PixelShader, &PS);
SetShaderParametersMixedPS(RHICmdList, PixelShader, PS, View, MaterialProxyForRendering);
DrawRectangle(
RHICmdList,
0, 0,
LightFunctionResolution.X, LightFunctionResolution.Y,
0, 0,
LightFunctionResolution.X, LightFunctionResolution.Y,
LightFunctionResolution,
LightFunctionResolution,
VertexShader);
}
);
}
}