Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VolumetricFogLightFunction.cpp
marcus wassmer b259f65406 Copying //UE4/Dev-Rendering[at]4854522 to Dev-Main (//UE4/Dev-Main)
#rb none

#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: marcus.wassmer
#ROBOMERGE-SOURCE: CL 4854553 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 4854570 by marcus wassmer in Dev-VR branch]
2019-01-30 21:24:04 -05:00

249 lines
11 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VolumetricFogLightFunction.cpp
=============================================================================*/
#include "VolumetricFog.h"
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneUtils.h"
#include "LightRendering.h"
#include "SceneFilterRendering.h"
#include "PostProcessing.h"
#include "RHI/Public/PipelineStateCache.h"
float GVolumetricFogLightFunctionSupersampleScale = 2.0f;
FAutoConsoleVariableRef CVarVolumetricFogLightFunctionSupersampleScale(
TEXT("r.VolumetricFog.LightFunctionSupersampleScale"),
GVolumetricFogLightFunctionSupersampleScale,
TEXT("Scales the slice depth distribution."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
class FVolumetricFogLightFunctionPS : public FMaterialShader
{
DECLARE_SHADER_TYPE(FVolumetricFogLightFunctionPS,Material);
public:
static bool ShouldCompilePermutation(EShaderPlatform Platform, const FMaterial* Material)
{
return Material->IsLightFunction() && DoesPlatformSupportVolumetricFog(Platform);
}
FVolumetricFogLightFunctionPS() {}
FVolumetricFogLightFunctionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FMaterialShader(Initializer)
{
LightFunctionParameters.Bind(Initializer.ParameterMap);
LightFunctionWorldToLight.Bind(Initializer.ParameterMap,TEXT("LightFunctionWorldToLight"));
LightFunctionParameters2.Bind(Initializer.ParameterMap,TEXT("LightFunctionParameters2"));
LightFunctionTexelSize.Bind(Initializer.ParameterMap,TEXT("LightFunctionTexelSize"));
ShadowToWorld.Bind(Initializer.ParameterMap,TEXT("ShadowToWorld"));
}
void SetParameters(
FRHICommandList& RHICmdList,
const FSceneView& View,
const FLightSceneInfo* LightSceneInfo,
const FMaterialRenderProxy* MaterialProxy,
FVector2D LightFunctionTexelSizeValue,
const FMatrix& ShadowToWorldValue)
{
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, *MaterialProxy->GetMaterial(View.GetFeatureLevel()), View, View.ViewUniformBuffer, ESceneTextureSetupMode::None);
LightFunctionParameters.Set(RHICmdList, ShaderRHI, LightSceneInfo, 1.0f);
SetShaderValue(RHICmdList, ShaderRHI, LightFunctionParameters2, FVector(
LightSceneInfo->Proxy->GetLightFunctionFadeDistance(),
LightSceneInfo->Proxy->GetLightFunctionDisabledBrightness(),
0.0f));
if (LightFunctionWorldToLight.IsBound())
{
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));
SetShaderValue(RHICmdList, ShaderRHI, LightFunctionWorldToLight, WorldToLight);
}
SetShaderValue(RHICmdList, ShaderRHI, LightFunctionTexelSize, LightFunctionTexelSizeValue);
SetShaderValue(RHICmdList, ShaderRHI, ShadowToWorld, ShadowToWorldValue);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FMaterialShader::Serialize(Ar);
Ar << LightFunctionParameters;
Ar << LightFunctionParameters2;
Ar << LightFunctionWorldToLight;
Ar << LightFunctionTexelSize;
Ar << ShadowToWorld;
return bShaderHasOutdatedParameters;
}
private:
FLightFunctionSharedParameters LightFunctionParameters;
FShaderParameter LightFunctionParameters2;
FShaderParameter LightFunctionWorldToLight;
FShaderParameter LightFunctionTexelSize;
FShaderParameter ShadowToWorld;
};
IMPLEMENT_MATERIAL_SHADER_TYPE(,FVolumetricFogLightFunctionPS,TEXT("/Engine/Private/VolumetricFogLightFunction.usf"),TEXT("Main"),SF_Pixel);
void FDeferredShadingSceneRenderer::RenderLightFunctionForVolumetricFog(
FRDGBuilder& GraphBuilder,
FViewInfo& View,
FIntVector VolumetricFogGridSize,
float VolumetricFogMaxDistance,
FMatrix& OutLightFunctionWorldToShadow,
const FRDGTexture*& OutLightFunctionTexture,
bool& bOutUseDirectionalLightShadowing)
{
OutLightFunctionWorldToShadow = FMatrix::Identity;
bOutUseDirectionalLightShadowing = true;
FLightSceneInfo* DirectionalLightSceneInfo = NULL;
for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt)
{
const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;
if (ViewFamily.EngineShowFlags.LightFunctions
&& LightSceneInfo->Proxy->GetLightType() == LightType_Directional
&& LightSceneInfo->ShouldRenderLightViewIndependent()
&& LightSceneInfo->ShouldRenderLight(View))
{
bOutUseDirectionalLightShadowing = LightSceneInfo->Proxy->CastsVolumetricShadow();
if (CheckForLightFunction(LightSceneInfo))
{
DirectionalLightSceneInfo = LightSceneInfo;
break;
}
}
}
if (DirectionalLightSceneInfo)
{
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)) * GVolumetricFogLightFunctionSupersampleScale;
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;
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 = FVector(1.0f, 1.0f / Bounds.W, 1.0f / Bounds.W);
ShadowInitializer.FaceDirection = FVector(1, 0, 0);
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,
0,
false
);
}
const FMaterialRenderProxy* MaterialProxy = DirectionalLightSceneInfo->Proxy->GetLightFunctionMaterial();
if (MaterialProxy && MaterialProxy->GetMaterial(Scene->GetFeatureLevel())->IsLightFunction())
{
FPooledRenderTargetDesc LightFunctionTextureDesc = FPooledRenderTargetDesc::Create2DDesc(LightFunctionResolution, PF_G8, FClearValueBinding::None, TexCreate_None, TexCreate_ShaderResource | TexCreate_RenderTargetable, false);
LightFunctionTextureDesc.Flags |= GFastVRamConfig.VolumetricFog;
OutLightFunctionTexture = GraphBuilder.CreateTexture(LightFunctionTextureDesc, TEXT("VolumetricFogLightFunction"));
const FMatrix WorldToShadowValue = FTranslationMatrix(ProjectedShadowInfo.PreShadowTranslation) * ProjectedShadowInfo.SubjectAndReceiverMatrix;
OutLightFunctionWorldToShadow = WorldToShadowValue;
FRenderTargetParameters* PassParameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(OutLightFunctionTexture, ERenderTargetLoadAction::ENoAction, ERenderTargetStoreAction::EStore);
GraphBuilder.AddPass(
RDG_EVENT_NAME("LightFunction"),
PassParameters,
ERenderGraphPassFlags::None,
[PassParameters, &View, MaterialProxy, LightFunctionResolution, DirectionalLightSceneInfo, WorldToShadowValue, this](FRHICommandListImmediate& RHICmdList)
{
const FMaterial* Material = MaterialProxy->GetMaterial(Scene->GetFeatureLevel());
SCOPED_DRAW_EVENTF(RHICmdList, LightFunction, TEXT("LightFunction %ux%u Material=%s"), LightFunctionResolution.X, LightFunctionResolution.Y, *Material->GetFriendlyName());
RHICmdList.SetViewport(0, 0, 0.0f, LightFunctionResolution.X, LightFunctionResolution.Y, 1.0f);
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);
FVolumetricFogLightFunctionPS* PixelShader = MaterialShaderMap->GetShader<FVolumetricFogLightFunctionPS>();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(PixelShader);
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
VertexShader->SetParameters(RHICmdList, View.ViewUniformBuffer);
PixelShader->SetParameters(RHICmdList, View, DirectionalLightSceneInfo, MaterialProxy, FVector2D(1.0f / LightFunctionResolution.X, 1.0f / LightFunctionResolution.Y), WorldToShadowValue.Inverse());
DrawRectangle(
RHICmdList,
0, 0,
LightFunctionResolution.X, LightFunctionResolution.Y,
0, 0,
LightFunctionResolution.X, LightFunctionResolution.Y,
LightFunctionResolution,
LightFunctionResolution,
*VertexShader);
});
}
}
}