Skip to content

Commit

Permalink
Redo shadow cascade blur
Browse files Browse the repository at this point in the history
Gaussian blur using linear interpolation, decreasing
filter radius based on the cascade.
  • Loading branch information
pr0bability committed Jan 14, 2025
1 parent 75ff323 commit 790f8d1
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 102 deletions.
4 changes: 2 additions & 2 deletions resource/NewVegasReloaded.dll.defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -602,8 +602,7 @@ Trees = true # Wether to include Trees when rendering sh

[_Shaders.ShadowsExteriors.Main]
PostProcess = true # Wether to render shadows as a post process effect. Currently the only supported mode.
BlurShadowMaps = true # Blur shadow maps for soft shadows.
Darkness = 0.65 # Darkness of shadows.
Darkness = 0.75 # Darkness of shadows.
Enabled = true # Enable sun shadows using rendered shadow maps.
NightMinDarkness = 0.35 # Darkness of shadows during dark moon phase. Set to 0 to disable shadows completely when there is no moon.
Quality = 2 # Not used.
Expand Down Expand Up @@ -670,6 +669,7 @@ RenderDistance = 12000 # Max distance at which to compute screen s

[_Shaders.ShadowsExteriors.ShadowMaps]
CascadeResolution = 2 # Resolution used for shadow cascades. 0: 1024, 1: 1536, 2: 2048
Prefilter = true # Enable prefiltering the shadow maps. Prefiltering is a blur.
MSAA = true # Enable multisample antialiasing for shadow maps.
Mipmaps = false # DISABLED. Enable mipmaps for the shadow maps.
Anisotropy = 0 # DISABLED. Anisotropic filteric for mipmaps, 0: disabled, 1: 8x, 2: 16x.
Expand Down
81 changes: 42 additions & 39 deletions src/core/ShadowManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,6 @@ void ShadowManager::RenderShadowMaps() {
Shadows->Constants.ShadowViewProj = Shadows->GetCascadeViewProj(ShadowMap, SunDir);

RenderShadowMap(ShadowMap, &Shadows->Constants.ShadowViewProj);
// if(ShadowsExteriors->BlurShadowMaps) BlurShadowMap(ShadowMap);

std::string message = "ShadowManager::RenderShadowMap ";
message += std::to_string(i);
Expand All @@ -634,6 +633,8 @@ void ShadowManager::RenderShadowMaps() {
if (Shadows->ShadowAtlasSurfaceMSAA)
Device->StretchRect(Shadows->ShadowAtlasSurfaceMSAA, NULL, Shadows->ShadowAtlasSurface, NULL, D3DTEXF_LINEAR);

if (Shadows->Settings.ShadowMaps.Prefilter) BlurShadowAtlas();

if (Shadows->Settings.ShadowMaps.Mipmaps)
Shadows->ShadowAtlasTexture->GenerateMipSubLevels();

Expand Down Expand Up @@ -732,42 +733,44 @@ void ShadowManager::RenderShadowMaps() {
/*
* Filters the Shadow Map of given index using a 2 pass gaussian blur
*/
//void ShadowManager::BlurShadowMap(ShadowsExteriorEffect::ShadowMapSettings* ShadowMap) {
// IDirect3DDevice9* Device = TheRenderManager->device;
// NiDX9RenderState* RenderState = TheRenderManager->renderState;
// IDirect3DTexture9* SourceShadowMap = ShadowMap->ShadowMapTexture;
// IDirect3DSurface9* TargetShadowMap = ShadowMap->ShadowMapSurface;
//
// Device->SetDepthStencilSurface(NULL);
// RenderState->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE, RenderStateArgs);
// RenderState->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE, RenderStateArgs);
// RenderState->SetVertexShader(ShadowMapBlurVertex->ShaderHandle, false);
// RenderState->SetPixelShader(ShadowMapBlurPixel->ShaderHandle, false);
// RenderState->SetFVF(FrameFVF, false);
// Device->SetStreamSource(0, ShadowMap->BlurShadowVertexBuffer, 0, sizeof(FrameVS));
// Device->SetRenderTarget(0, TargetShadowMap);
//
// // Pass map resolution to shader as a constant
// D3DXVECTOR4 inverseRes = { ShadowMap->ShadowMapInverseResolution, ShadowMap->ShadowMapInverseResolution, 0.0f, 0.0f };
// ShadowMapBlurPixel->SetShaderConstantF(0, &inverseRes, 1);
// RenderState->SetTexture(0, SourceShadowMap);
//
// // blur in two passes, vertically and horizontally
// D3DXVECTOR4 Blur[2] = {
// D3DXVECTOR4(1.0f, 0.0f, 0.0f, 0.0f),
// D3DXVECTOR4(0.0f, 1.0f, 0.0f, 0.0f),
// };
//
// for (int i = 0; i < 2; i++) {
// // set blur direction shader constants
// ShadowMapBlurPixel->SetShaderConstantF(1, &Blur[i], 1);
//
// Device->BeginScene();
// Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); // draw call to execute the shader
// Device->EndScene();
// }
//
// RenderState->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE, RenderStateArgs);
// RenderState->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_TRUE, RenderStateArgs);
//}
void ShadowManager::BlurShadowAtlas() {
ShadowsExteriorEffect* Shadows = TheShaderManager->Effects.ShadowsExteriors;

IDirect3DDevice9* Device = TheRenderManager->device;
NiDX9RenderState* RenderState = TheRenderManager->renderState;
IDirect3DTexture9* SourceShadowMap = Shadows->ShadowAtlasTexture;
IDirect3DSurface9* TargetShadowMap = Shadows->ShadowAtlasSurface;

Device->SetDepthStencilSurface(NULL);
RenderState->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE, RenderStateArgs);
RenderState->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE, RenderStateArgs);
RenderState->SetVertexShader(ShadowMapBlurVertex->ShaderHandle, false);
RenderState->SetPixelShader(ShadowMapBlurPixel->ShaderHandle, false);
RenderState->SetFVF(FrameFVF, false);
Device->SetStreamSource(0, Shadows->ShadowAtlasVertexBuffer, 0, sizeof(FrameVS));
Device->SetRenderTarget(0, TargetShadowMap);

// Pass map resolution to shader as a constant
D3DXVECTOR4 inverseRes = { Shadows->ShadowAtlasCascadeTexelSize, Shadows->ShadowAtlasCascadeTexelSize, 0.0f, 0.0f };
ShadowMapBlurPixel->SetShaderConstantF(0, &inverseRes, 1);
RenderState->SetTexture(0, SourceShadowMap);

// blur in two passes, vertically and horizontally
D3DXVECTOR4 Blur[2] = {
D3DXVECTOR4(1.0f, 0.0f, 0.0f, 0.0f),
D3DXVECTOR4(0.0f, 1.0f, 0.0f, 0.0f),
};

for (int i = 0; i < 2; i++) {
// set blur direction shader constants
ShadowMapBlurPixel->SetShaderConstantF(1, &Blur[i], 1);

Device->BeginScene();
Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); // draw call to execute the shader
Device->EndScene();
}

RenderState->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE, RenderStateArgs);
RenderState->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_TRUE, RenderStateArgs);
}

2 changes: 1 addition & 1 deletion src/core/ShadowManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ShadowManager { // Never disposed
void RenderShadowCubeMap(ShadowSceneLight** Lights, UInt32 LightIndex);
void RenderShadowSpotlight(NiSpotLight** Lights, UInt32 LightIndex);
void RenderShadowMaps();
void BlurShadowMap(ShadowsExteriorEffect::ShadowMapSettings* ShadowMap);
void BlurShadowAtlas();

ShadowRenderPass* geometryPass;
AlphaShadowRenderPass* alphaPass;
Expand Down
33 changes: 23 additions & 10 deletions src/effects/ShadowsExterior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ void ShadowsExteriorEffect::UpdateSettings() {
Settings.Exteriors.OrthoMapResolution = TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.Main", "OrthoMapResolution");
Settings.Exteriors.ShadowMapFarPlane = TheSettingManager->GetSettingF("Shaders.ShadowsExteriors.Main", "ShadowMapFarPlane");
Settings.Exteriors.ShadowMode = TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.Main", "ShadowMode");
Settings.Exteriors.BlurShadowMaps = TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.Main", "BlurShadowMaps");
Settings.Exteriors.UsePointShadowsDay = TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.Main", "UsePointShadowsDay");
Settings.Exteriors.UsePointShadowsNight = TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.Main", "UsePointShadowsNight");

Expand Down Expand Up @@ -96,6 +95,8 @@ void ShadowsExteriorEffect::UpdateSettings() {
Settings.ShadowMaps.Anisotropy = (std::clamp(TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.ShadowMaps", "Anisotropy"), 0, 2)) * 8;*/

Settings.ShadowMaps.Prefilter = TheSettingManager->GetSettingI("Shaders.ShadowsExteriors.ShadowMaps", "Prefilter");

//Shadows Cascade settings
GetCascadeDepths();

Expand Down Expand Up @@ -202,21 +203,25 @@ void ShadowsExteriorEffect::RegisterConstants() {
void ShadowsExteriorEffect::RegisterTextures() {
ULONG ShadowMapSize = Settings.ShadowMaps.CascadeResolution;
ULONG ShadowCubeMapSize = Settings.Interiors.ShadowCubeMapSize;
ULONG ShadowAtlasSize = ShadowMapSize * 2;

TheTextureManager->InitTexture("TESR_ShadowAtlas", &ShadowAtlasTexture, &ShadowAtlasSurface, ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_G32R32F, Settings.ShadowMaps.Mipmaps);
TheTextureManager->InitTexture("TESR_ShadowAtlas", &ShadowAtlasTexture, &ShadowAtlasSurface, ShadowAtlasSize, ShadowAtlasSize, D3DFMT_G32R32F, Settings.ShadowMaps.Mipmaps);

if (!Settings.ShadowMaps.MSAA)
TheRenderManager->device->CreateDepthStencilSurface(ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &ShadowAtlasDepthSurface, NULL);
TheRenderManager->device->CreateDepthStencilSurface(ShadowAtlasSize, ShadowAtlasSize, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &ShadowAtlasDepthSurface, NULL);
else {
TheRenderManager->device->CreateRenderTarget(ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_G32R32F, D3DMULTISAMPLE_4_SAMPLES, 0, 0, &ShadowAtlasSurfaceMSAA, NULL);
TheRenderManager->device->CreateDepthStencilSurface(ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_D24S8, D3DMULTISAMPLE_4_SAMPLES, 0, true, &ShadowAtlasDepthSurface, NULL);
TheRenderManager->device->CreateRenderTarget(ShadowAtlasSize, ShadowAtlasSize, D3DFMT_G32R32F, D3DMULTISAMPLE_4_SAMPLES, 0, 0, &ShadowAtlasSurfaceMSAA, NULL);
TheRenderManager->device->CreateDepthStencilSurface(ShadowAtlasSize, ShadowAtlasSize, D3DFMT_D24S8, D3DMULTISAMPLE_4_SAMPLES, 0, true, &ShadowAtlasDepthSurface, NULL);
}

for (int i = 0; i <= MapLod; i++) {
ShadowMaps[i].ShadowMapViewPort = { i % 2 == 0 ? 0 : ShadowMapSize, i < 2 ? 0 : ShadowMapSize, ShadowMapSize, ShadowMapSize, 0.0f, 1.0f};
ShadowMaps[i].ShadowMapViewPort = { i % 2 == 0 ? 0 : ShadowMapSize, i < 2 ? 0 : ShadowMapSize, ShadowMapSize, ShadowMapSize, 0.0f, 1.0f };
ShadowMaps[i].ShadowMapInverseResolution = 1.0f / (float)ShadowMapSize;
}

TheShaderManager->CreateFrameVertex(ShadowAtlasSize, ShadowAtlasSize, &ShadowAtlasVertexBuffer);
ShadowAtlasCascadeTexelSize = 1.0f / (float)ShadowAtlasSize;

// ortho texture
ULONG orthoMapRes = Settings.Exteriors.OrthoMapResolution;
TheTextureManager->InitTexture("TESR_OrthoMapBuffer", &ShadowMapOrthoTexture, &ShadowMapOrthoSurface, orthoMapRes, orthoMapRes, D3DFMT_G32R32F);
Expand Down Expand Up @@ -275,23 +280,31 @@ void ShadowsExteriorEffect::RecreateTextures(bool cascades, bool ortho, bool cub
ShadowAtlasDepthSurface->Release();
ShadowAtlasDepthSurface = nullptr;
};
if (ShadowAtlasVertexBuffer) {
ShadowAtlasVertexBuffer->Release();
ShadowAtlasVertexBuffer = nullptr;
}

ULONG ShadowMapSize = Settings.ShadowMaps.CascadeResolution;
ULONG ShadowAtlasSize = ShadowMapSize * 2;

TheTextureManager->InitTexture("TESR_ShadowAtlas", &ShadowAtlasTexture, &ShadowAtlasSurface, ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_G32R32F, Settings.ShadowMaps.Mipmaps);
TheTextureManager->InitTexture("TESR_ShadowAtlas", &ShadowAtlasTexture, &ShadowAtlasSurface, ShadowAtlasSize, ShadowAtlasSize, D3DFMT_G32R32F, Settings.ShadowMaps.Mipmaps);

if (!Settings.ShadowMaps.MSAA)
TheRenderManager->device->CreateDepthStencilSurface(ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &ShadowAtlasDepthSurface, NULL);
TheRenderManager->device->CreateDepthStencilSurface(ShadowAtlasSize, ShadowAtlasSize, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, true, &ShadowAtlasDepthSurface, NULL);
else {
TheRenderManager->device->CreateRenderTarget(ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_G32R32F, D3DMULTISAMPLE_4_SAMPLES, 0, 0, &ShadowAtlasSurfaceMSAA, NULL);
TheRenderManager->device->CreateDepthStencilSurface(ShadowMapSize * 2, ShadowMapSize * 2, D3DFMT_D24S8, D3DMULTISAMPLE_4_SAMPLES, 0, true, &ShadowAtlasDepthSurface, NULL);
TheRenderManager->device->CreateRenderTarget(ShadowAtlasSize, ShadowAtlasSize, D3DFMT_G32R32F, D3DMULTISAMPLE_4_SAMPLES, 0, 0, &ShadowAtlasSurfaceMSAA, NULL);
TheRenderManager->device->CreateDepthStencilSurface(ShadowAtlasSize, ShadowAtlasSize, D3DFMT_D24S8, D3DMULTISAMPLE_4_SAMPLES, 0, true, &ShadowAtlasDepthSurface, NULL);
}

for (int i = 0; i <= MapLod; i++) {
ShadowMaps[i].ShadowMapViewPort = { i % 2 == 0 ? 0 : ShadowMapSize, i < 2 ? 0 : ShadowMapSize, ShadowMapSize, ShadowMapSize, 0.0f, 1.0f };
ShadowMaps[i].ShadowMapInverseResolution = 1.0f / (float)ShadowMapSize;
}

TheShaderManager->CreateFrameVertex(ShadowAtlasSize, ShadowAtlasSize, &ShadowAtlasVertexBuffer);
ShadowAtlasCascadeTexelSize = 1.0f / (float)ShadowAtlasSize;

SunShadowsEffect* sunShadows = TheShaderManager->Effects.SunShadows;
ShaderTextureValue* Sampler;
for (UInt32 c = 0; c < sunShadows->TextureShaderValuesCount; c++) {
Expand Down
6 changes: 4 additions & 2 deletions src/effects/ShadowsExterior.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class ShadowsExteriorEffect : public EffectRecord
D3DXMATRIX ShadowCameraToLight;
D3DVIEWPORT9 ShadowMapViewPort;
frustum ShadowMapFrustum;
IDirect3DVertexBuffer9* BlurShadowVertexBuffer;
FormsStruct Forms;
float ShadowMapInverseResolution;
float ShadowMapRadius;
Expand All @@ -74,14 +73,14 @@ class ShadowsExteriorEffect : public EffectRecord

struct ShadowMapStruct {
int CascadeResolution;
bool Prefilter;
bool MSAA;
bool Mipmaps;
int Anisotropy;
};

struct ExteriorsStruct {
bool Enabled;
bool BlurShadowMaps;
int Quality;
int OrthoMapResolution;
float OrthoRadius;
Expand Down Expand Up @@ -144,6 +143,9 @@ class ShadowsExteriorEffect : public EffectRecord
IDirect3DSurface9* ShadowAtlasSurfaceMSAA;
IDirect3DSurface9* ShadowAtlasDepthSurface;

IDirect3DVertexBuffer9* ShadowAtlasVertexBuffer;
float ShadowAtlasCascadeTexelSize;

// Ortho shadows for various effects.
IDirect3DTexture9* ShadowMapOrthoTexture;
IDirect3DSurface9* ShadowMapOrthoSurface;
Expand Down
109 changes: 61 additions & 48 deletions src/hlsl/NewVegas/Shaders/Shadows/ShadowMapBlur.pso.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,72 @@ struct VSOUT
float2 UVCoord : TEXCOORD0;
};

#define cKernelSize 12
float4 Blur3(float2 uv) {
float weights[1] = { 0.5f };
float offsets[1] = { 0.6f };

float2 offset = BlurDirection * offsets[0] * TESR_ReciprocalResolution.x;

float4 Blur(uniform float2 OffsetMask, uniform float2 uv)
{
float4 color = tex2Dlod(SourceBuffer, float4(uv.xy + offset, 0.0f, 0.0f)) * weights[0];
color += tex2Dlod(SourceBuffer, float4(uv.xy - offset, 0.0f, 0.0f)) * weights[0];

float blurRadius = 0.5;
float BlurWeights[cKernelSize] =
{
0.057424882f,
0.058107773f,
0.061460144f,
0.071020611f,
0.088092873f,
0.106530916f,
0.106530916f,
0.088092873f,
0.071020611f,
0.061460144f,
0.058107773f,
0.057424882f
};

float2 BlurOffsets[cKernelSize] =
{
float2(-6.0f * TESR_ReciprocalResolution.x, -6.0f * TESR_ReciprocalResolution.x),
float2(-5.0f * TESR_ReciprocalResolution.x, -5.0f * TESR_ReciprocalResolution.x),
float2(-4.0f * TESR_ReciprocalResolution.x, -4.0f * TESR_ReciprocalResolution.x),
float2(-3.0f * TESR_ReciprocalResolution.x, -3.0f * TESR_ReciprocalResolution.x),
float2(-2.0f * TESR_ReciprocalResolution.x, -2.0f * TESR_ReciprocalResolution.x),
float2(-1.0f * TESR_ReciprocalResolution.x, -1.0f * TESR_ReciprocalResolution.x),
float2( 1.0f * TESR_ReciprocalResolution.x, 1.0f * TESR_ReciprocalResolution.x),
float2( 2.0f * TESR_ReciprocalResolution.x, 2.0f * TESR_ReciprocalResolution.x),
float2( 3.0f * TESR_ReciprocalResolution.x, 3.0f * TESR_ReciprocalResolution.x),
float2( 4.0f * TESR_ReciprocalResolution.x, 4.0f * TESR_ReciprocalResolution.x),
float2( 5.0f * TESR_ReciprocalResolution.x, 5.0f * TESR_ReciprocalResolution.x),
float2( 6.0f * TESR_ReciprocalResolution.x, 6.0f * TESR_ReciprocalResolution.x)
};
return color;
}

float WeightSum = 0.114725602f;
float4 color = tex2D(SourceBuffer, uv) * WeightSum;
float4 Blur5(float2 uv) {
float weights[2] = { 0.2941176470f, 0.3529411764f };
float offsets[2] = { 0.0f, 1.3333333333f };

float4 color = tex2Dlod(SourceBuffer, float4(uv.xy, 0.0f, 0.0f)) * weights[0];

float2 offset;
for (int i = 1; i < 2; i++) {
offset = BlurDirection * offsets[i] * TESR_ReciprocalResolution.x;

color += tex2Dlod(SourceBuffer, float4(uv.xy + offset, 0.0f, 0.0f)) * weights[i];
color += tex2Dlod(SourceBuffer, float4(uv.xy - offset, 0.0f, 0.0f)) * weights[i];
}

return color;
}

for (int i = 0; i < cKernelSize; i++)
{
float2 uvOff = (BlurOffsets[i] * OffsetMask) * blurRadius;
color += tex2D(SourceBuffer, uv + uvOff) * BlurWeights[i];
WeightSum += BlurWeights[i];
}

color /= WeightSum;
return color;
float4 Blur9(float2 uv) {
float weights[3] = { 0.2270270270f, 0.3162162162f, 0.0702702703f };
float offsets[3] = { 0.0f, 1.3846153846f, 3.2307692308f };

float4 color = tex2Dlod(SourceBuffer, float4(uv.xy, 0.0f, 0.0f)) * weights[0];

float2 offset;
for (int i = 1; i < 3; i++) {
offset = BlurDirection * offsets[i] * TESR_ReciprocalResolution.x;

color += tex2Dlod(SourceBuffer, float4(uv.xy + offset, 0.0f, 0.0f)) * weights[i];
color += tex2Dlod(SourceBuffer, float4(uv.xy - offset, 0.0f, 0.0f)) * weights[i];
}

return color;
}

float4 main(VSOUT IN) : COLOR0
{
return Blur(BlurDirection.xy, IN.UVCoord);
// Gaussian blur using linear texture sampling.
//
// Weights calculated from the Pascal triangle, and recalculated as below.
//
// Sample in between texels.
// weight_l(t1, t2) = weight_d(t1) + weight_d(t2)
// offset_l(t1, t2) = (offset_d(t1) * weight_d(t1) + offset_d(t2) * weight_d(t2)) / weight_l(t1, t2)
//
// https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/
float4 main(VSOUT IN) : COLOR0 {
// Blur the different cascades non-uniformly. The closer the cascade, the bigger the blur.
float2 uv = IN.UVCoord.xy;

[branch] if (uv.x < 0.5 && uv.y < 0.5)
return Blur9(uv); // Closest cascade.
else if (uv.y < 0.5)
return Blur5(uv); // Second cascade.
else if (uv.x < 0.5)
return Blur3(uv); // Third cascade.
else
return Blur3(uv); // Furthest cascade.
}

0 comments on commit 790f8d1

Please sign in to comment.