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/PostProcessMisc.cpp

705 lines
26 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"
#include "Common/RenderView.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void CVolumetricScattering::Render()
{
PROFILE_SHADER_SCOPE;
// quickie-prototype
// - some ideas: add several types (cloudy/sparky/yadayada)
// Get current viewport
int iTempX, iTempY, iWidth, iHeight;
gcpRendD3D->GetViewport(&iTempX, &iTempY, &iWidth, &iHeight);
//////////////////////////////////////////////////////////////////////////////////////////////////
// Render god-rays into low-res render target for less fillrate hit
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexBackBufferScaled[1], NULL);
gcpRendD3D->FX_SetColorDontCareActions(0, false, false);
gcpRendD3D->FX_ClearTarget(CTexture::s_ptexBackBufferScaled[1], Clr_Transparent);
gcpRendD3D->RT_SetViewport(0, 0, CTexture::s_ptexBackBufferScaled[1]->GetWidth(), CTexture::s_ptexBackBufferScaled[1]->GetHeight());
float fAmount = m_pAmount->GetParam();
float fTiling = m_pTiling->GetParam();
float fSpeed = m_pSpeed->GetParam();
Vec4 pColor = m_pColor->GetParamVec4();
static CCryNameTSCRC pTechName("VolumetricScattering");
uint32 nPasses;
CShaderMan::s_shPostEffects->FXSetTechnique(pTechName);
{
PROFILE_LABEL_SCOPE("VOLUMETRICSCATTERING");
CShaderMan::s_shPostEffects->FXBegin(&nPasses, FEF_DONTSETSTATES);
gcpRendD3D->SetCullMode(R_CULL_NONE);
gcpRendD3D->FX_SetState(GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCCOL | GS_NODEPTHTEST);
int nSlicesCount = 10;
Vec4 pParams;
pParams = Vec4(fTiling, fSpeed, fTiling, fSpeed);
static CCryNameR pParam0Name("VolumetricScattering");
static CCryNameR pParam1Name("VolumetricScatteringColor");
static CCryNameR pParam2Name("PI_volScatterParamsVS");
static CCryNameR pParam3Name("PI_volScatterParamsPS");
for (int r(0); r < nSlicesCount; ++r)
{
// !force updating constants per-pass! (dx10..)
CShaderMan::s_shPostEffects->FXBeginPass(0);
// Set PS default params
Vec4 pParamsPI = Vec4(1.0f, fAmount, r, 1.0f / (float) nSlicesCount);
CShaderMan::s_shPostEffects->FXSetVSFloat(pParam0Name, &pParams, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam1Name, &pColor, 1);
CShaderMan::s_shPostEffects->FXSetVSFloat(pParam2Name, &pParamsPI, 1);
CShaderMan::s_shPostEffects->FXSetPSFloat(pParam3Name, &pParamsPI, 1);
GetUtils().DrawFullScreenTri(CTexture::s_ptexBackBufferScaled[1]->GetWidth(), CTexture::s_ptexBackBufferScaled[1]->GetHeight());
CShaderMan::s_shPostEffects->FXEndPass();
}
CShaderMan::s_shPostEffects->FXEnd();
}
// Restore previous viewport
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->RT_SetViewport(iTempX, iTempY, iWidth, iHeight);
//////////////////////////////////////////////////////////////////////////////////////////////////
// Display volumetric scattering effect
{
PROFILE_LABEL_SCOPE("VOLUMETRICSCATTERINGFINAL");
CCryNameTSCRC pTechName0("VolumetricScatteringFinal");
GetUtils().ShBeginPass(CShaderMan::s_shPostEffects, pTechName0, FEF_DONTSETSTATES);
gcpRendD3D->FX_SetState(GS_NODEPTHTEST);
GetUtils().DrawFullScreenTri(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight());
GetUtils().ShEndPass();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void CPost3DRenderer::Render()
{
PROFILE_LABEL_SCOPE("POST_3D_RENDERER");
PROFILE_SHADER_SCOPE;
// Must update the RT pointers here, otherwise they can get out-of-date
if (CRenderer::CV_r_UsePersistentRTForModelHUD > 0)
{
m_pFlashRT = CTexture::s_ptexModelHudBuffer;
}
else
{
m_pFlashRT = CTexture::s_ptexBackBuffer;
}
m_pTempRT = CTexture::s_ptexSceneDiffuse;
#if defined(WIN32) || defined(APPLE) || defined(LINUX) || defined(SUPPORTS_DEFERRED_SHADING_L_BUFFERS_FORMAT)
m_pTempRT = CTexture::s_ptexSceneNormalsBent; // non-msaaed target
#endif
if (HasModelsToRender() && IsActive())
{
// Render model groups
m_edgeFadeScale = (1.0f / clamp_tpl<float>(m_pEdgeFadeScale->GetParam(), 0.001f, 1.0f));
m_post3DRendererflags |= eP3DR_DirtyFlashRT;
m_groupCount = 1; // There must be at least 1 group, this will then get set the correct amount when processing the models
for (uint8 groupId = 0; groupId < m_groupCount; groupId++)
{
RenderGroup(groupId);
}
}
else
{
// Nothing to render, so clear Flash RT so that we don't render rubbish on the flash objects
ClearFlashRT();
}
}
void CPost3DRenderer::ClearFlashRT()
{
PROFILE_LABEL_SCOPE("CLEAR_RT");
gcpRendD3D->FX_ClearTarget(m_pFlashRT);
}
void CPost3DRenderer::RenderGroup(uint8 groupId)
{
PROFILE_LABEL_SCOPE("RENDER_GROUP");
SRenderPipeline& renderPipeline = gcpRendD3D->m_RP;
// Reset group vars
m_post3DRendererflags &= ~eP3DR_HasSilhouettes;
m_alpha = 1.0f;
float screenRect[4];
memset(screenRect, 0, sizeof(screenRect));
{
PROFILE_LABEL_SCOPE("RENDER_DEPTH");
// On PC we render depth separately
renderPipeline.m_pRenderFunc = gcpRendD3D->FX_FlushShader_ZPass;
RenderMeshes(groupId, screenRect, eRMM_DepthOnly);
}
renderPipeline.m_pRenderFunc = gcpRendD3D->FX_FlushShader_General;
RenderMeshes(groupId, screenRect);
AlphaCorrection();
GammaCorrection(screenRect);
if (m_post3DRendererflags & eP3DR_HasSilhouettes)
{
RenderSilhouettes(groupId, screenRect);
}
}
void CPost3DRenderer::RenderMeshes(uint8 groupId, float screenRect[4], ERenderMeshMode renderMeshMode)
{
PROFILE_LABEL_SCOPE("RENDER_MESHES");
const bool bCustomRender = (renderMeshMode == eRMM_Custom) ? true : false;
const bool bDefaultRender = (renderMeshMode == eRMM_Default) ? true : false;
const bool bDepthOnlyRender = (renderMeshMode == eRMM_DepthOnly) ? true : false;
const bool bDoStencil = bDefaultRender;
const bool bReverseDepth = (gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags & RBPF_REVERSE_DEPTH) != 0;
gcpRendD3D->FX_ClearTarget(&gcpRendD3D->m_DepthBufferOrig, (bDepthOnlyRender * CLEAR_ZBUFFER) | CLEAR_STENCIL, Clr_FarPlane_R.r, (bDepthOnlyRender * 1));
if (!bDepthOnlyRender)
{
gcpRendD3D->FX_ClearTarget(m_pTempRT, Clr_Transparent);
gcpRendD3D->FX_PushRenderTarget(0, m_pTempRT, &gcpRendD3D->m_DepthBufferOrig);
gcpRendD3D->RT_SetViewport(0, 0, m_pTempRT->GetWidth(), m_pTempRT->GetHeight());
}
else
{
// Setup depth render
const bool bClearOnResolve = false;
const int nCMSide = -1;
const bool bScreenVP = true;
gcpRendD3D->FX_ClearTarget(CTexture::s_ptexZTarget, Clr_Transparent);
gcpRendD3D->FX_ClearTarget(CTexture::s_ptexSceneNormalsMap, Clr_Transparent);
gcpRendD3D->FX_PushRenderTarget(0, CTexture::s_ptexZTarget, &gcpRendD3D->m_DepthBufferOrig, bClearOnResolve, nCMSide, bScreenVP);
gcpRendD3D->FX_PushRenderTarget(1, CTexture::s_ptexSceneNormalsMap, NULL);
gcpRendD3D->FX_SetState(GS_DEPTHWRITE);
gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->m_MainViewport.nWidth, gcpRendD3D->m_MainViewport.nHeight);
// Stencil initialized to 1 - 0 is reserved for MSAAed samples
gcpRendD3D->m_nStencilMaskRef = 1;
gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags |= RBPF_ZPASS;
if (CTexture::s_eTFZ == eTF_R32F || CTexture::s_eTFZ == eTF_R16G16F || CTexture::s_eTFZ == eTF_R16G16B16A16F || CTexture::s_eTFZ == eTF_D24S8 || CTexture::s_eTFZ == eTF_D32FS8)
{
gcpRendD3D->m_RP.m_PersFlags2 |= (RBPF2_NOALPHABLEND | RBPF2_NOALPHATEST);
gcpRendD3D->m_RP.m_StateAnd &= ~(GS_BLEND_MASK | GS_ALPHATEST_MASK);
}
}
// Set scissor
gcpRendD3D->EF_Scissor(true, 0, 0, gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());
SRenderPipeline& RESTRICT_REFERENCE renderPipeline = gcpRendD3D->m_RP;
if (bDoStencil)
{
// Setup stencil for alpha correction pass
renderPipeline.m_ForceStateOr |= GS_STENCIL;
const int32 nStencilState = STENC_FUNC(FSS_STENCFUNC_ALWAYS) | STENCOP_FAIL(FSS_STENCOP_KEEP) | STENCOP_ZFAIL(FSS_STENCOP_KEEP) | STENCOP_PASS(FSS_STENCOP_REPLACE);
gcpRendD3D->FX_SetStencilState(nStencilState, 1, 0xFF, 0xFF);
GetUtils().SetupStencilStates(FSS_STENCFUNC_EQUAL);
gcpRendD3D->m_nStencilMaskRef = 1;
GetUtils().BeginStencilPrePass(true);
}
// Create custom camera so FOV is the same when ever we render
CCamera prevCamera = gcpRendD3D->GetCamera();
CCamera postRenderCamera = prevCamera;
Matrix34 cameraMatrix;
cameraMatrix.SetIdentity(); // Camera is at origin
postRenderCamera.SetMatrix(cameraMatrix);
const float fov = DEFAULT_FOV * clamp_tpl<float>(m_pFOVScale->GetParam(), 0.05f, 1.0f);
const float pixelAspectRatio = m_pPixelAspectRatio->GetParam();
postRenderCamera.SetFrustum(prevCamera.GetViewSurfaceX(), prevCamera.GetViewSurfaceZ(), fov, DEFAULT_NEAR, DEFAULT_FAR, pixelAspectRatio);
gcpRendD3D->SetCamera(postRenderCamera);
// Set flags
const uint32 prevAnd = renderPipeline.m_ForceStateAnd;
const uint32 prevOr = renderPipeline.m_ForceStateOr;
if (!bDepthOnlyRender)
{
renderPipeline.m_ForceStateAnd |= GS_DEPTHFUNC_EQUAL;
renderPipeline.m_ForceStateOr |= GS_DEPTHWRITE;
}
renderPipeline.m_PersFlags2 |= RBPF2_POST_3D_RENDERER_PASS | RBPF2_CUSTOM_RENDER_PASS;
if (bCustomRender)
{
renderPipeline.m_PersFlags2 |= RBPF2_SINGLE_FORWARD_LIGHT_PASS;
}
// Set fog
I3DEngine* p3DEngine = gEnv->p3DEngine;
const ColorF fogColor(0.0f, 0.0f, 0.0f, 0.0f);
gcpRendD3D->SetFogColor(fogColor);
// Draw custom objects
{
PROFILE_LABEL_SCOPE("FB_POST_3D_RENDER");
ProcessRenderList(EFSLIST_GENERAL, FB_POST_3D_RENDER, groupId, screenRect, bCustomRender);
ProcessRenderList(EFSLIST_SKIN, FB_POST_3D_RENDER, groupId, screenRect, bCustomRender);
if (!bCustomRender)
{
ProcessRenderList(EFSLIST_DECAL, FB_POST_3D_RENDER, groupId, screenRect, bCustomRender);
}
if (!bDepthOnlyRender)
{
ProcessRenderList(EFSLIST_TRANSP, FB_POST_3D_RENDER, groupId, screenRect, bCustomRender);
}
}
// Pop render targets
if (!bDepthOnlyRender)
{
gcpRendD3D->FX_PopRenderTarget(0);
}
else
{
gcpRendD3D->m_RP.m_TI[gcpRendD3D->m_RP.m_nProcessThreadID].m_PersFlags &= ~RBPF_ZPASS;
if (CTexture::s_eTFZ == eTF_R16G16F || CTexture::s_eTFZ == eTF_R32F || CTexture::s_eTFZ == eTF_R16G16B16A16F || CTexture::s_eTFZ == eTF_D24S8 || CTexture::s_eTFZ == eTF_D32FS8)
{
gcpRendD3D->m_RP.m_PersFlags2 &= ~(RBPF2_NOALPHABLEND | RBPF2_NOALPHATEST);
gcpRendD3D->m_RP.m_StateAnd |= (GS_BLEND_MASK | GS_ALPHATEST_MASK);
}
gcpRendD3D->FX_PopRenderTarget(0);
gcpRendD3D->FX_PopRenderTarget(1);
}
gcpRendD3D->FX_ResetPipe();
gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());
// Set everything back again
renderPipeline.m_ForceStateAnd = prevAnd;
renderPipeline.m_ForceStateOr = prevOr;
renderPipeline.m_PersFlags2 &= ~(RBPF2_POST_3D_RENDERER_PASS | RBPF2_CUSTOM_RENDER_PASS);
if (bCustomRender)
{
renderPipeline.m_PersFlags2 &= ~RBPF2_SINGLE_FORWARD_LIGHT_PASS;
}
gcpRendD3D->SetCamera(prevCamera);
p3DEngine->SetupDistanceFog();
if (bDoStencil)
{
GetUtils().EndStencilPrePass();
renderPipeline.m_ForceStateOr &= ~GS_STENCIL;
}
}
void CPost3DRenderer::AlphaCorrection()
{
// Alpha correction - Override alpha using stencil, otherwise the alpha from the diffuse map will get copied
// into render target, which then will get used when drawing the 3D objects to screen
PROFILE_LABEL_SCOPE("ALPHA_CORRECTION");
const ColorF clearColor(0.0f, 0.0f, 0.0f, 0.0f);
gcpRendD3D->FX_PushRenderTarget(0, m_pTempRT, &gcpRendD3D->m_DepthBufferOrig);
GetUtils().ShBeginPass(CShaderMan::s_shPostEffects, m_alphaCorrectionTechName, FEF_DONTSETSTATES);
GetUtils().SetupStencilStates(FSS_STENCFUNC_EQUAL);
gcpRendD3D->FX_SetState(GS_NODEPTHTEST | GS_STENCIL | GS_BLSRC_ONE | GS_BLDST_ONE_A_ZERO);
GetUtils().DrawFullScreenTri(m_pTempRT->GetWidth(), m_pTempRT->GetHeight());
GetUtils().ShEndPass();
GetUtils().SetupStencilStates(-1);
gcpRendD3D->FX_PopRenderTarget(0);
}
void CPost3DRenderer::GammaCorrection(float screenRect[4])
{
// Gamma correction and move to correct location on RT
PROFILE_LABEL_SCOPE("GAMMA_CORRECTION");
const int flashRTWidth = m_pFlashRT->GetWidth();
const int flashRTHeight = m_pFlashRT->GetHeight();
// Clear buffer for first group
if (m_post3DRendererflags & eP3DR_DirtyFlashRT)
{
m_post3DRendererflags &= ~eP3DR_DirtyFlashRT;
gcpRendD3D->FX_ClearTarget(m_pFlashRT, Clr_Transparent);
}
gcpRendD3D->FX_PushRenderTarget(0, m_pFlashRT, NULL);
gcpRendD3D->RT_SetViewport(0, 0, flashRTWidth, flashRTHeight);
const float rectWidth = max(screenRect[2] - screenRect[0], 0.0001f);
const float rectHeight = max(screenRect[3] - screenRect[1], 0.0001f);
// Super sample when we copy so double the size of the source texture uv's
float sourceRectWidth = rectWidth * 2.0f;
float sourceRectHeight = rectHeight * 2.0f;
// Clamp the size if larger than 1.0
if (sourceRectWidth >= sourceRectHeight)
{
if (sourceRectWidth > 1.0f)
{
sourceRectHeight *= (1.0f / sourceRectWidth);
sourceRectWidth = 1.0f;
}
}
else if (sourceRectHeight > 1.0f)
{
sourceRectWidth *= (1.0f / sourceRectHeight);
sourceRectHeight = 1.0f;
}
float halfSourceRectWidth = sourceRectWidth * 0.5f;
float halfSourceRectHeight = sourceRectHeight * 0.5f;
const float sourceLeft = max(0.5f - halfSourceRectWidth, 0.0f);
const float sourceRight = min(0.5f + halfSourceRectWidth, 1.0f);
const float sourceTop = max(0.5f - halfSourceRectHeight, 0.0f);
const float sourceBottom = min(0.5f + halfSourceRectHeight, 1.0f);
Vec2 pos[4];
Vec2 uv[4];
pos[0] = Vec2(screenRect[0], screenRect[1]);
pos[1] = Vec2(screenRect[0], screenRect[3]);
pos[2] = Vec2(screenRect[2], screenRect[3]);
pos[3] = Vec2(screenRect[2], screenRect[1]);
uv[0] = Vec2(sourceLeft, sourceTop);
uv[1] = Vec2(sourceLeft, sourceBottom);
uv[2] = Vec2(sourceRight, sourceBottom);
uv[3] = Vec2(sourceRight, sourceTop);
GetUtils().ShBeginPass(CShaderMan::s_shPostEffects, m_gammaCorrectionTechName, FEF_DONTSETSTATES | FEF_DONTSETTEXTURES);
GetUtils().SetTexture(m_pTempRT, 0, FILTER_LINEAR);
// VS params
Vec4 vsParam;
vsParam.x = sourceLeft; // Top
vsParam.y = sourceTop; // Left
vsParam.z = 1.0f / sourceRectWidth; // inv scale x
vsParam.w = 1.0f / sourceRectHeight; // inv scale y
CShaderMan::s_shPostEffects->FXSetVSFloat(m_vsParamName, &vsParam, 1);
// PS params
const Vec4 psParams(m_alpha, m_edgeFadeScale, 0.0f, 0.0f);
CShaderMan::s_shPostEffectsGame->FXSetPSFloat(m_psParamName, &psParams, 1);
const int blendState = GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ONE;
gcpRendD3D->FX_SetState(blendState);
GetUtils().DrawQuad(flashRTWidth, flashRTHeight,
pos[0], pos[1], pos[2], pos[3],
uv[0], uv[1], uv[2], uv[3]);
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
}
void CPost3DRenderer::ProcessRenderList(int list, uint32 batchFilter, uint8 groupId, float screenRect[4], bool bCustomRender)
{
const int stage = 3;
gcpRendD3D->FX_PreRender(stage);
SRenderPipeline& renderPipeline = gcpRendD3D->m_RP;
renderPipeline.m_nPassGroupID = list;
renderPipeline.m_nPassGroupDIP = list;
SRenderListDesc* pRenderListDesc = gRenDev->m_RP.m_pRLD;
renderPipeline.m_nSortGroupID = 0;
int listStart = pRenderListDesc->m_nStartRI[renderPipeline.m_nSortGroupID][list];
int listEnd = pRenderListDesc->m_nEndRI[renderPipeline.m_nSortGroupID][list];
ProcessBatchesList(listStart, listEnd, batchFilter, groupId, screenRect, bCustomRender);
renderPipeline.m_nSortGroupID = 1;
listStart = pRenderListDesc->m_nStartRI[renderPipeline.m_nSortGroupID][list];
listEnd = pRenderListDesc->m_nEndRI[renderPipeline.m_nSortGroupID][list];
ProcessBatchesList(listStart, listEnd, batchFilter, groupId, screenRect, bCustomRender);
gcpRendD3D->FX_PostRender();
}
void CPost3DRenderer::ProcessBatchesList(int listStart, int listEnd, uint32 batchFilter, uint8 groupId, float screenRect[4], bool bCustomRender)
{
// Basis for this function taken from FX_ProcessBatchesList
if ((listEnd - listStart) > 0)
{
gcpRendD3D->FX_StartBatching();
SRenderPipeline& renderPipeline = gcpRendD3D->m_RP;
const int threadID = renderPipeline.m_nProcessThreadID;
const int sortGroupID = renderPipeline.m_nSortGroupID;
const int passGroupID = renderPipeline.m_nPassGroupID;
auto& renderItems = CRenderView::CurrentRenderView()->GetRenderItems(sortGroupID, passGroupID);
renderPipeline.m_nBatchFilter = batchFilter;
CShader* pShader = NULL;
CShaderResources* pCurShaderResources = NULL;
CRenderObject* pCurObject = NULL;
CShader* pCurShader = NULL;
int tech = 0;
for (int i = listStart; i < listEnd; i++)
{
SRendItem& renderItem = renderItems[i];
if (!(renderItem.nBatchFlags & batchFilter))
{
continue;
}
assert(renderItem.pObj);
PREFAST_ASSUME(renderItem.pObj);
CRenderObject* pRenderObject = renderItem.pObj;
// Apply group filter and update group count
SRenderObjData* pRenderObjData = pRenderObject->GetObjData();
uint8 currentObjGroupId = pRenderObjData->m_nCustomData;
m_groupCount = max((uint8)(currentObjGroupId + 1), m_groupCount); // update group count
if (currentObjGroupId != groupId)
{
continue;
}
// Detect if shader has changed
IRenderElement* pRenderElement = renderItem.pElem;
bool bChangedShader = false;
CShaderResources* pShaderResources = NULL;
SRendItem::mfGet(renderItem.SortVal, tech, pShader, pShaderResources);
// Set custom shader
if (bCustomRender && pShader)
{
if (!pShader->FXSetTechnique(m_customRenderTechName))
{
continue;
}
tech = renderPipeline.m_nShaderTechnique;
}
if ((pShader != pCurShader) ||
(!pShaderResources) ||
(!pCurShaderResources) ||
(pShaderResources->m_IdGroup != pCurShaderResources->m_IdGroup) ||
(pRenderObject->m_ObjFlags & (FOB_SKINNED | FOB_DECAL)))
{
bChangedShader = true;
}
pCurShaderResources = pShaderResources;
if (pRenderObject != pCurObject)
{
if (pCurShader)
{
renderPipeline.m_pRenderFunc();
pCurShader = NULL;
bChangedShader = true;
}
if (!gcpRendD3D->FX_ObjectChange(pShader, pCurShaderResources, pRenderObject, pRenderElement))
{
continue;
}
pCurObject = pRenderObject;
}
if (bChangedShader)
{
if (pCurShader)
{
renderPipeline.m_pRenderFunc();
}
pCurShader = pShader;
gcpRendD3D->FX_Start(pShader, tech, pCurShaderResources, pRenderElement);
}
pRenderElement->mfPrepare(true);
if (renderPipeline.m_RIs[0].size() == 0)
{
// Add item to batch
renderPipeline.m_RIs[0].AddElem(&renderItem);
// Update screen rect
memcpy(screenRect, &pRenderObjData->m_fTempVars[5], sizeof(float) * 4);
if (pRenderObjData->m_nHUDSilhouetteParams)
{
m_post3DRendererflags |= eP3DR_HasSilhouettes;
}
// Set current alpha to be the minimum object within group
const float objAlpha = pRenderObjData->m_fTempVars[9];
m_alpha = min(objAlpha, m_alpha);
}
}
if (pCurShader)
{
renderPipeline.m_pRenderFunc();
}
}
}
void CPost3DRenderer::RenderSilhouettes(uint8 groupId, float screenRect[4])
{
PROFILE_LABEL_SCOPE("SILHOUTTES");
RenderMeshes(groupId, screenRect, eRMM_Custom);
CTexture* pOutlineTex = CTexture::s_ptexBackBufferScaled[0];
CTexture* pGlowTex = CTexture::s_ptexBackBufferScaled[1];
ApplyShaderQuality();
SilhouetteOutlines(pOutlineTex, pGlowTex);
SilhouetteGlow(pOutlineTex, pGlowTex);
SilhouetteCombineBlurAndOutline(pOutlineTex, pGlowTex);
GammaCorrection(screenRect);
}
void CPost3DRenderer::SilhouetteCombineBlurAndOutline(CTexture* pOutlineTex, CTexture* pGlowTex)
{
PROFILE_LABEL_SCOPE("COMBINE_BLUR_AND_OUTLINE");
// Combine blur and outline
gcpRendD3D->FX_ClearTarget(m_pTempRT, Clr_Transparent);
gcpRendD3D->FX_PushRenderTarget(0, m_pTempRT, NULL);
GetUtils().ShBeginPass(CShaderMan::s_shPostEffects, m_combineSilhouettesTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ONE);
GetUtils().SetTexture(pOutlineTex, 0, FILTER_LINEAR);
GetUtils().SetTexture(pGlowTex, 1, FILTER_LINEAR);
// Set PS default params
const float silhouetteStrength = max(m_pSilhouetteStrength->GetParam(), 0.0f);
const float fillStrength = 0.02f;
const float glowStrength = 0.5f;
const Vec4 psParams(silhouetteStrength, fillStrength, glowStrength, 0.0f);
CShaderMan::s_shPostEffectsGame->FXSetPSFloat(m_psParamName, &psParams, 1);
GetUtils().DrawFullScreenTri(m_pTempRT->GetWidth(), m_pTempRT->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
}
void CPost3DRenderer::SilhouetteGlow(CTexture* pOutlineTex, CTexture* pGlowTex)
{
PROFILE_LABEL_SCOPE("GLOW");
GetUtils().StretchRect(pOutlineTex, pGlowTex);
GetUtils().TexBlurIterative(pGlowTex, 1, false);
gcpRendD3D->RT_SetViewport(0, 0, gcpRendD3D->GetWidth(), gcpRendD3D->GetHeight());
}
void CPost3DRenderer::SilhouetteOutlines(CTexture* pOutlineTex, [[maybe_unused]] CTexture* pGlowTex)
{
PROFILE_LABEL_SCOPE("OUTLINES");
// Set improved (expensive) edge detection flag
CD3D9Renderer* const __restrict pRD = gcpRendD3D;
const uint64 prevRTFlags = pRD->m_RP.m_FlagsShader_RT;
pRD->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
gcpRendD3D->FX_PushRenderTarget(0, pOutlineTex, NULL);
gcpRendD3D->RT_SetViewport(0, 0, pOutlineTex->GetWidth(), pOutlineTex->GetHeight());
GetUtils().ShBeginPass(CShaderMan::s_shPostEffectsGame, m_silhouetteTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);
gRenDev->FX_SetState(GS_NODEPTHTEST);
// Set shader params
const float edgeScale = 1.0f;
const float fillSrength = 0.1f;
const Vec4 vsParams(edgeScale, 0.0f, 0.0f, 0.0f);
const Vec4 psParams(0.0f, 0.0f, 0.0f, fillSrength);
CShaderMan::s_shPostEffectsGame->FXSetVSFloat(m_vsParamName, &vsParams, 1);
CShaderMan::s_shPostEffectsGame->FXSetPSFloat(m_psParamName, &psParams, 1);
GetUtils().SetTexture(m_pTempRT, 0, FILTER_LINEAR);
GetUtils().SetTexture(CTexture::s_ptexZTarget, 1, FILTER_POINT);
GetUtils().DrawFullScreenTri(CTexture::s_ptexSceneTarget->GetWidth(), CTexture::s_ptexSceneTarget->GetHeight());
GetUtils().ShEndPass();
gcpRendD3D->FX_PopRenderTarget(0);
// Revert shader flags
pRD->m_RP.m_FlagsShader_RT = prevRTFlags;
}
void CPost3DRenderer::ApplyShaderQuality(EShaderType shaderType)
{
CD3D9Renderer* const __restrict pRD = gcpRendD3D;
// Retrieve quality for shader type
SShaderProfile* pSP = &pRD->m_cEF.m_ShaderProfiles[shaderType];
int nQuality = (int)pSP->GetShaderQuality();
// Apply correct flag set
pRD->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_QUALITY] | g_HWSR_MaskBit[HWSR_QUALITY1]);
pRD->m_RP.m_nShaderQuality = nQuality;
switch (nQuality)
{
case eSQ_Medium:
pRD->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_QUALITY];
break;
case eSQ_High:
pRD->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_QUALITY1];
break;
case eSQ_VeryHigh:
pRD->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_QUALITY];
pRD->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_QUALITY1];
break;
}
}