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.
298 lines
12 KiB
C++
298 lines
12 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.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include "RenderDll_precompiled.h"
|
|
#include "DriverD3D.h"
|
|
#include "I3DEngine.h"
|
|
#include "D3DPostProcess.h"
|
|
|
|
// All code here, besides CMotionBlur::Preprocess is a direct copy & modification of the CMotionBlurPass code found in GraphicsPipeline/MotionBlur.cpp.
|
|
|
|
void CMotionBlur::CopyAndScaleDoFBuffer(CTexture* pSrcTex, CTexture* pDestTex)
|
|
{
|
|
CD3D9Renderer* const __restrict rd = gcpRendD3D;
|
|
|
|
if (pSrcTex == NULL || pDestTex == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PROFILE_LABEL_SCOPE("Motion Blur - Scale & Copy DoF");
|
|
|
|
bool bResample = pSrcTex->GetWidth() != pDestTex->GetWidth() || pSrcTex->GetHeight() != pDestTex->GetHeight();
|
|
const D3DFormat destFormat = CTexture::DeviceFormatFromTexFormat(pDestTex->GetDstFormat());
|
|
const D3DFormat srcFormat = CTexture::DeviceFormatFromTexFormat(pSrcTex->GetDstFormat());
|
|
|
|
if (!bResample && destFormat == srcFormat)
|
|
{
|
|
rd->GetDeviceContext().CopyResource(pDestTex->GetDevTexture()->GetBaseTexture(), pSrcTex->GetDevTexture()->GetBaseTexture());
|
|
return;
|
|
}
|
|
|
|
static CCryNameTSCRC techTexToTex("TextureToTexture");
|
|
static CCryNameTSCRC techTexToTexResampled("TextureToTextureResampled");
|
|
|
|
gcpRendD3D->FX_PushRenderTarget(0, pDestTex, NULL);
|
|
gcpRendD3D->RT_SetViewport(0, 0, pDestTex->GetWidth(), pDestTex->GetHeight());
|
|
PostProcessUtils().ShBeginPass(CShaderMan::s_shPostEffects, bResample ? techTexToTexResampled : techTexToTex, 0);
|
|
gRenDev->FX_SetState(GS_NODEPTHTEST);
|
|
|
|
int texFilter = CTexture::GetTexState(STexState(bResample ? FILTER_LINEAR : FILTER_POINT, true));
|
|
PostProcessUtils().SetTexture(pSrcTex, 0, texFilter);
|
|
|
|
static CCryNameR param0Name("texToTexParams0");
|
|
static CCryNameR param1Name("texToTexParams1");
|
|
|
|
const bool bBigDownsample = false; // TODO
|
|
CTexture* pOffsetTex = bBigDownsample ? pDestTex : pSrcTex;
|
|
|
|
float s1 = 0.5f / (float)pOffsetTex->GetWidth(); // 2.0 better results on lower res images resizing
|
|
float t1 = 0.5f / (float)pOffsetTex->GetHeight();
|
|
|
|
Vec4 params0, params1;
|
|
if (bBigDownsample)
|
|
{
|
|
// Use rotated grid + middle sample (~Quincunx)
|
|
params0 = Vec4(s1 * 0.96f, t1 * 0.25f, -s1 * 0.25f, t1 * 0.96f);
|
|
params1 = Vec4(-s1 * 0.96f, -t1 * 0.25f, s1 * 0.25f, -t1 * 0.96f);
|
|
}
|
|
else
|
|
{
|
|
// Use box filtering (faster - can skip bilinear filtering, only 4 taps)
|
|
params0 = Vec4(-s1, -t1, s1, -t1);
|
|
params1 = Vec4(s1, t1, -s1, t1);
|
|
}
|
|
|
|
CShaderMan::s_shPostEffects->FXSetPSFloat(param0Name, ¶ms0, 1);
|
|
CShaderMan::s_shPostEffects->FXSetPSFloat(param1Name, ¶ms1, 1);
|
|
|
|
PostProcessUtils().DrawFullScreenTri(pDestTex->GetWidth(), pDestTex->GetHeight());
|
|
PostProcessUtils().ShEndPass();
|
|
gcpRendD3D->FX_PopRenderTarget(0);
|
|
}
|
|
|
|
float CMotionBlur::ComputeMotionScale()
|
|
{
|
|
static float storedMotionScale = 0.0f;
|
|
if (gEnv->pTimer->IsTimerPaused(ITimer::ETIMER_GAME))
|
|
{
|
|
return storedMotionScale;
|
|
}
|
|
|
|
// The length of the generated motion vectors is proportional to the current time step, so we need
|
|
// to rescale the motion vectors to simulate a constant camera exposure time
|
|
|
|
float exposureTime = 1.0f / std::max(CRenderer::CV_r_MotionBlurShutterSpeed, 1e-6f);
|
|
float timeStep = std::max(gEnv->pTimer->GetFrameTime(), 1e-6f);
|
|
|
|
exposureTime *= gEnv->pTimer->GetTimeScale();
|
|
|
|
storedMotionScale = exposureTime / timeStep;
|
|
return storedMotionScale;
|
|
}
|
|
|
|
void CMotionBlur::Render()
|
|
{
|
|
PROFILE_LABEL_SCOPE("MB");
|
|
|
|
CD3D9Renderer* rd = gcpRendD3D;
|
|
CShader* pShader = CShaderMan::s_shPostMotionBlur;
|
|
|
|
int vpX, vpY, vpWidth, vpHeight;
|
|
rd->GetViewport(&vpX, &vpY, &vpWidth, &vpHeight);
|
|
|
|
// Check if DOF is enabled
|
|
CDepthOfField* pDofRenderTech = (CDepthOfField*)PostEffectMgr()->GetEffect(ePFX_eDepthOfField);
|
|
DepthOfFieldParameters dofParameters = pDofRenderTech->GetParameters();
|
|
const bool bGatherDofEnabled = CRenderer::CV_r_dof > 0 && dofParameters.m_bEnabled;
|
|
|
|
Matrix44A mViewProjPrev = GetPrevView();
|
|
Matrix44 mViewProj = PostProcessUtils().m_pView;
|
|
mViewProjPrev = mViewProjPrev * PostProcessUtils().m_pProj * PostProcessUtils().m_pScaleBias;
|
|
mViewProjPrev.Transpose();
|
|
|
|
CTexture* pVelocityRT = CTexture::s_ptexVelocity;
|
|
float tileCountX = (float)CTexture::s_ptexVelocityTiles[1]->GetWidth();
|
|
float tileCountY = (float)CTexture::s_ptexVelocityTiles[1]->GetHeight();
|
|
|
|
static CCryNameR motionBlurParamName("vMotionBlurParams");
|
|
|
|
int texStateLinear = CTexture::GetTexState(STexState(FILTER_LINEAR, true));
|
|
int texStatePoint = CTexture::GetTexState(STexState(FILTER_POINT, true));
|
|
|
|
{
|
|
PROFILE_LABEL_SCOPE("PACK VELOCITY");
|
|
|
|
static CCryNameTSCRC techPackVelocities("PackVelocities");
|
|
static CCryNameR viewProjPrevName("mViewProjPrev");
|
|
static CCryNameR dirBlurName("vDirectionalBlur");
|
|
static CCryNameR radBlurName("vRadBlurParam");
|
|
|
|
const float maxRange = 32.0f;
|
|
const float amount = clamp_tpl<float>(m_pRadBlurAmount->GetParam() / maxRange, 0.0f, 1.0f);
|
|
const float radius = 1.0f / clamp_tpl<float>(m_pRadBlurRadius->GetParam(), 1e-6f, 2.0f);
|
|
const Vec4 blurDir = m_pDirectionalBlurVec->GetParamVec4();
|
|
const Vec4 dirBlurParam = Vec4(blurDir.x * (maxRange / (float)vpWidth), blurDir.y * (maxRange / (float)vpHeight), (float)vpWidth / (float)vpHeight, 1.0f);
|
|
const Vec4 radBlurParam = Vec4(m_pRadBlurScreenPosX->GetParam() * dirBlurParam.z, m_pRadBlurScreenPosY->GetParam(), radius * amount, amount);
|
|
|
|
const bool bRadialBlur = amount + (blurDir.x * blurDir.x) + (blurDir.y * blurDir.y) > 1.0f / (float)vpWidth;
|
|
|
|
uint64 mask = bRadialBlur ? g_HWSR_MaskBit[HWSR_SAMPLE0] : 0;
|
|
AZ_Assert(((mask >> 32) & 0xFFFFFFFF) == 0, "Make sure we aren't trying to use the top 32 bits, as they will be lost");
|
|
|
|
|
|
gcpRendD3D->FX_PushRenderTarget(0, pVelocityRT, NULL);
|
|
gcpRendD3D->RT_SetViewport(0, 0, pVelocityRT->GetWidth(), pVelocityRT->GetHeight());
|
|
PostProcessUtils().ShBeginPass(pShader, techPackVelocities, (uint32)mask);
|
|
gRenDev->FX_SetState(GS_NODEPTHTEST);
|
|
|
|
PostProcessUtils().SetTexture(CTexture::s_ptexZTarget, 0, texStatePoint);
|
|
PostProcessUtils().SetTexture(CTexture::s_ptexHDRTarget, 1, texStatePoint);
|
|
PostProcessUtils().SetTexture(PostProcessUtils().GetVelocityObjectRT(), 2, texStatePoint);
|
|
|
|
mViewProjPrev.Transpose();
|
|
pShader->FXSetPSFloat(viewProjPrevName, (Vec4*)mViewProjPrev.GetData(), 4);
|
|
pShader->FXSetPSFloat(dirBlurName, &dirBlurParam, 1);
|
|
pShader->FXSetPSFloat(radBlurName, &radBlurParam, 1);
|
|
const Vec4 motionBlurParams = Vec4(ComputeMotionScale(), 1.0f / tileCountX, 1.0f / tileCountX * CRenderer::CV_r_MotionBlurCameraMotionScale, 0);
|
|
pShader->FXSetPSFloat(motionBlurParamName, &motionBlurParams, 1);
|
|
|
|
PostProcessUtils().DrawFullScreenTriWPOS(pVelocityRT->GetWidth(), pVelocityRT->GetHeight());
|
|
PostProcessUtils().ShEndPass();
|
|
gcpRendD3D->FX_PopRenderTarget(0);
|
|
|
|
}
|
|
|
|
{
|
|
PROFILE_LABEL_SCOPE("VELOCITY TILES");
|
|
|
|
static CCryNameTSCRC techVelocityTileGen("VelocityTileGen");
|
|
static CCryNameTSCRC techTileNeighborhood("VelocityTileNeighborhood");
|
|
|
|
// Tile generation first pass
|
|
{
|
|
CTexture* pVelocityTile = CTexture::s_ptexVelocityTiles[0];
|
|
|
|
gcpRendD3D->FX_PushRenderTarget(0, pVelocityTile, NULL);
|
|
gcpRendD3D->RT_SetViewport(0, 0, pVelocityTile->GetWidth(), pVelocityTile->GetHeight());
|
|
PostProcessUtils().ShBeginPass(pShader, techVelocityTileGen, 0);
|
|
gRenDev->FX_SetState(GS_NODEPTHTEST);
|
|
PostProcessUtils().SetTexture(pVelocityRT, 0, texStatePoint);
|
|
|
|
Vec4 params = Vec4((float)pVelocityRT->GetWidth(), (float)pVelocityRT->GetHeight(), ceilf((float)gcpRendD3D->GetWidth() / tileCountX), 0);
|
|
pShader->FXSetPSFloat(motionBlurParamName, ¶ms, 1);
|
|
|
|
PostProcessUtils().DrawFullScreenTri(pVelocityTile->GetWidth(), pVelocityTile->GetHeight());
|
|
PostProcessUtils().ShEndPass();
|
|
gcpRendD3D->FX_PopRenderTarget(0);
|
|
}
|
|
|
|
// Tile generation second pass
|
|
{
|
|
CTexture* pVelocityTile = CTexture::s_ptexVelocityTiles[1];
|
|
|
|
gcpRendD3D->FX_PushRenderTarget(0, pVelocityTile, NULL);
|
|
gcpRendD3D->RT_SetViewport(0, 0, pVelocityTile->GetWidth(), pVelocityTile->GetHeight());
|
|
PostProcessUtils().ShBeginPass(pShader, techVelocityTileGen, 0);
|
|
gRenDev->FX_SetState(GS_NODEPTHTEST);
|
|
PostProcessUtils().SetTexture(CTexture::s_ptexVelocityTiles[0], 0, texStatePoint);
|
|
|
|
Vec4 params = Vec4((float)CTexture::s_ptexVelocityTiles[0]->GetWidth(), (float)CTexture::s_ptexVelocityTiles[0]->GetHeight(), ceilf((float)gcpRendD3D->GetHeight() / tileCountY), 1);
|
|
pShader->FXSetPSFloat(motionBlurParamName, ¶ms, 1);
|
|
|
|
PostProcessUtils().DrawFullScreenTri(pVelocityTile->GetWidth(), pVelocityTile->GetHeight());
|
|
PostProcessUtils().ShEndPass();
|
|
gcpRendD3D->FX_PopRenderTarget(0);
|
|
}
|
|
|
|
// Neighborhood max
|
|
{
|
|
CTexture* pVelocityTile = CTexture::s_ptexVelocityTiles[2];
|
|
|
|
gcpRendD3D->FX_PushRenderTarget(0, pVelocityTile, NULL);
|
|
gcpRendD3D->RT_SetViewport(0, 0, pVelocityTile->GetWidth(), pVelocityTile->GetHeight());
|
|
PostProcessUtils().ShBeginPass(pShader, techTileNeighborhood, 0);
|
|
gRenDev->FX_SetState(GS_NODEPTHTEST);
|
|
PostProcessUtils().SetTexture(CTexture::s_ptexVelocityTiles[1], 0, texStatePoint);
|
|
|
|
Vec4 params = Vec4(1.0f / tileCountX, 1.0f / tileCountY, 0, 0);
|
|
pShader->FXSetPSFloat(motionBlurParamName, ¶ms, 1);
|
|
|
|
PostProcessUtils().DrawFullScreenTri(pVelocityTile->GetWidth(), pVelocityTile->GetHeight());
|
|
PostProcessUtils().ShEndPass();
|
|
gcpRendD3D->FX_PopRenderTarget(0);
|
|
}
|
|
}
|
|
|
|
{
|
|
PROFILE_LABEL_SCOPE("MOTION VECTOR APPLY");
|
|
|
|
static CCryNameTSCRC techMotionBlur("MotionBlur");
|
|
|
|
if (bGatherDofEnabled)
|
|
{
|
|
CopyAndScaleDoFBuffer(CTexture::s_ptexHDRTarget, CTexture::s_ptexSceneTargetR11G11B10F[0]);
|
|
}
|
|
|
|
uint64 rtMask = 0;
|
|
rtMask |= (CRenderer::CV_r_MotionBlurQuality >= 2) ? g_HWSR_MaskBit[HWSR_SAMPLE2] : 0;
|
|
rtMask |= (CRenderer::CV_r_MotionBlurQuality == 1) ? g_HWSR_MaskBit[HWSR_SAMPLE1] : 0;
|
|
|
|
CTexture* pHDRTarget = CTexture::s_ptexHDRTarget;
|
|
AZ_Assert(((rtMask >> 32) & 0xFFFFFFFF) == 0, "Make sure we aren't trying to use the top 32 bits, as they will be lost");
|
|
|
|
gcpRendD3D->FX_PushRenderTarget(0, pHDRTarget, NULL);
|
|
gcpRendD3D->RT_SetViewport(0, 0, pHDRTarget->GetWidth(), pHDRTarget->GetHeight());
|
|
PostProcessUtils().ShBeginPass(pShader, techMotionBlur, (uint32)rtMask);
|
|
gRenDev->FX_SetState(GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA);
|
|
PostProcessUtils().SetTexture(bGatherDofEnabled ? CTexture::s_ptexSceneTargetR11G11B10F[0] : CTexture::s_ptexHDRTargetPrev, 0, texStateLinear);
|
|
PostProcessUtils().SetTexture(pVelocityRT, 1, texStatePoint);
|
|
PostProcessUtils().SetTexture(CTexture::s_ptexVelocityTiles[2], 2, texStatePoint);
|
|
|
|
Vec4 params = Vec4(1.0f / tileCountX, 1.0f / tileCountY, 0, 0);
|
|
pShader->FXSetPSFloat(motionBlurParamName, ¶ms, 1);
|
|
|
|
|
|
PostProcessUtils().DrawFullScreenTri(pHDRTarget->GetWidth(), pHDRTarget->GetHeight());
|
|
PostProcessUtils().ShEndPass();
|
|
gcpRendD3D->FX_PopRenderTarget(0);
|
|
}
|
|
}
|
|
|
|
bool CMotionBlur::Preprocess()
|
|
{
|
|
bool bQualityCheck = CPostEffectsMgr::CheckPostProcessQuality(eRQ_Medium, eSQ_Medium);
|
|
|
|
if (!bQualityCheck)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if IsCVarConstAccess(constexpr) (!CRenderer::CV_r_PostProcessFilters)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CRenderer::CV_r_MotionBlur)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!CRenderer::CV_r_RenderMotionBlurAfterHDR)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|