// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. /*============================================================================= PostProcessSubsurface.cpp: Screenspace subsurface scattering implementation. =============================================================================*/ #include "RendererPrivate.h" #include "ScenePrivate.h" #include "SceneFilterRendering.h" #include "PostProcessSubsurface.h" #include "PostProcessing.h" /** Encapsulates the post processing ambient occlusion pixel shader. */ template class FPostProcessSubsurfaceSetupPS : public FGlobalShader { DECLARE_SHADER_TYPE(FPostProcessSubsurfaceSetupPS , Global); static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("SSSS_SPECULAR_CORRECTION"), SpecularCorrection); } /** Default constructor. */ FPostProcessSubsurfaceSetupPS () {} public: FPostProcessPassParameters PostprocessParameter; FDeferredPixelShaderParameters DeferredParameters; /** Initialization constructor. */ FPostProcessSubsurfaceSetupPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { PostprocessParameter.Bind(Initializer.ParameterMap); DeferredParameters.Bind(Initializer.ParameterMap); } void SetParameters(const FRenderingCompositePassContext& Context) { const FFinalPostProcessSettings& Settings = Context.View.FinalPostProcessSettings; const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View); PostprocessParameter.SetPS(ShaderRHI, Context, TStaticSamplerState::GetRHI()); DeferredParameters.Set(Context.RHICmdList, ShaderRHI, Context.View); } // FShader interface. virtual bool Serialize(FArchive& Ar) { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << PostprocessParameter << DeferredParameters; return bShaderHasOutdatedParameters; } static const TCHAR* GetSourceFilename() { return TEXT("PostProcessSubsurface"); } static const TCHAR* GetFunctionName() { return TEXT("SetupPS"); } }; // #define avoids a lot of code duplication #define VARIATION1(A) typedef FPostProcessSubsurfaceSetupPS FPostProcessSubsurfaceSetupPS##A; \ IMPLEMENT_SHADER_TYPE2(FPostProcessSubsurfaceSetupPS##A, SF_Pixel); VARIATION1(0) VARIATION1(1) #undef VARIATION1 template void SetSubsurfaceSetupShader(const FRenderingCompositePassContext& Context) { TShaderMapRef VertexShader(GetGlobalShaderMap()); TShaderMapRef > PixelShader(GetGlobalShaderMap()); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(Context.RHICmdList, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); PixelShader->SetParameters(Context); VertexShader->SetParameters(Context); } static TAutoConsoleVariable CVarSubsurfaceQuality( TEXT("r.SubsurfaceQuality"), 1, TEXT("Define the quality of the Screenspace subsurface scattering postprocess.\n") TEXT(" 0: low quality for speculars on subsurface materials\n") TEXT(" 1: higher quality as specular is separated before screenspace blurring (Only used if SceneColor has an alpha channel)"), ECVF_Scalability | ECVF_RenderThreadSafe); static bool DoSpecularCorrection() { bool CVarState = CVarSubsurfaceQuality.GetValueOnRenderThread() > 0; int SceneColorFormat; { static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.SceneColorFormat")); SceneColorFormat = CVar->GetInt(); } // we need an alpha channel for this feature return CVarState && (SceneColorFormat >= 4); } void FRCPassPostProcessSubsurfaceSetup::Process(FRenderingCompositePassContext& Context) { SCOPED_DRAW_EVENT(PostProcessSubsurfaceSetup, DEC_SCENE_ITEMS); const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0); if(!InputDesc) { // input is not hooked up correctly return; } const FSceneView& View = Context.View; const FSceneViewFamily& ViewFamily = *(View.Family); FIntPoint SrcSize = InputDesc->Extent; FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent; // e.g. 4 means the input texture is 4x smaller than the buffer size uint32 ScaleFactor = GSceneRenderTargets.GetBufferSizeXY().X / SrcSize.X; FIntRect SrcRect = View.ViewRect / ScaleFactor; FIntRect DestRect = SrcRect; const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context); // Set the view family's render target/viewport. SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef()); // is optimized away if possible (RT size=view size, ) Context.RHICmdList.Clear(true, FLinearColor::Black, false, 1.0f, false, 0, DestRect); Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f ); // set the state Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); if(DoSpecularCorrection()) { // with separate specular SetSubsurfaceSetupShader<1>(Context); } else { // no separate specular SetSubsurfaceSetupShader<0>(Context); } // Draw a quad mapping scene color to the view's render target TShaderMapRef VertexShader(GetGlobalShaderMap()); DrawRectangle( Context.RHICmdList, DestRect.Min.X, DestRect.Min.Y, DestRect.Width(), DestRect.Height(), SrcRect.Min.X, SrcRect.Min.Y, SrcRect.Width(), SrcRect.Height(), DestSize, SrcSize, *VertexShader, EDRF_UseTriangleOptimization); Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); } FPooledRenderTargetDesc FRCPassPostProcessSubsurfaceSetup::ComputeOutputDesc(EPassOutputId InPassOutputId) const { FPooledRenderTargetDesc Ret = PassInputs[0].GetOutput()->RenderTargetDesc; Ret.Reset(); Ret.DebugName = TEXT("SubsurfaceSetup"); // alpha is unsed, todo: consider 32bit format return Ret; } /** Encapsulates the post processing down sample pixel shader. */ template class FPostProcessSubsurfacePS : public FGlobalShader { DECLARE_SHADER_TYPE(FPostProcessSubsurfacePS, Global); static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment); OutEnvironment.SetDefine(TEXT("METHOD"), Method); } /** Default constructor. */ FPostProcessSubsurfacePS() {} public: FPostProcessPassParameters PostprocessParameter; FDeferredPixelShaderParameters DeferredParameters; FShaderParameter SSSParams; /** Initialization constructor. */ FPostProcessSubsurfacePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { PostprocessParameter.Bind(Initializer.ParameterMap); DeferredParameters.Bind(Initializer.ParameterMap); SSSParams.Bind(Initializer.ParameterMap, TEXT("SSSParams")); } // FShader interface. virtual bool Serialize(FArchive& Ar) { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << PostprocessParameter << DeferredParameters << SSSParams; return bShaderHasOutdatedParameters; } void SetParameters(const FRenderingCompositePassContext& Context, float InRadius) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(Context.RHICmdList, ShaderRHI, Context.View); DeferredParameters.Set(Context.RHICmdList, ShaderRHI, Context.View); PostprocessParameter.SetPS(ShaderRHI, Context, TStaticSamplerState::GetRHI()); { FVector4 ColorScale(InRadius, 0, 0, 0); SetShaderValue(Context.RHICmdList, ShaderRHI, SSSParams, ColorScale); } } static const TCHAR* GetSourceFilename() { return TEXT("PostProcessSubsurface"); } static const TCHAR* GetFunctionName() { return TEXT("MainPS"); } }; // #define avoids a lot of code duplication #define VARIATION1(A) typedef FPostProcessSubsurfacePS FPostProcessSubsurfacePS##A; \ IMPLEMENT_SHADER_TYPE2(FPostProcessSubsurfacePS##A, SF_Pixel); VARIATION1(0) VARIATION1(1) VARIATION1(2) #undef VARIATION1 FRCPassPostProcessSubsurface::FRCPassPostProcessSubsurface(uint32 InPass, float InRadius) : Radius(InRadius) , Pass(InPass) { } template void SetSubsurfaceShader(const FRenderingCompositePassContext& Context, float InRadius) { TShaderMapRef VertexShader(GetGlobalShaderMap()); TShaderMapRef > PixelShader(GetGlobalShaderMap()); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(Context.RHICmdList, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); PixelShader->SetParameters(Context, InRadius); VertexShader->SetParameters(Context); } void FRCPassPostProcessSubsurface::Process(FRenderingCompositePassContext& Context) { const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0); if(!InputDesc) { // input is not hooked up correctly return; } const FSceneView& View = Context.View; const FSceneViewFamily& ViewFamily = *(View.Family); FIntPoint SrcSize = InputDesc->Extent; FIntPoint DestSize = SrcSize; // e.g. 4 means the input texture is 4x smaller than the buffer size uint32 ScaleFactor = GSceneRenderTargets.GetBufferSizeXY().X / SrcSize.X; FIntRect SrcRect = View.ViewRect / ScaleFactor; FIntRect DestRect = SrcRect; const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context); // Set the view family's render target/viewport. SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef()); // is optimized away if possible (RT size=view size, ) Context.RHICmdList.Clear(true, FLinearColor(0, 0, 0, 0), false, 1.0f, false, 0, DestRect); Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f ); // set the state Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); TShaderMapRef VertexShader(GetGlobalShaderMap()); if(Pass == 0) { SCOPED_DRAW_EVENT(SubsurfacePass0, DEC_SCENE_ITEMS); SetSubsurfaceShader<0>(Context, Radius); // Draw a quad mapping scene color to the view's render target DrawRectangle( Context.RHICmdList, DestRect.Min.X, DestRect.Min.Y, DestRect.Width(), DestRect.Height(), SrcRect.Min.X, SrcRect.Min.Y, SrcRect.Width(), SrcRect.Height(), DestSize, SrcSize, *VertexShader, EDRF_UseTriangleOptimization); Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); } if(Pass == 1) { SCOPED_DRAW_EVENT(SubsurfacePass1, DEC_SCENE_ITEMS); if(DoSpecularCorrection()) { // reconstruct specular and add it in final pass SetSubsurfaceShader<2>(Context, Radius); } else { SetSubsurfaceShader<1>(Context, Radius); } // Draw a quad mapping scene color to the view's render target DrawRectangle( Context.RHICmdList, DestRect.Min.X, DestRect.Min.Y, DestRect.Width(), DestRect.Height(), SrcRect.Min.X, SrcRect.Min.Y, SrcRect.Width(), SrcRect.Height(), DestSize, SrcSize, *VertexShader, EDRF_UseTriangleOptimization); Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); } } FPooledRenderTargetDesc FRCPassPostProcessSubsurface::ComputeOutputDesc(EPassOutputId InPassOutputId) const { FPooledRenderTargetDesc Ret = PassInputs[0].GetOutput()->RenderTargetDesc; Ret.Reset(); Ret.DebugName = (Pass == 0) ? TEXT("SubsurfaceTemp") : TEXT("SubsurfaceSceneColor"); return Ret; }