You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/RenderDll/XRenderD3D9/GraphicsPipeline/FurPasses.cpp

256 lines
8.7 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "RenderDll_precompiled.h"
#include "FurPasses.h"
#include "DriverD3D.h"
#include "D3DPostProcess.h"
FurPasses* FurPasses::s_pInstance = nullptr;
/*static*/ void FurPasses::InstallInstance()
{
if (s_pInstance == nullptr)
{
s_pInstance = new FurPasses();
}
}
/*static*/ void FurPasses::ReleaseInstance()
{
delete s_pInstance;
s_pInstance = nullptr;
}
/*static*/ FurPasses& FurPasses::GetInstance()
{
AZ_Assert(s_pInstance != nullptr, "FurPasses instance being retrieved before install.");
return *s_pInstance;
}
FurPasses::FurPasses()
: m_furShellPassPercent(0.0f)
{
}
FurPasses::~FurPasses()
{
}
FurPasses::RenderMode FurPasses::GetFurRenderingMode()
{
switch (CRenderer::CV_r_Fur)
{
case 1:
return RenderMode::AlphaBlended;
case 2:
return RenderMode::AlphaTested;
default:
return RenderMode::None;
}
}
bool FurPasses::IsRenderingFur()
{
if (GetFurRenderingMode() == RenderMode::None)
{
return false;
}
uint32 flags = SRendItem::BatchFlags(GetFurRenderList(), gcpRendD3D->m_RP.m_pRLD);
return (flags & FB_FUR) != 0;
}
int FurPasses::GetFurRenderList()
{
return (GetFurRenderingMode() == RenderMode::AlphaBlended) ? EFSLIST_TRANSP : EFSLIST_GENERAL;
}
void FurPasses::ExecuteZPostPass()
{
// This pass renders the outermost fur shell in a 1-in-4 stipple pattern to gather lighting data for fur tips.
// It also performs an additional LinearizeDepth pass to provide the updated depths to the deferred pipeline.
if (IsRenderingFur())
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
{
PROFILE_LABEL_SCOPE("FUR_ZPOST");
rd->FX_ZScene(true, false);
rd->m_RP.m_pRenderFunc = &ZPostRenderFunc;
rd->FX_ProcessRenderList(GetFurRenderList(), FB_FUR, false /*bSetRenderFunc*/);
rd->FX_ZScene(false, false, true);
}
rd->FX_LinearizeDepth(CTexture::s_ptexFurZTarget);
}
}
void FurPasses::ExecuteObliteratePass()
{
// This pass captures the lighting data from HDRTarget to s_ptexFurLightAcc, and then removes the stipples from
// the final target (via a horizontal blur only on the stippled pixels) and depth buffer (direct copy from Z target)
// before beginning the forward shading passes
if (IsRenderingFur())
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
PROFILE_LABEL_SCOPE("FUR_OBLITERATE");
// Copy HDR target so we can use it as an input texture
PostProcessUtils().CopyScreenToTexture(CTexture::s_ptexFurLightAcc);
PostProcessUtils().SetTexture(CTexture::s_ptexFurLightAcc, 0, FILTER_POINT);
// Use Z target rather than fur Z target so that the "true" depth can be retained for forward passes
// Without this, some passes may fail depth tests when they should pass (such as eye rendering)
PostProcessUtils().SetTexture(CTexture::s_ptexZTarget, 1, FILTER_POINT);
rd->m_RP.m_pRenderFunc = &ObliterateRenderFunc;
rd->FX_ProcessRenderList(GetFurRenderList(), FB_FUR, false /*bSetRenderFunc*/);
}
}
void FurPasses::ExecuteFinPass()
{
// This pass renders alpha-tested camera-facing silhouettes of the fur fins. It uses similar logic to the fur shadow pass.
if (IsRenderingFur())
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
PROFILE_LABEL_SCOPE("FUR_FINS");
uint64 nSavedFlags = rd->m_RP.m_FlagsShader_RT;
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_GPU_PARTICLE_TURBULENCE]; // Indicates fin pass
FurPasses::GetInstance().ApplyFurDebugFlags();
rd->m_RP.m_pRenderFunc = &FinRenderFunc;
rd->FX_ProcessRenderList(GetFurRenderList(), FB_FUR, false /*bSetRenderFunc*/);
rd->m_RP.m_FlagsShader_RT = nSavedFlags;
}
}
void FurPasses::ExecuteShellPrepass()
{
// This pass gathers and packs all data required by the shell passes into a single buffer. The RGB channels contain
// the accumulated diffuse and specular lighting (without albedo applied), with the diffuse stored in the upper half of
// the channels, and the specular stored in the lower half. The alpha channel contains the scene depth, to save a
// texture read of the linearized depth buffer.
if (IsRenderingFur())
{
// Skip shell prepass for aux viewports. Shader side, this is indicated by %_RT_HDR_MODE being unset, but since the
// render pass hasn't started yet, we have to instead mimic the check that FX_Start performs to set %_RT_HDR_MODE
CD3D9Renderer* const __restrict rd = gcpRendD3D;
bool hdrMode = (rd->m_RP.m_PersFlags2 & RBPF2_HDR_FP16) && !(rd->m_RP.m_nBatchFilter & (FB_Z));
if (hdrMode)
{
PROFILE_LABEL_SCOPE("FUR_SHELL_PREPASS");
uint64 savedFlags = rd->m_RP.m_FlagsShader_RT;
// Volumetric fog is applied in prepass only if fur is alpha blended; alpha tested fur is drawn before fog
bool useVolumetricFog = CD3D9Renderer::CV_r_VolumetricFog != 0 && GetFurRenderingMode() == RenderMode::AlphaBlended;
if (useVolumetricFog)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_VOLUMETRIC_FOG];
}
static CCryNameTSCRC techFurShellPrepass("FurShellPrepass");
rd->FX_PushRenderTarget(0, CTexture::s_ptexFurPrepass, nullptr);
PostProcessUtils().ShBeginPass(CShaderMan::s_ShaderFur, techFurShellPrepass, FEF_DONTSETSTATES | FEF_DONTSETTEXTURES);
SPostEffectsUtils::SetTexture(CTexture::s_ptexFurLightAcc, 0, FILTER_POINT, 0);
SPostEffectsUtils::SetTexture(CTexture::s_ptexSceneTargetR11G11B10F[0], 1, FILTER_POINT, 0);
SPostEffectsUtils::SetTexture(CTexture::s_ptexSceneDiffuse, 2, FILTER_POINT, 0);
SPostEffectsUtils::SetTexture(CTexture::s_ptexSceneNormalsMap, 3, FILTER_POINT, 0);
SPostEffectsUtils::SetTexture(CTexture::s_ptexSceneSpecular, 4, FILTER_POINT, 0);
SPostEffectsUtils::SetTexture(CTexture::s_ptexFurZTarget, 5, FILTER_POINT, 0);
if (useVolumetricFog)
{
SPostEffectsUtils::SetTexture(CTexture::s_ptexVolumetricFog, 6, FILTER_TRILINEAR, 1);
}
rd->FX_SetState(GS_NODEPTHTEST);
GetUtils().DrawQuadFS(CShaderMan::s_ShaderFur, true /*bOutputCamVec*/, CTexture::s_ptexFurPrepass->GetWidth(), CTexture::s_ptexFurPrepass->GetHeight());
GetUtils().ShEndPass();
rd->FX_PopRenderTarget(0);
rd->m_RP.m_FlagsShader_RT = savedFlags;
}
}
}
void FurPasses::ApplyFurDebugFlags()
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
if (rd->CV_r_FurDebug > 0 && (rd->m_RP.m_FlagsShader_RT & g_HWSR_MaskBit[HWSR_HDR_MODE]) != 0) // Don't apply fur debug flags in aux views
{
if (rd->CV_r_FurDebug & 1)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG0];
}
if (rd->CV_r_FurDebug & 2)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG1];
}
if (rd->CV_r_FurDebug & 4)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG2];
}
if (rd->CV_r_FurDebug & 8)
{
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG3];
}
}
}
void FurPasses::SetFurShellPassPercent(float percent)
{
m_furShellPassPercent = AZ::GetClamp(percent, 0.0f, 1.0f);
}
float FurPasses::GetFurShellPassPercent()
{
return m_furShellPassPercent;
}
/*static*/ void FurPasses::ZPostRenderFunc()
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
static CCryNameTSCRC techFurZPost("FurZPost");
rd->m_RP.m_pShader->FXSetTechnique(techFurZPost);
rd->FX_FlushShader_General();
}
/*static*/ void FurPasses::ObliterateRenderFunc()
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
static CCryNameTSCRC techFurObliterate("FurObliterate");
rd->m_RP.m_pShader->FXSetTechnique(techFurObliterate);
rd->FX_FlushShader_General();
}
/*static*/ void FurPasses::FinRenderFunc()
{
CD3D9Renderer* const __restrict rd = gcpRendD3D;
static CCryNameTSCRC techFurFins("FurFins");
rd->m_RP.m_pShader->FXSetTechnique(techFurFins);
rd->FX_FlushShader_General();
}