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.
1197 lines
49 KiB
C++
1197 lines
49 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 "D3DTiledShading.h"
|
|
#include "DriverD3D.h"
|
|
#include "D3DPostProcess.h"
|
|
#include "D3DVolumetricFog.h"
|
|
#include "GraphicsPipeline/FurPasses.h"
|
|
|
|
#include "../Common/Textures/TextureManager.h"
|
|
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#undef AZ_RESTRICTED_SECTION
|
|
#define D3DTILEDSHADING_CPP_SECTION_1 1
|
|
#define D3DTILEDSHADING_CPP_SECTION_2 2
|
|
#define D3DTILEDSHADING_CPP_SECTION_3 3
|
|
#endif
|
|
|
|
#if defined(FEATURE_SVO_GI)
|
|
#include "D3D_SVO.h"
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const uint32 AtlasArrayDim = 64;
|
|
const uint32 SpotTexSize = 512;
|
|
const uint32 DiffuseProbeSize = 32;
|
|
const uint32 SpecProbeSize = 256;
|
|
const uint32 ShadowSampleCount = 16;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// The following structs and constants have to match the shader code
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const uint32 MaxNumTileLights = 255;
|
|
const uint32 LightTileSizeX = 8;
|
|
const uint32 LightTileSizeY = 8;
|
|
const uint32 MaxNumClipVolumes = MAX_DEFERRED_CLIP_VOLUMES;
|
|
|
|
|
|
struct STiledLightCullInfo
|
|
{
|
|
uint32 volumeType;
|
|
uint32 PADDING0;
|
|
Vec2 depthBounds;
|
|
Vec4 posRad;
|
|
Vec4 volumeParams0;
|
|
Vec4 volumeParams1;
|
|
Vec4 volumeParams2;
|
|
}; // 80 bytes
|
|
|
|
struct STiledClipVolumeInfo
|
|
{
|
|
float data;
|
|
};
|
|
|
|
enum ETiledVolumeTypes
|
|
{
|
|
tlVolumeSphere = 1,
|
|
tlVolumeCone = 2,
|
|
tlVolumeOBB = 3,
|
|
tlVolumeSun = 4,
|
|
};
|
|
|
|
enum ETiledLightTypes
|
|
{
|
|
tlTypeProbe = 1,
|
|
tlTypeAmbientPoint = 2,
|
|
tlTypeAmbientProjector = 3,
|
|
tlTypeAmbientArea = 4,
|
|
tlTypeRegularPoint = 5,
|
|
tlTypeRegularProjector = 6,
|
|
tlTypeRegularPointFace = 7,
|
|
tlTypeRegularArea = 8,
|
|
tlTypeSun = 9,
|
|
};
|
|
|
|
// Sun area light parameters (same as in standard deferred shading)
|
|
const float SunDistance = 10000.0f;
|
|
const float SunSourceDiameter = 94.0f; // atan(AngDiameterSun) * 2 * SunDistance, where AngDiameterSun=0.54deg
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace
|
|
{
|
|
STiledLightCullInfo g_tileLightsCull[MaxNumTileLights];
|
|
STiledLightShadeInfo g_tileLightsShade[MaxNumTileLights];
|
|
}
|
|
|
|
CTiledShading::CTiledShading()
|
|
{
|
|
// 16 byte alignment is important for performance on nVidia cards
|
|
STATIC_ASSERT(sizeof(STiledLightCullInfo) % 16 == 0, "STiledLightCullInfo should be 16 byte aligned for GPU performance");
|
|
STATIC_ASSERT(sizeof(STiledLightShadeInfo) % 16 == 0, "STiledLightShadeInfo should be 16 byte aligned for GPU performance");
|
|
|
|
m_dispatchSizeX = 0;
|
|
m_dispatchSizeY = 0;
|
|
|
|
m_nTexStateCompare = 0xFFFFFFFF;
|
|
|
|
m_numAtlasUpdates = 0;
|
|
m_numSkippedLights = 0;
|
|
|
|
m_arrShadowCastingLights.Reserve(16);
|
|
|
|
m_bApplyCaustics = false;
|
|
}
|
|
|
|
|
|
void CTiledShading::CreateResources()
|
|
{
|
|
uint32 dispatchSizeX = gRenDev->GetWidth() / LightTileSizeX + (gRenDev->GetWidth() % LightTileSizeX > 0 ? 1 : 0);
|
|
uint32 dispatchSizeY = gRenDev->GetHeight() / LightTileSizeY + (gRenDev->GetHeight() % LightTileSizeY > 0 ? 1 : 0);
|
|
|
|
if (m_dispatchSizeX == dispatchSizeX && m_dispatchSizeY == dispatchSizeY)
|
|
{
|
|
assert(m_lightCullInfoBuf.m_pBuffer && m_specularProbeAtlas.texArray);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
DestroyResources(false);
|
|
}
|
|
|
|
m_dispatchSizeX = dispatchSizeX;
|
|
m_dispatchSizeY = dispatchSizeY;
|
|
|
|
if (!m_lightCullInfoBuf.m_pBuffer)
|
|
{
|
|
m_lightCullInfoBuf.Create(MaxNumTileLights, sizeof(STiledLightCullInfo), DXGI_FORMAT_UNKNOWN, DX11BUF_DYNAMIC | DX11BUF_STRUCTURED | DX11BUF_BIND_SRV, NULL);
|
|
}
|
|
|
|
if (!m_LightShadeInfoBuf.m_pBuffer)
|
|
{
|
|
m_LightShadeInfoBuf.Create(MaxNumTileLights, sizeof(STiledLightShadeInfo), DXGI_FORMAT_UNKNOWN, DX11BUF_DYNAMIC | DX11BUF_STRUCTURED | DX11BUF_BIND_SRV, NULL);
|
|
}
|
|
|
|
if (!m_tileLightIndexBuf.m_pBuffer)
|
|
{
|
|
PREFAST_SUPPRESS_WARNING(6326)
|
|
DXGI_FORMAT format = MaxNumTileLights < 256 ? DXGI_FORMAT_R8_UINT : DXGI_FORMAT_R16_UINT;
|
|
PREFAST_SUPPRESS_WARNING(6326)
|
|
uint32 stride = MaxNumTileLights < 256 ? sizeof(char) : sizeof(short);
|
|
|
|
m_tileLightIndexBuf.Create(dispatchSizeX * dispatchSizeY * MaxNumTileLights, stride, format, DX11BUF_BIND_SRV | DX11BUF_BIND_UAV, NULL);
|
|
}
|
|
|
|
if (!m_clipVolumeInfoBuf.m_pBuffer)
|
|
{
|
|
m_clipVolumeInfoBuf.Create(MaxNumClipVolumes, sizeof(STiledClipVolumeInfo), DXGI_FORMAT_UNKNOWN, DX11BUF_DYNAMIC | DX11BUF_STRUCTURED | DX11BUF_BIND_SRV, NULL);
|
|
}
|
|
|
|
if (!m_specularProbeAtlas.texArray)
|
|
{
|
|
ETEX_Format specProbeAtlasFormat = eTF_BC6UH;
|
|
#if defined(AZ_PLATFORM_MAC)
|
|
specProbeAtlasFormat = eTF_R9G9B9E5;
|
|
#endif
|
|
m_specularProbeAtlas.texArray = CTexture::CreateTextureArray("$TiledSpecProbeTexArr", eTT_Cube, SpecProbeSize, SpecProbeSize, AtlasArrayDim, IntegerLog2(SpecProbeSize) - 1, 0, specProbeAtlasFormat);
|
|
m_specularProbeAtlas.items.resize(AtlasArrayDim);
|
|
|
|
if (m_specularProbeAtlas.texArray->GetFlags() & FT_FAILED)
|
|
{
|
|
CryFatalError("Couldn't allocate specular probe texture atlas");
|
|
}
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION D3DTILEDSHADING_CPP_SECTION_1
|
|
#include AZ_RESTRICTED_FILE(D3DTiledShading_cpp)
|
|
#endif
|
|
}
|
|
|
|
if (!m_diffuseProbeAtlas.texArray)
|
|
{
|
|
ETEX_Format diffuseProbeAtlasFormat = eTF_BC6UH;
|
|
#if defined(AZ_PLATFORM_MAC)
|
|
diffuseProbeAtlasFormat = eTF_R9G9B9E5;
|
|
#endif
|
|
m_diffuseProbeAtlas.texArray = CTexture::CreateTextureArray("$TiledDiffuseProbeTexArr", eTT_Cube, DiffuseProbeSize, DiffuseProbeSize, AtlasArrayDim, 1, 0, diffuseProbeAtlasFormat);
|
|
m_diffuseProbeAtlas.items.resize(AtlasArrayDim);
|
|
|
|
if (m_diffuseProbeAtlas.texArray->GetFlags() & FT_FAILED)
|
|
{
|
|
CryFatalError("Couldn't allocate diffuse probe texture atlas");
|
|
}
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION D3DTILEDSHADING_CPP_SECTION_2
|
|
#include AZ_RESTRICTED_FILE(D3DTiledShading_cpp)
|
|
#endif
|
|
}
|
|
|
|
if (!m_spotTexAtlas.texArray)
|
|
{
|
|
// Note: BC4 has 4x4 as lowest mipmap
|
|
m_spotTexAtlas.texArray = CTexture::CreateTextureArray("$TiledSpotTexArr", eTT_2D, SpotTexSize, SpotTexSize, AtlasArrayDim, IntegerLog2(SpotTexSize) - 1, 0, eTF_BC4U);
|
|
m_spotTexAtlas.items.resize(AtlasArrayDim);
|
|
|
|
if (m_spotTexAtlas.texArray->GetFlags() & FT_FAILED)
|
|
{
|
|
CryFatalError("Couldn't allocate spot texture atlas");
|
|
}
|
|
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#define AZ_RESTRICTED_SECTION D3DTILEDSHADING_CPP_SECTION_3
|
|
#include AZ_RESTRICTED_FILE(D3DTiledShading_cpp)
|
|
#endif
|
|
}
|
|
|
|
STexState ts1(FILTER_LINEAR, true);
|
|
ts1.SetComparisonFilter(true);
|
|
m_nTexStateCompare = CTexture::GetTexState(ts1);
|
|
}
|
|
|
|
|
|
void CTiledShading::DestroyResources(bool destroyResolutionIndependentResources)
|
|
{
|
|
m_dispatchSizeX = 0;
|
|
m_dispatchSizeY = 0;
|
|
|
|
m_lightCullInfoBuf.Release();
|
|
m_LightShadeInfoBuf.Release();
|
|
m_tileLightIndexBuf.Release();
|
|
m_clipVolumeInfoBuf.Release();
|
|
|
|
if (destroyResolutionIndependentResources)
|
|
{
|
|
m_specularProbeAtlas.items.clear();
|
|
m_diffuseProbeAtlas.items.clear();
|
|
m_spotTexAtlas.items.clear();
|
|
|
|
SAFE_RELEASE_FORCE(m_specularProbeAtlas.texArray);
|
|
SAFE_RELEASE_FORCE(m_diffuseProbeAtlas.texArray);
|
|
SAFE_RELEASE_FORCE(m_spotTexAtlas.texArray);
|
|
}
|
|
}
|
|
|
|
|
|
void CTiledShading::Clear()
|
|
{
|
|
for (uint32 i = 0, si = m_specularProbeAtlas.items.size(); i < si; ++i)
|
|
{
|
|
m_specularProbeAtlas.items[i] = AtlasItem();
|
|
}
|
|
for (uint32 i = 0, si = m_diffuseProbeAtlas.items.size(); i < si; ++i)
|
|
{
|
|
m_diffuseProbeAtlas.items[i] = AtlasItem();
|
|
}
|
|
for (uint32 i = 0, si = m_spotTexAtlas.items.size(); i < si; ++i)
|
|
{
|
|
m_spotTexAtlas.items[i] = AtlasItem();
|
|
}
|
|
|
|
m_numAtlasUpdates = 0;
|
|
m_numSkippedLights = 0;
|
|
}
|
|
|
|
|
|
int CTiledShading::InsertTexture(CTexture* texture, TextureAtlas& atlas, int arrayIndex)
|
|
{
|
|
CD3D9Renderer* const __restrict rd = gcpRendD3D;
|
|
const int frameID = rd->GetFrameID(false);
|
|
|
|
if (texture == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Make sure the texture is loaded already
|
|
if (texture->GetWidthNonVirtual() == 0 && texture->GetHeightNonVirtual() == 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
bool isEditor = gEnv->IsEditor();
|
|
|
|
// Check if texture is already in atlas
|
|
for (uint32 i = 0, si = atlas.items.size(); i < si; ++i)
|
|
{
|
|
if (atlas.items[i].texture == texture)
|
|
{
|
|
bool textureUpToDate = !isEditor ? true : (texture->GetUpdateFrameID() == abs(atlas.items[i].updateFrameID) || texture->GetUpdateFrameID() == 0);
|
|
|
|
if (textureUpToDate)
|
|
{
|
|
if (atlas.items[i].invalid)
|
|
{
|
|
// Texture was processed before and is invalid
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
atlas.items[i].accessFrameID = frameID;
|
|
return (int)i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find least recently used entry in atlas
|
|
if (arrayIndex < 0)
|
|
{
|
|
uint32 minIndex = 0, minValue = 0xFFFFFFFF;
|
|
for (int i = 0, si = atlas.items.size(); i < si; ++i)
|
|
{
|
|
uint32 accessFrameID = atlas.items[i].accessFrameID;
|
|
if (accessFrameID < minValue)
|
|
{
|
|
minValue = accessFrameID;
|
|
minIndex = i;
|
|
}
|
|
}
|
|
|
|
arrayIndex = minIndex;
|
|
}
|
|
|
|
atlas.items[arrayIndex].texture = texture;
|
|
atlas.items[arrayIndex].accessFrameID = frameID;
|
|
atlas.items[arrayIndex].updateFrameID = texture->GetUpdateFrameID();
|
|
atlas.items[arrayIndex].invalid = false;
|
|
|
|
if (texture->GetIsTextureMissing())
|
|
{
|
|
//if Texture is missing than do not do error checking
|
|
return -1;
|
|
}
|
|
|
|
// Check if texture is valid
|
|
if (!texture->IsLoaded() ||
|
|
texture->GetWidthNonVirtual() < atlas.texArray->GetWidthNonVirtual() ||
|
|
texture->GetWidthNonVirtual() != texture->GetHeightNonVirtual() ||
|
|
texture->GetPixelFormat() != atlas.texArray->GetPixelFormat() ||
|
|
(texture->IsStreamed() && texture->IsPartiallyLoaded()))
|
|
{
|
|
atlas.items[arrayIndex].invalid = true;
|
|
|
|
if (!texture->IsLoaded())
|
|
{
|
|
iLog->LogError("TiledShading: Texture not found: %s", texture->GetName());
|
|
}
|
|
else if (texture->IsStreamed() && texture->IsPartiallyLoaded())
|
|
{
|
|
iLog->LogError("TiledShading: Texture not fully streamed so impossible to add: %s (W:%i H:%i F:%s)",
|
|
texture->GetName(), texture->GetWidth(), texture->GetHeight(), texture->GetFormatName());
|
|
}
|
|
else if (texture->GetPixelFormat() != atlas.texArray->GetPixelFormat())
|
|
{
|
|
iLog->LogError("TiledShading: Unsupported texture format: %s (W:%i H:%i F:%s), it has to be equal to the tile-atlas (F:%s), please change the texture's preset by re-exporting with CryTif",
|
|
texture->GetName(), texture->GetWidth(), texture->GetHeight(), texture->GetFormatName(), atlas.texArray->GetFormatName());
|
|
}
|
|
else
|
|
{
|
|
iLog->LogError("TiledShading: Unsupported texture properties: %s (W:%i H:%i F:%s)",
|
|
texture->GetName(), texture->GetWidth(), texture->GetHeight(), texture->GetFormatName());
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Update atlas
|
|
uint32 numSrcMips = (uint32)texture->GetNumMipsNonVirtual();
|
|
uint32 numDstMips = (uint32)atlas.texArray->GetNumMipsNonVirtual();
|
|
uint32 firstSrcMip = IntegerLog2((uint32)texture->GetWidthNonVirtual() / atlas.texArray->GetWidthNonVirtual());
|
|
uint32 numFaces = atlas.texArray->GetTexType() == eTT_Cube ? 6 : 1;
|
|
|
|
CDeviceTexture* pTexArrayDevTex = atlas.texArray->GetDevTexture();
|
|
|
|
//ASSERT_DEVICE_TEXTURE_IS_FIXED(pTexArrayDevTex);
|
|
|
|
for (uint32 i = 0; i < numDstMips; ++i)
|
|
{
|
|
for (uint32 j = 0; j < numFaces; ++j)
|
|
{
|
|
rd->GetDeviceContext().CopySubresourceRegion(
|
|
pTexArrayDevTex->GetBaseTexture(), D3D11CalcSubresource(i, arrayIndex * numFaces + j, numDstMips), 0, 0, 0,
|
|
texture->GetDevTexture()->GetBaseTexture(), D3D11CalcSubresource(i + firstSrcMip, j, numSrcMips), NULL);
|
|
}
|
|
}
|
|
|
|
++m_numAtlasUpdates;
|
|
|
|
return arrayIndex;
|
|
}
|
|
|
|
|
|
AABB RotateAABB(const AABB& aabb, const Matrix33& mat)
|
|
{
|
|
Matrix33 matAbs;
|
|
matAbs.m00 = fabs_tpl(mat.m00);
|
|
matAbs.m01 = fabs_tpl(mat.m01);
|
|
matAbs.m02 = fabs_tpl(mat.m02);
|
|
matAbs.m10 = fabs_tpl(mat.m10);
|
|
matAbs.m11 = fabs_tpl(mat.m11);
|
|
matAbs.m12 = fabs_tpl(mat.m12);
|
|
matAbs.m20 = fabs_tpl(mat.m20);
|
|
matAbs.m21 = fabs_tpl(mat.m21);
|
|
matAbs.m22 = fabs_tpl(mat.m22);
|
|
|
|
Vec3 sz = ((aabb.max - aabb.min) * 0.5f) * matAbs;
|
|
Vec3 pos = ((aabb.max + aabb.min) * 0.5f) * mat;
|
|
|
|
return AABB(pos - sz, pos + sz);
|
|
}
|
|
|
|
void CTiledShading::PrepareEnvironmentProbes(bool& shouldAdd, SRenderLight& renderLight, STiledLightCullInfo& lightCullInfo, STiledLightShadeInfo& lightShadeInfo, Matrix44A& matView, const float invCameraFar, const Vec4& posVS)
|
|
{
|
|
lightCullInfo.volumeType = tlVolumeOBB;
|
|
lightShadeInfo.lightType = tlTypeProbe;
|
|
lightShadeInfo.resIndex = 0xFFFFFFFF;
|
|
|
|
const float uniformAttenuation = renderLight.m_fProbeAttenuation;
|
|
const float edgeFalloffSmoothness = max(renderLight.GetFalloffMax(), 0.001f);
|
|
lightShadeInfo.attenuationParams = Vec2(uniformAttenuation, edgeFalloffSmoothness);
|
|
|
|
AABB aabb = RotateAABB(AABB(-renderLight.m_ProbeExtents, renderLight.m_ProbeExtents), Matrix33(renderLight.m_ObjMatrix));
|
|
aabb = RotateAABB(aabb, Matrix33(matView));
|
|
lightCullInfo.depthBounds = Vec2(posVS.z + aabb.min.z, posVS.z + aabb.max.z) * invCameraFar;
|
|
|
|
const Vec4 u0 = Vec4(renderLight.m_ObjMatrix.GetColumn0().GetNormalized(), 0) * matView;
|
|
const Vec4 u1 = Vec4(renderLight.m_ObjMatrix.GetColumn1().GetNormalized(), 0) * matView;
|
|
const Vec4 u2 = Vec4(renderLight.m_ObjMatrix.GetColumn2().GetNormalized(), 0) * matView;
|
|
lightCullInfo.volumeParams0 = Vec4(u0.x, u0.y, u0.z, renderLight.m_ProbeExtents.x);
|
|
lightCullInfo.volumeParams1 = Vec4(u1.x, u1.y, u1.z, renderLight.m_ProbeExtents.y);
|
|
lightCullInfo.volumeParams2 = Vec4(u2.x, u2.y, u2.z, renderLight.m_ProbeExtents.z);
|
|
|
|
lightShadeInfo.projectorMatrix.SetRow4(0, Vec4(renderLight.m_ObjMatrix.GetColumn0().GetNormalized() / renderLight.m_ProbeExtents.x, 0));
|
|
lightShadeInfo.projectorMatrix.SetRow4(1, Vec4(renderLight.m_ObjMatrix.GetColumn1().GetNormalized() / renderLight.m_ProbeExtents.y, 0));
|
|
lightShadeInfo.projectorMatrix.SetRow4(2, Vec4(renderLight.m_ObjMatrix.GetColumn2().GetNormalized() / renderLight.m_ProbeExtents.z, 0));
|
|
|
|
Vec3 boxProxyMin(-1000000, -1000000, -1000000);
|
|
Vec3 boxProxyMax(1000000, 1000000, 1000000);
|
|
|
|
if (renderLight.m_Flags & DLF_BOX_PROJECTED_CM)
|
|
{
|
|
boxProxyMin = Vec3(-renderLight.m_fBoxLength * 0.5f, -renderLight.m_fBoxWidth * 0.5f, -renderLight.m_fBoxHeight * 0.5f);
|
|
boxProxyMax = Vec3(renderLight.m_fBoxLength * 0.5f, renderLight.m_fBoxWidth * 0.5f, renderLight.m_fBoxHeight * 0.5f);
|
|
}
|
|
|
|
lightShadeInfo.shadowMatrix.SetRow4(0, Vec4(boxProxyMin, 0));
|
|
lightShadeInfo.shadowMatrix.SetRow4(1, Vec4(boxProxyMax, 0));
|
|
|
|
const int arrayIndex = InsertTexture((CTexture*)renderLight.GetSpecularCubemap(), m_specularProbeAtlas, -1);
|
|
if (arrayIndex >= 0)
|
|
{
|
|
if (InsertTexture((CTexture*)renderLight.GetDiffuseCubemap(), m_diffuseProbeAtlas, arrayIndex) >= 0)
|
|
{
|
|
lightShadeInfo.resIndex = arrayIndex;
|
|
}
|
|
else
|
|
{
|
|
shouldAdd = false; // Skip light
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shouldAdd = false; // Skip light
|
|
}
|
|
}
|
|
|
|
void CTiledShading::PrepareRegularAndAmbientLights(bool& shouldAdd, SRenderLight& renderLight, STiledLightCullInfo& lightCullInfo, STiledLightShadeInfo& lightShadeInfo, Matrix44A& matView, const float invCameraFar, const Vec4& posVS,
|
|
const bool ambientLight, CD3D9Renderer* const __restrict rd, const bool areaLightRect, const uint32 lightIdx, const uint32 firstShadowLight, const uint32 curShadowPoolLight, const int nThreadID, const int nRecurseLevel, uint32& numTileLights)
|
|
{
|
|
const float sqrt_2 = 1.414214f; // Scale for cone so that it's base encloses a pyramid base
|
|
|
|
lightCullInfo.volumeType = tlVolumeSphere;
|
|
lightShadeInfo.lightType = ambientLight ? tlTypeAmbientPoint : tlTypeRegularPoint;
|
|
|
|
if (!ambientLight)
|
|
{
|
|
lightShadeInfo.attenuationParams.x = max(lightShadeInfo.attenuationParams.x, 0.001f);
|
|
|
|
// Adjust light intensity so that the intended brightness is reached 1 meter from the light's surface
|
|
// Solve I * 1 / (1 + d/lightsize)^2 = 1
|
|
float intensityMul = 1.0f + 1.0f / lightShadeInfo.attenuationParams.x;
|
|
intensityMul *= intensityMul;
|
|
lightShadeInfo.color.x *= intensityMul;
|
|
lightShadeInfo.color.y *= intensityMul;
|
|
lightShadeInfo.color.z *= intensityMul;
|
|
}
|
|
|
|
// Handle projectors
|
|
if (renderLight.m_Flags & DLF_PROJECT)
|
|
{
|
|
lightCullInfo.volumeType = tlVolumeCone;
|
|
lightShadeInfo.lightType = ambientLight ? tlTypeAmbientProjector : tlTypeRegularProjector;
|
|
lightShadeInfo.resIndex = 0xFFFFFFFF;
|
|
|
|
int arrayIndex = InsertTexture((CTexture*)renderLight.m_pLightImage, m_spotTexAtlas, -1);
|
|
if (arrayIndex >= 0)
|
|
{
|
|
lightShadeInfo.resIndex = (uint32)arrayIndex;
|
|
}
|
|
else
|
|
{
|
|
shouldAdd = false; // Skip light
|
|
return;
|
|
}
|
|
// Prevent culling errors for frustums with large FOVs by slightly enlarging the frustum
|
|
const float frustumAngleDelta = renderLight.m_fLightFrustumAngle > 50 ? 7.5f : 0.0f;
|
|
|
|
Matrix34 objMat = renderLight.m_ObjMatrix;
|
|
objMat.m03 = objMat.m13 = objMat.m23 = 0; // Remove translation
|
|
const Vec3 lightDir = objMat * Vec3(-1, 0, 0);
|
|
lightCullInfo.volumeParams0 = Vec4(lightDir.x, lightDir.y, lightDir.z, 0) * matView;
|
|
lightCullInfo.volumeParams0.w = renderLight.m_fRadius * tanf(DEG2RAD(min(renderLight.m_fLightFrustumAngle + frustumAngleDelta, 89.9f))) * sqrt_2;
|
|
|
|
const Vec3 coneTip = Vec3(lightCullInfo.posRad.x, lightCullInfo.posRad.y, lightCullInfo.posRad.z);
|
|
const Vec3 coneDir = Vec3(-lightCullInfo.volumeParams0.x, -lightCullInfo.volumeParams0.y, -lightCullInfo.volumeParams0.z);
|
|
const AABB coneBounds = AABB::CreateAABBfromCone(Cone(coneTip, coneDir, renderLight.m_fRadius, lightCullInfo.volumeParams0.w));
|
|
lightCullInfo.depthBounds = Vec2(coneBounds.min.z, coneBounds.max.z) * invCameraFar;
|
|
|
|
lightShadeInfo.dirCosAngle = Vec4(lightDir.GetNormalized(), cosf(DEG2RAD(min(renderLight.m_fLightFrustumAngle + frustumAngleDelta, 89.9f))));
|
|
|
|
Matrix44A projMatT;
|
|
CShadowUtils::GetProjectiveTexGen(&renderLight, 0, &projMatT);
|
|
|
|
// Translate into camera space
|
|
projMatT.Transpose();
|
|
const Vec4 vEye(rd->GetViewParameters().vOrigin, 0.0f);
|
|
const Vec4 vecTranslation(vEye.Dot((Vec4&)projMatT.m00), vEye.Dot((Vec4&)projMatT.m10), vEye.Dot((Vec4&)projMatT.m20), vEye.Dot((Vec4&)projMatT.m30));
|
|
projMatT.m03 += vecTranslation.x;
|
|
projMatT.m13 += vecTranslation.y;
|
|
projMatT.m23 += vecTranslation.z;
|
|
projMatT.m33 += vecTranslation.w;
|
|
|
|
lightShadeInfo.projectorMatrix = projMatT;
|
|
}
|
|
|
|
// Handle rectangular area lights
|
|
if (areaLightRect)
|
|
{
|
|
lightCullInfo.volumeType = tlVolumeOBB;
|
|
lightShadeInfo.lightType = ambientLight ? tlTypeAmbientArea : tlTypeRegularArea;
|
|
|
|
const float expensionRadius = renderLight.m_fRadius * 1.08f;
|
|
const Matrix34 areaLightMat = CShadowUtils::GetAreaLightMatrix(&renderLight, Vec3(expensionRadius, expensionRadius, expensionRadius));
|
|
|
|
const Vec4 u0 = Vec4(areaLightMat.GetColumn0().GetNormalized(), 0) * matView;
|
|
const Vec4 u1 = Vec4(areaLightMat.GetColumn1().GetNormalized(), 0) * matView;
|
|
const Vec4 u2 = Vec4(areaLightMat.GetColumn2().GetNormalized(), 0) * matView;
|
|
lightCullInfo.volumeParams0 = Vec4(u0.x, u0.y, u0.z, areaLightMat.GetColumn0().GetLength() * 0.5f);
|
|
lightCullInfo.volumeParams1 = Vec4(u1.x, u1.y, u1.z, areaLightMat.GetColumn1().GetLength() * 0.5f);
|
|
lightCullInfo.volumeParams2 = Vec4(u2.x, u2.y, u2.z, areaLightMat.GetColumn2().GetLength() * 0.5f);
|
|
|
|
const float volumeExtent = renderLight.m_fRadius + max(renderLight.m_fAreaWidth, renderLight.m_fAreaHeight);
|
|
lightCullInfo.depthBounds = Vec2(posVS.z - volumeExtent, posVS.z + volumeExtent) * invCameraFar;
|
|
|
|
float areaFov = renderLight.m_fLightFrustumAngle * 2.0f;
|
|
if (renderLight.m_Flags & DLF_CASTSHADOW_MAPS)
|
|
{
|
|
areaFov = min(areaFov, 135.0f); // Shadow can only cover ~135 degree FOV without looking bad, so we clamp the FOV to hide shadow clipping
|
|
}
|
|
const float cosAngle = cosf(areaFov * (gf_PI / 360.0f));
|
|
|
|
Matrix44 areaLightParams;
|
|
areaLightParams.SetRow4(0, Vec4(renderLight.m_ObjMatrix.GetColumn0().GetNormalized(), 1.0f));
|
|
areaLightParams.SetRow4(1, Vec4(renderLight.m_ObjMatrix.GetColumn1().GetNormalized(), 1.0f));
|
|
areaLightParams.SetRow4(2, Vec4(renderLight.m_ObjMatrix.GetColumn2().GetNormalized(), 1.0f));
|
|
areaLightParams.SetRow4(3, Vec4(renderLight.m_fAreaWidth * 0.5f, renderLight.m_fAreaHeight * 0.5f, 0, cosAngle));
|
|
|
|
lightShadeInfo.projectorMatrix = areaLightParams;
|
|
}
|
|
|
|
// Handle shadow casters
|
|
if (!ambientLight && lightIdx >= firstShadowLight && lightIdx < curShadowPoolLight)
|
|
{
|
|
const int numDLights = rd->m_RP.m_DLights[nThreadID][nRecurseLevel].size();
|
|
const int frustumIdx = renderLight.m_lightId + numDLights;
|
|
const int startIdx = SRendItem::m_StartFrust[nThreadID][frustumIdx];
|
|
const int endIdx = SRendItem::m_EndFrust[nThreadID][frustumIdx];
|
|
|
|
if (endIdx > startIdx && nRecurseLevel < (int)rd->m_RP.m_SMFrustums[nThreadID]->size())
|
|
{
|
|
ShadowMapFrustum& firstFrustum = rd->m_RP.m_SMFrustums[nThreadID][nRecurseLevel][startIdx];
|
|
assert(firstFrustum.bUseShadowsPool);
|
|
|
|
const int numSides = firstFrustum.bOmniDirectionalShadow ? 6 : 1;
|
|
const float kernelSize = firstFrustum.bOmniDirectionalShadow ? 2.5f : 1.5f;
|
|
|
|
if (numTileLights + numSides > MaxNumTileLights)
|
|
{
|
|
shouldAdd = false; // Skip light
|
|
return;
|
|
}
|
|
static ICVar* pShadowAtlasResCVar = iConsole->GetCVar("e_ShadowsPoolSize");
|
|
const Vec2 shadowParams = Vec2(kernelSize * ((float)firstFrustum.nTexSize / (float)pShadowAtlasResCVar->GetIVal()), firstFrustum.fDepthConstBias);
|
|
|
|
static const Vec3 cubeDirs[6] = { Vec3(-1, 0, 0), Vec3(1, 0, 0), Vec3(0, -1, 0), Vec3(0, 1, 0), Vec3(0, 0, -1), Vec3(0, 0, 1) };
|
|
|
|
for (int side = 0; side < numSides; ++side)
|
|
{
|
|
rd->ConfigShadowTexgen(0, &firstFrustum, side);
|
|
Matrix44A shadowMat = rd->m_TempMatrices[0][0];
|
|
|
|
// Translate into camera space
|
|
const Vec4 vEye(rd->GetViewParameters().vOrigin, 0.0f);
|
|
const Vec4 vecTranslation(vEye.Dot((Vec4&)shadowMat.m00), vEye.Dot((Vec4&)shadowMat.m10), vEye.Dot((Vec4&)shadowMat.m20), vEye.Dot((Vec4&)shadowMat.m30));
|
|
shadowMat.m03 += vecTranslation.x;
|
|
shadowMat.m13 += vecTranslation.y;
|
|
shadowMat.m23 += vecTranslation.z;
|
|
shadowMat.m33 += vecTranslation.w;
|
|
|
|
// Pre-multiply by inverse frustum far plane distance
|
|
(Vec4&)shadowMat.m20 *= rd->m_cEF.m_TempVecs[2].x;
|
|
|
|
Vec4 spotParamsVS = Vec4(cubeDirs[side].x, cubeDirs[side].y, cubeDirs[side].z, 0) * matView;
|
|
|
|
// slightly enlarge the frustum to prevent culling errors
|
|
spotParamsVS.w = renderLight.m_fRadius * tanf(DEG2RAD(45.0f + 14.5f)) * sqrt_2;
|
|
|
|
const Vec3 coneTip(lightCullInfo.posRad.x, lightCullInfo.posRad.y, lightCullInfo.posRad.z);
|
|
const Vec3 coneDir(-spotParamsVS.x, -spotParamsVS.y, -spotParamsVS.z);
|
|
const AABB coneBounds = AABB::CreateAABBfromCone(Cone(coneTip, coneDir, renderLight.m_fRadius, spotParamsVS.w));
|
|
const Vec2 depthBoundsVS = Vec2(coneBounds.min.z, coneBounds.max.z) * invCameraFar;
|
|
const Vec2 sideShadowParams = (firstFrustum.nShadowGenMask & (1 << side)) ? shadowParams : Vec2(ZERO);
|
|
|
|
if (side == 0)
|
|
{
|
|
lightShadeInfo.shadowParams = sideShadowParams;
|
|
lightShadeInfo.shadowMatrix = shadowMat;
|
|
lightShadeInfo.shadowChannelIndex = Vec4(
|
|
renderLight.m_ShadowChanMask % 4 == 0,
|
|
renderLight.m_ShadowChanMask % 4 == 1,
|
|
renderLight.m_ShadowChanMask % 4 == 2,
|
|
renderLight.m_ShadowChanMask % 4 == 3
|
|
);
|
|
lightShadeInfo.shadowMaskIndex = renderLight.m_ShadowMaskIndex;
|
|
|
|
if (numSides > 1)
|
|
{
|
|
lightCullInfo.volumeType = tlVolumeCone;
|
|
lightShadeInfo.lightType = tlTypeRegularPointFace;
|
|
lightShadeInfo.resIndex = side;
|
|
lightCullInfo.volumeParams0 = spotParamsVS;
|
|
lightCullInfo.depthBounds = depthBoundsVS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Split point light
|
|
++numTileLights;
|
|
g_tileLightsCull[numTileLights] = lightCullInfo;
|
|
g_tileLightsShade[numTileLights] = lightShadeInfo;
|
|
g_tileLightsShade[numTileLights].shadowParams = sideShadowParams;
|
|
g_tileLightsShade[numTileLights].shadowMatrix = shadowMat;
|
|
g_tileLightsShade[numTileLights].resIndex = side;
|
|
g_tileLightsCull[numTileLights].volumeParams0 = spotParamsVS;
|
|
g_tileLightsCull[numTileLights].depthBounds = depthBoundsVS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTiledShading::PrepareLightList(TArray<SRenderLight>& envProbes, TArray<SRenderLight>& ambientLights, TArray<SRenderLight>& defLights)
|
|
{
|
|
AZ_TRACE_METHOD();
|
|
CD3D9Renderer* const __restrict rd = gcpRendD3D;
|
|
|
|
const float invCameraFar = 1.0f / rd->GetViewParameters().fFar;
|
|
|
|
// Prepare view matrix with flipped z-axis
|
|
Matrix44A matView = rd->m_ViewMatrix;
|
|
matView.m02 *= -1;
|
|
matView.m12 *= -1;
|
|
matView.m22 *= -1;
|
|
matView.m32 *= -1;
|
|
|
|
const int nThreadID = rd->m_RP.m_nProcessThreadID;
|
|
const int nRecurseLevel = SRendItem::m_RecurseLevel[nThreadID];
|
|
const uint32 firstShadowLight = CDeferredShading::Instance().m_nFirstCandidateShadowPoolLight;
|
|
const uint32 curShadowPoolLight = CDeferredShading::Instance().m_nCurrentShadowPoolLight;
|
|
|
|
uint32 numTileLights = 0;
|
|
uint32 numRenderLights = 0;
|
|
uint32 numValidRenderLights = 0;
|
|
|
|
// Reset lights
|
|
ZeroMemory(g_tileLightsCull, sizeof(STiledLightCullInfo) * MaxNumTileLights);
|
|
ZeroMemory(g_tileLightsShade, sizeof(STiledLightShadeInfo) * MaxNumTileLights);
|
|
|
|
TArray<SRenderLight>* lightLists[3] = {
|
|
CRenderer::CV_r_DeferredShadingEnvProbes ? &envProbes : NULL,
|
|
CRenderer::CV_r_DeferredShadingAmbientLights ? &ambientLights : NULL,
|
|
CRenderer::CV_r_DeferredShadingLights ? &defLights : NULL
|
|
};
|
|
|
|
for (uint32 lightListIdx = 0; lightListIdx < 3; ++lightListIdx)
|
|
{
|
|
if (lightLists[lightListIdx] == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (uint32 lightIdx = 0, lightListSize = lightLists[lightListIdx]->size(); lightIdx < lightListSize; ++lightIdx)
|
|
{
|
|
SRenderLight& renderLight = (*lightLists[lightListIdx])[lightIdx];
|
|
STiledLightCullInfo& lightCullInfo = g_tileLightsCull[numTileLights];
|
|
STiledLightShadeInfo& lightShadeInfo = g_tileLightsShade[numTileLights];
|
|
|
|
if (renderLight.m_Flags & (DLF_FAKE | DLF_VOLUMETRIC_FOG_ONLY))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Skip non-ambient area light if support is disabled
|
|
if ((renderLight.m_Flags & DLF_AREA_LIGHT) && !(renderLight.m_Flags & DLF_AMBIENT) && !CRenderer::CV_r_DeferredShadingAreaLights)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
++numRenderLights;
|
|
|
|
if (numTileLights == MaxNumTileLights)
|
|
{
|
|
continue; // Skip light
|
|
}
|
|
// Setup standard parameters
|
|
const bool areaLightRect = (renderLight.m_Flags & DLF_AREA_LIGHT) && renderLight.m_fAreaWidth && renderLight.m_fAreaHeight && renderLight.m_fLightFrustumAngle;
|
|
const float volumeSize = (lightListIdx == 0) ? renderLight.m_ProbeExtents.len() : renderLight.m_fRadius;
|
|
const Vec3 pos = renderLight.GetPosition();
|
|
const Vec3 worldViewPos = rd->GetViewParameters().vOrigin;
|
|
lightShadeInfo.posRad = Vec4(pos.x - worldViewPos.x, pos.y - worldViewPos.y, pos.z - worldViewPos.z, volumeSize);
|
|
const Vec4 posVS = Vec4(pos, 1) * matView;
|
|
lightCullInfo.posRad = Vec4(posVS.x, posVS.y, posVS.z, volumeSize);
|
|
lightShadeInfo.attenuationParams = Vec2(areaLightRect ? (renderLight.m_fAreaWidth + renderLight.m_fAreaHeight) * 0.25f : renderLight.m_fAttenuationBulbSize, renderLight.m_fAreaHeight * 0.5f);
|
|
lightCullInfo.depthBounds = Vec2(posVS.z - volumeSize, posVS.z + volumeSize) * invCameraFar;
|
|
lightShadeInfo.color = Vec4(
|
|
renderLight.m_Color.r,
|
|
renderLight.m_Color.g,
|
|
renderLight.m_Color.b,
|
|
renderLight.m_SpecMult);
|
|
lightShadeInfo.resIndex = 0;
|
|
lightShadeInfo.shadowParams = Vec2(0, 0);
|
|
lightShadeInfo.stencilID0 = renderLight.m_nStencilRef[0] + 1;
|
|
lightShadeInfo.stencilID1 = renderLight.m_nStencilRef[1] + 1;
|
|
|
|
bool shouldAdd = true;
|
|
|
|
// Environment probes
|
|
if (lightListIdx == 0)
|
|
{
|
|
PrepareEnvironmentProbes(shouldAdd, renderLight, lightCullInfo, lightShadeInfo, matView, invCameraFar, posVS);
|
|
}
|
|
// Regular and ambient lights
|
|
else
|
|
{
|
|
const bool ambientLight = (lightListIdx == 1);
|
|
PrepareRegularAndAmbientLights(shouldAdd, renderLight, lightCullInfo, lightShadeInfo, matView, invCameraFar, posVS,
|
|
ambientLight, rd, areaLightRect, lightIdx, firstShadowLight, curShadowPoolLight, nThreadID, nRecurseLevel, numTileLights);
|
|
}
|
|
|
|
if (shouldAdd)
|
|
{
|
|
// Add current light
|
|
++numTileLights;
|
|
++numValidRenderLights;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Invalidate last light in case it got skipped
|
|
if (numTileLights < MaxNumTileLights)
|
|
{
|
|
ZeroMemory(&g_tileLightsCull[numTileLights], sizeof(STiledLightCullInfo));
|
|
ZeroMemory(&g_tileLightsShade[numTileLights], sizeof(STiledLightShadeInfo));
|
|
}
|
|
|
|
m_numSkippedLights = numRenderLights - numValidRenderLights;
|
|
|
|
// Add sun
|
|
if (rd->m_RP.m_pSunLight)
|
|
{
|
|
if (numTileLights < MaxNumTileLights)
|
|
{
|
|
STiledLightCullInfo& lightCullInfo = g_tileLightsCull[numTileLights];
|
|
STiledLightShadeInfo& lightShadeInfo = g_tileLightsShade[numTileLights];
|
|
|
|
lightCullInfo.volumeType = tlVolumeSun;
|
|
lightCullInfo.depthBounds = Vec2(-100000, 100000);
|
|
lightCullInfo.posRad = Vec4(0, 0, 0, 100000);
|
|
|
|
lightShadeInfo.lightType = tlTypeSun;
|
|
lightShadeInfo.attenuationParams = Vec2(SunSourceDiameter, SunSourceDiameter);
|
|
lightShadeInfo.shadowParams = Vec2(1, 0);
|
|
lightShadeInfo.shadowMaskIndex = 0;
|
|
lightShadeInfo.shadowChannelIndex = Vec4(1, 0, 0, 0);
|
|
lightShadeInfo.stencilID0 = lightShadeInfo.stencilID1 = 0;
|
|
|
|
Vec3 sunColor;
|
|
gEnv->p3DEngine->GetGlobalParameter(E3DPARAM_SUN_COLOR, sunColor);
|
|
lightShadeInfo.color = Vec4(sunColor.x, sunColor.y, sunColor.z, gEnv->p3DEngine->GetGlobalParameter(E3DPARAM_SUN_SPECULAR_MULTIPLIER));
|
|
|
|
++numTileLights;
|
|
}
|
|
else
|
|
{
|
|
m_numSkippedLights += 1;
|
|
}
|
|
}
|
|
|
|
#ifndef _RELEASE
|
|
rd->m_RP.m_PS[rd->m_RP.m_nProcessThreadID].m_NumTiledShadingSkippedLights = m_numSkippedLights;
|
|
#endif
|
|
|
|
// Update light buffer
|
|
m_lightCullInfoBuf.UpdateBufferContent(g_tileLightsCull, sizeof(STiledLightCullInfo) * MaxNumTileLights);
|
|
m_LightShadeInfoBuf.UpdateBufferContent(g_tileLightsShade, sizeof(STiledLightShadeInfo) * MaxNumTileLights);
|
|
|
|
|
|
rd->GetVolumetricFog().PrepareLightList(lightLists[0], lightLists[1], lightLists[2], firstShadowLight, curShadowPoolLight);
|
|
}
|
|
|
|
void CTiledShading::PrepareShadowCastersList(TArray<SRenderLight>& defLights)
|
|
{
|
|
uint32 firstShadowLight = CDeferredShading::Instance().m_nFirstCandidateShadowPoolLight;
|
|
uint32 curShadowPoolLight = CDeferredShading::Instance().m_nCurrentShadowPoolLight;
|
|
|
|
m_arrShadowCastingLights.SetUse(0);
|
|
|
|
for (int lightIdx = firstShadowLight; lightIdx < curShadowPoolLight; ++lightIdx)
|
|
{
|
|
const SRenderLight& light = defLights[lightIdx];
|
|
if (light.m_Flags & DLF_CASTSHADOW_MAPS)
|
|
{
|
|
m_arrShadowCastingLights.Add(light.m_lightId);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTiledShading::PrepareClipVolumeList(Vec4* clipVolumeParams)
|
|
{
|
|
STiledClipVolumeInfo clipVolumeInfo[MaxNumClipVolumes];
|
|
for (uint32 volumeIndex = 0; volumeIndex < MaxNumClipVolumes; ++volumeIndex)
|
|
{
|
|
clipVolumeInfo[volumeIndex].data = clipVolumeParams[volumeIndex].w;
|
|
}
|
|
|
|
m_clipVolumeInfoBuf.UpdateBufferContent(clipVolumeInfo, sizeof(STiledClipVolumeInfo) * MaxNumClipVolumes);
|
|
}
|
|
|
|
void CTiledShading::Render(TArray<SRenderLight>& envProbes, TArray<SRenderLight>& ambientLights, TArray<SRenderLight>& defLights, Vec4* clipVolumeParams)
|
|
{
|
|
CD3D9Renderer* const __restrict rd = gcpRendD3D;
|
|
|
|
if (!CTexture::s_ptexHDRTarget) // Sketch mode
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Temporary hack until tiled shading has proper MSAA support
|
|
if (CRenderer::CV_r_DeferredShadingTiled == 2 && CRenderer::CV_r_msaa)
|
|
{
|
|
CRenderer::CV_r_DeferredShadingTiled = 1;
|
|
}
|
|
|
|
// Generate shadow mask. Note that in tiled forward only mode the shadow mask is generated in CDeferredShading::DeferredShadingPass()
|
|
if (CRenderer::CV_r_DeferredShadingTiled > 1)
|
|
{
|
|
PROFILE_LABEL_SCOPE("SHADOWMASK");
|
|
|
|
PrepareShadowCastersList(defLights);
|
|
rd->FX_DeferredShadowMaskGen(m_arrShadowCastingLights);
|
|
|
|
rd->FX_SetActiveRenderTargets(false);
|
|
}
|
|
|
|
PROFILE_LABEL_SCOPE("TILED_SHADING");
|
|
|
|
PrepareClipVolumeList(clipVolumeParams);
|
|
|
|
PrepareLightList(envProbes, ambientLights, defLights);
|
|
|
|
// Make sure HDR target is not bound as RT any more
|
|
rd->FX_PushRenderTarget(0, CTexture::s_ptexSceneSpecularAccMap, NULL);
|
|
|
|
uint64 nPrevRTFlags = rd->m_RP.m_FlagsShader_RT;
|
|
rd->m_RP.m_FlagsShader_RT &= ~(g_HWSR_MaskBit[HWSR_SAMPLE0] | g_HWSR_MaskBit[HWSR_SAMPLE1] | g_HWSR_MaskBit[HWSR_SAMPLE2] | g_HWSR_MaskBit[HWSR_SAMPLE3] | g_HWSR_MaskBit[HWSR_SAMPLE4] | g_HWSR_MaskBit[HWSR_SAMPLE5] | g_HWSR_MaskBit[HWSR_DEBUG0] | g_HWSR_MaskBit[HWSR_APPLY_SSDO]);
|
|
|
|
if (CRenderer::CV_r_DeferredShadingTiled > 1) // Tiled deferred
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
|
|
}
|
|
if (CRenderer::CV_r_DeferredShadingTiled == 4) // Light coverage visualization
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE2];
|
|
}
|
|
if (CRenderer::CV_r_ssdoColorBleeding)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1];
|
|
}
|
|
if (CRenderer::CV_r_SSReflections)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE3];
|
|
}
|
|
|
|
bool isRenderingFur = FurPasses::GetInstance().IsRenderingFur();
|
|
if (CRenderer::CV_r_DeferredShadingSSS || isRenderingFur)
|
|
{
|
|
// Output diffuse accumulation if SSS is enabled or if there are render items using fur
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE4];
|
|
}
|
|
|
|
if IsCVarConstAccess(constexpr) (CRenderer::CV_r_DeferredShadingAreaLights > 0)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE5];
|
|
}
|
|
|
|
if (CRenderer::CV_r_ApplyToonShading > 0)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_APPLY_TOON_SHADING];
|
|
}
|
|
|
|
if (CRenderer::CV_r_ssdo)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_APPLY_SSDO];
|
|
}
|
|
|
|
if (CRenderer::CV_r_SlimGBuffer)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SLIM_GBUFFER];
|
|
}
|
|
|
|
if IsCVarConstAccess(constexpr) (CRenderer::CV_r_DeferredShadingLBuffersFmt == 2)
|
|
{
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEFERRED_RENDER_TARGET_OPTIMIZATION];
|
|
}
|
|
#if defined(FEATURE_SVO_GI)
|
|
rd->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_CUBEMAP0];
|
|
rd->m_RP.m_FlagsShader_RT &= ~g_HWSR_MaskBit[HWSR_DECAL_TEXGEN_2D];
|
|
|
|
if (CSvoRenderer::GetInstance()->IsActive())
|
|
{
|
|
int nModeGI = CSvoRenderer::GetInstance()->GetIntegratioMode();
|
|
|
|
if (nModeGI == 0 && gEnv->pConsole->GetCVar("e_svoTI_UseLightProbes")->GetIVal())
|
|
{ // AO modulates diffuse and specular
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_CUBEMAP0];
|
|
}
|
|
else if (nModeGI <= 1)
|
|
{ // GI replaces diffuse and modulates specular
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DECAL_TEXGEN_2D];
|
|
}
|
|
else if (nModeGI == 2)
|
|
{ // GI replaces diffuse and specular
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_CUBEMAP0];
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DECAL_TEXGEN_2D];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Selectively enables debug mode permutation if a debug parameter is non-zero.
|
|
bool bDebugEnabled = false;
|
|
Vec4 vDebugParams(
|
|
(float)rd->CV_r_DeferredShadingTiledDebugDirect,
|
|
(float)rd->CV_r_DeferredShadingTiledDebugIndirect,
|
|
(float)rd->CV_r_DeferredShadingTiledDebugAccumulation,
|
|
(float)rd->CV_r_DeferredShadingTiledDebugAlbedo);
|
|
if (vDebugParams.Dot(vDebugParams) > 0.0f) // Simple check to see if anything is enabled.
|
|
{
|
|
bDebugEnabled = true;
|
|
rd->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG0];
|
|
}
|
|
|
|
static CCryNameTSCRC techTiledShading("TiledDeferredShading");
|
|
|
|
uint32 nScreenWidth = rd->GetWidth();
|
|
uint32 nScreenHeight = rd->GetHeight();
|
|
uint32 dispatchSizeX = nScreenWidth / LightTileSizeX + (nScreenWidth % LightTileSizeX > 0 ? 1 : 0);
|
|
uint32 dispatchSizeY = nScreenHeight / LightTileSizeY + (nScreenHeight % LightTileSizeY > 0 ? 1 : 0);
|
|
|
|
const bool shaderAvailable = SD3DPostEffectsUtils::ShBeginPass(CShaderMan::s_shDeferredShading, techTiledShading, FEF_DONTSETSTATES);
|
|
if (shaderAvailable) // Temporary workaround for a shader cache issue
|
|
{
|
|
D3DShaderResourceView* pTiledBaseRes[8] = {
|
|
m_LightShadeInfoBuf.GetShaderResourceView(),
|
|
m_specularProbeAtlas.texArray->GetShaderResourceView(),
|
|
m_diffuseProbeAtlas.texArray->GetShaderResourceView(),
|
|
m_spotTexAtlas.texArray->GetShaderResourceView(),
|
|
CTexture::s_ptexRT_ShadowPool->GetShaderResourceView(),
|
|
CTextureManager::Instance()->GetDefaultTexture("ShadowJitterMap")->GetShaderResourceView(),
|
|
m_lightCullInfoBuf.GetShaderResourceView(),
|
|
m_clipVolumeInfoBuf.GetShaderResourceView(),
|
|
};
|
|
rd->m_DevMan.BindSRV(eHWSC_Compute, pTiledBaseRes, 16, 8);
|
|
|
|
CTexture* texClipVolumeIndex = CTexture::s_ptexVelocity;
|
|
CTexture* pTexCaustics = m_bApplyCaustics ? CTexture::s_ptexSceneTargetR11G11B10F[1] : CTextureManager::Instance()->GetBlackTexture();
|
|
|
|
CTexture* ptexGiDiff = CTextureManager::Instance()->GetBlackTexture();
|
|
CTexture* ptexGiSpec = CTextureManager::Instance()->GetBlackTexture();
|
|
|
|
#if defined(FEATURE_SVO_GI)
|
|
if (CSvoRenderer::GetInstance()->IsActive() && CSvoRenderer::GetInstance()->GetSpecularFinRT())
|
|
{
|
|
ptexGiDiff = (CTexture*)CSvoRenderer::GetInstance()->GetDiffuseFinRT();
|
|
ptexGiSpec = (CTexture*)CSvoRenderer::GetInstance()->GetSpecularFinRT();
|
|
}
|
|
#endif
|
|
|
|
CTexture* ptexDepth = CTexture::s_ptexZTarget;
|
|
if (isRenderingFur)
|
|
{
|
|
ptexDepth = CTexture::s_ptexFurZTarget;
|
|
}
|
|
|
|
D3DShaderResourceView* pDeferredShadingRes[13] = {
|
|
ptexDepth->GetShaderResourceView(),
|
|
CTexture::s_ptexSceneNormalsMap->GetShaderResourceView(),
|
|
CTexture::s_ptexSceneSpecular->GetShaderResourceView(),
|
|
CTexture::s_ptexSceneDiffuse->GetShaderResourceView(),
|
|
CTexture::s_ptexShadowMask->GetShaderResourceView(),
|
|
CTexture::s_ptexSceneNormalsBent->GetShaderResourceView(),
|
|
CTexture::s_ptexHDRTargetScaledTmp[0]->GetShaderResourceView(),
|
|
CTextureManager::Instance()->GetDefaultTexture("EnvironmentBRDF")->GetShaderResourceView(),
|
|
texClipVolumeIndex->GetShaderResourceView(),
|
|
CTexture::s_ptexAOColorBleed->GetShaderResourceView(),
|
|
ptexGiDiff->GetShaderResourceView(),
|
|
ptexGiSpec->GetShaderResourceView(),
|
|
pTexCaustics->GetShaderResourceView(),
|
|
};
|
|
rd->m_DevMan.BindSRV(eHWSC_Compute, pDeferredShadingRes, 0, 13);
|
|
|
|
if (bDebugEnabled)
|
|
{
|
|
static CCryNameR paramDebug("LightingDebugParams");
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramDebug, &vDebugParams, 1);
|
|
}
|
|
|
|
static CCryNameR paramProj("ProjParams");
|
|
Vec4 vParamProj(rd->m_ProjMatrix.m00, rd->m_ProjMatrix.m11, rd->m_ProjMatrix.m20, rd->m_ProjMatrix.m21);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramProj, &vParamProj, 1);
|
|
|
|
static CCryNameR paramScreenSize("ScreenSize");
|
|
float fScreenWidth = (float)nScreenWidth;
|
|
float fScreenHeight = (float)nScreenHeight;
|
|
Vec4 vParamScreenSize(fScreenWidth, fScreenHeight, 1.0f / fScreenWidth, 1.0f / fScreenHeight);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramScreenSize, &vParamScreenSize, 1);
|
|
|
|
Vec3 worldViewPos = rd->GetViewParameters().vOrigin;
|
|
static CCryNameR paramWorldViewPos("WorldViewPos");
|
|
Vec4 vParamWorldViewPos(worldViewPos.x, worldViewPos.y, worldViewPos.z, 0);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramWorldViewPos, &vParamWorldViewPos, 1);
|
|
|
|
SD3DPostEffectsUtils::UpdateFrustumCorners();
|
|
static CCryNameR paramFrustumTL("FrustumTL");
|
|
Vec4 vParamFrustumTL(SD3DPostEffectsUtils::m_vLT.x, SD3DPostEffectsUtils::m_vLT.y, SD3DPostEffectsUtils::m_vLT.z, 0);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramFrustumTL, &vParamFrustumTL, 1);
|
|
static CCryNameR paramFrustumTR("FrustumTR");
|
|
Vec4 vParamFrustumTR(SD3DPostEffectsUtils::m_vRT.x, SD3DPostEffectsUtils::m_vRT.y, SD3DPostEffectsUtils::m_vRT.z, 0);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramFrustumTR, &vParamFrustumTR, 1);
|
|
static CCryNameR paramFrustumBL("FrustumBL");
|
|
Vec4 vParamFrustumBL(SD3DPostEffectsUtils::m_vLB.x, SD3DPostEffectsUtils::m_vLB.y, SD3DPostEffectsUtils::m_vLB.z, 0);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramFrustumBL, &vParamFrustumBL, 1);
|
|
|
|
Vec3 sunDir = gEnv->p3DEngine->GetSunDirNormalized();
|
|
static CCryNameR paramSunDir("SunDir");
|
|
Vec4 vParamSunDir(sunDir.x, sunDir.y, sunDir.z, SunDistance);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramSunDir, &vParamSunDir, 1);
|
|
|
|
Vec3 sunColor;
|
|
gEnv->p3DEngine->GetGlobalParameter(E3DPARAM_SUN_COLOR, sunColor);
|
|
static CCryNameR paramSunColor("SunColor");
|
|
Vec4 vParamSunColor(sunColor.x, sunColor.y, sunColor.z, gEnv->p3DEngine->GetGlobalParameter(E3DPARAM_SUN_SPECULAR_MULTIPLIER));
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramSunColor, &vParamSunColor, 1);
|
|
|
|
static CCryNameR paramSSDO("SSDOParams");
|
|
Vec4 vParamSSDO(CRenderer::CV_r_ssdoAmountDirect, CRenderer::CV_r_ssdoAmountAmbient, CRenderer::CV_r_ssdoAmountReflection, 0);
|
|
|
|
#if defined(FEATURE_SVO_GI)
|
|
if (CSvoRenderer::GetInstance()->IsActive())
|
|
{
|
|
vParamSSDO *= CSvoRenderer::GetInstance()->GetSsaoAmount();
|
|
}
|
|
#endif
|
|
|
|
Vec4 vParamSSDONull(0, 0, 0, 0);
|
|
CShaderMan::s_shDeferredShading->FXSetCSFloat(paramSSDO, CRenderer::CV_r_ssdo ? &vParamSSDO : &vParamSSDONull, 1);
|
|
|
|
rd->FX_Commit();
|
|
|
|
ID3D11UnorderedAccessView* pUAV[3] = {
|
|
m_tileLightIndexBuf.GetUnorderedAccessView(),
|
|
CTexture::s_ptexHDRTarget->GetDeviceUAV(),
|
|
CTexture::s_ptexSceneTargetR11G11B10F[0]->GetDeviceUAV(),
|
|
};
|
|
rd->GetDeviceContext().CSSetUnorderedAccessViews(0, 3, pUAV, NULL);
|
|
|
|
rd->m_DevMan.Dispatch(dispatchSizeX, dispatchSizeY, 1);
|
|
SD3DPostEffectsUtils::ShEndPass();
|
|
}
|
|
|
|
ID3D11UnorderedAccessView* pUAVNull[3] = { NULL };
|
|
rd->GetDeviceContext().CSSetUnorderedAccessViews(0, 3, pUAVNull, NULL);
|
|
|
|
D3DShaderResourceView* pSRVNull[13] = { NULL };
|
|
rd->m_DevMan.BindSRV(eHWSC_Compute, pSRVNull, 0, 13);
|
|
rd->m_DevMan.BindSRV(eHWSC_Compute, pSRVNull, 16, 8);
|
|
|
|
rd->FX_PopRenderTarget(0);
|
|
|
|
rd->m_RP.m_FlagsShader_RT = nPrevRTFlags;
|
|
|
|
// Output debug information
|
|
if (CRenderer::CV_r_DeferredShadingTiled == 3)
|
|
{
|
|
rd->Draw2dLabel(20, 60, 2.0f, Col_Blue, false, "Tiled Shading Debug");
|
|
rd->Draw2dLabel(20, 95, 1.7f, m_numSkippedLights > 0 ? Col_Red : Col_Blue, false, "Skipped Lights: %i", m_numSkippedLights);
|
|
rd->Draw2dLabel(20, 120, 1.7f, Col_Blue, false, "Atlas Updates: %i", m_numAtlasUpdates);
|
|
}
|
|
|
|
m_bApplyCaustics = false; // Reset flag
|
|
}
|
|
|
|
void CTiledShading::BindForwardShadingResources(CShader*, EHWShaderClass shaderType)
|
|
{
|
|
if (!CRenderer::CV_r_DeferredShadingTiled || !m_dispatchSizeX || !m_dispatchSizeY)
|
|
{
|
|
return;
|
|
}
|
|
AZ_TRACE_METHOD();
|
|
|
|
CD3D9Renderer* const __restrict rd = gcpRendD3D;
|
|
|
|
CTexture* ptexGiDiff = CTextureManager::Instance()->GetBlackTexture();
|
|
CTexture* ptexGiSpec = CTextureManager::Instance()->GetBlackTexture();
|
|
CTexture* ptexRsmCol = NULL;
|
|
CTexture* ptexRsmNor = NULL;
|
|
|
|
#if defined(FEATURE_SVO_GI)
|
|
if (CSvoRenderer::GetInstance()->IsActive() && CSvoRenderer::GetInstance()->GetSpecularFinRT())
|
|
{
|
|
ptexGiDiff = (CTexture*)CSvoRenderer::GetInstance()->GetDiffuseFinRT();
|
|
ptexGiSpec = (CTexture*)CSvoRenderer::GetInstance()->GetSpecularFinRT();
|
|
ptexRsmCol = (CTexture*)CSvoRenderer::GetInstance()->GetRsmPoolCol();
|
|
ptexRsmNor = (CTexture*)CSvoRenderer::GetInstance()->GetRsmPoolNor();
|
|
}
|
|
#endif
|
|
|
|
D3DShaderResourceView* pTiledBaseRes[12] = {
|
|
m_LightShadeInfoBuf.GetShaderResourceView(),
|
|
m_specularProbeAtlas.texArray->GetShaderResourceView(),
|
|
m_diffuseProbeAtlas.texArray->GetShaderResourceView(),
|
|
m_spotTexAtlas.texArray->GetShaderResourceView(),
|
|
CTexture::s_ptexRT_ShadowPool->GetShaderResourceView(),
|
|
CTextureManager::Instance()->GetDefaultTexture("ShadowJitterMap")->GetShaderResourceView(),
|
|
m_tileLightIndexBuf.GetShaderResourceView(),
|
|
m_clipVolumeInfoBuf.GetShaderResourceView(),
|
|
ptexGiDiff->GetShaderResourceView(),
|
|
ptexGiSpec->GetShaderResourceView(),
|
|
ptexRsmCol ? ptexRsmCol->GetShaderResourceView() : NULL,
|
|
ptexRsmNor ? ptexRsmNor->GetShaderResourceView() : NULL,
|
|
};
|
|
|
|
rd->m_DevMan.BindSRV(shaderType, pTiledBaseRes, 16, 12);
|
|
|
|
D3DSamplerState* pSamplers[1] = {
|
|
(D3DSamplerState*)CTexture::s_TexStates[m_nTexStateCompare].m_pDeviceState,
|
|
};
|
|
rd->m_DevMan.BindSampler(shaderType, pSamplers, 14, 1);
|
|
}
|
|
|
|
|
|
void CTiledShading::UnbindForwardShadingResources(EHWShaderClass shaderType)
|
|
{
|
|
if (!CRenderer::CV_r_DeferredShadingTiled)
|
|
{
|
|
return;
|
|
}
|
|
AZ_TRACE_METHOD();
|
|
|
|
CD3D9Renderer* const __restrict rd = gcpRendD3D;
|
|
|
|
D3DShaderResourceView* pNullViews[12] = { NULL };
|
|
rd->m_DevMan.BindSRV(shaderType, pNullViews, 16, 12);
|
|
|
|
D3DSamplerState* pNullSamplers[1] = { NULL };
|
|
rd->m_DevMan.BindSampler(shaderType, pNullSamplers, 14, 1);
|
|
}
|
|
|
|
struct STiledLightShadeInfo* CTiledShading::GetTiledLightShadeInfo()
|
|
{
|
|
return &g_tileLightsShade[0];
|
|
}
|