Refactor shadows rendering to use Shadow Map Atlas
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
#include "./Flax/ExponentialHeightFog.hlsl"
|
||||
@2// Forward Shading: Constants
|
||||
LightData DirectionalLight;
|
||||
LightShadowData DirectionalLightShadow;
|
||||
LightData SkyLight;
|
||||
ProbeData EnvironmentProbe;
|
||||
ExponentialHeightFogData ExponentialHeightFog;
|
||||
@@ -26,9 +25,9 @@ LightData LocalLights[MAX_LOCAL_LIGHTS];
|
||||
@3// Forward Shading: Resources
|
||||
TextureCube EnvProbe : register(t__SRV__);
|
||||
TextureCube SkyLightTexture : register(t__SRV__);
|
||||
Texture2DArray DirectionalLightShadowMap : register(t__SRV__);
|
||||
Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
||||
Texture2D<float> ShadowMap : register(t__SRV__);
|
||||
@4// Forward Shading: Utilities
|
||||
DECLARE_LIGHTSHADOWDATA_ACCESS(DirectionalLightShadow);
|
||||
@5// Forward Shading: Shaders
|
||||
|
||||
// Pixel Shader function for Forward Pass
|
||||
@@ -80,11 +79,8 @@ void PS_Forward(
|
||||
|
||||
// Calculate lighting from a single directional light
|
||||
float4 shadowMask = 1.0f;
|
||||
if (DirectionalLight.CastShadows > 0)
|
||||
{
|
||||
LightShadowData directionalLightShadowData = GetDirectionalLightShadowData();
|
||||
shadowMask.r = SampleShadow(DirectionalLight, directionalLightShadowData, DirectionalLightShadowMap, gBuffer, shadowMask.g);
|
||||
}
|
||||
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
shadowMask = GetShadowMask(shadow);
|
||||
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
|
||||
|
||||
// Calculate lighting from sky light
|
||||
|
||||
@@ -23,3 +23,8 @@
|
||||
#define OUT_OF_MEMORY Platform::OutOfMemory(__LINE__, __FILE__)
|
||||
#define MISSING_CODE(info) Platform::MissingCode(__LINE__, __FILE__, info)
|
||||
#define NON_COPYABLE(type) type(type&&) = delete; type(const type&) = delete; type& operator=(const type&) = delete; type& operator=(type&&) = delete;
|
||||
#define POD_COPYABLE(type) \
|
||||
type(const type& other) { Platform::MemoryCopy(this, &other, sizeof(type)); } \
|
||||
type(type&& other) noexcept { Platform::MemoryCopy(this, &other, sizeof(type)); } \
|
||||
type& operator=(const type& other) { Platform::MemoryCopy(this, &other, sizeof(type)); return *this; } \
|
||||
type& operator=(type&& other) noexcept { Platform::MemoryCopy(this, &other, sizeof(type)); return *this; }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 162
|
||||
#define MATERIAL_GRAPH_VERSION 163
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -21,7 +21,8 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
ASSERT_LOW_LAYER(cb.Length() >= sizeof(Data));
|
||||
const int32 envProbeShaderRegisterIndex = srv + 0;
|
||||
const int32 skyLightShaderRegisterIndex = srv + 1;
|
||||
const int32 dirLightShaderRegisterIndex = srv + 2;
|
||||
const int32 shadowsBufferRegisterIndex = srv + 2;
|
||||
const int32 shadowMapShaderRegisterIndex = srv + 3;
|
||||
const bool canUseShadow = view.Pass != DrawPass::Depth;
|
||||
|
||||
// Set fog input
|
||||
@@ -39,24 +40,19 @@ void ForwardShadingFeature::Bind(MaterialShader::BindParameters& params, Span<by
|
||||
if (cache->DirectionalLights.HasItems())
|
||||
{
|
||||
const auto& dirLight = cache->DirectionalLights.First();
|
||||
const auto shadowPass = ShadowsPass::Instance();
|
||||
const bool useShadow = shadowPass->LastDirLightIndex == 0 && canUseShadow;
|
||||
if (useShadow)
|
||||
{
|
||||
data.DirectionalLightShadow = shadowPass->LastDirLight;
|
||||
params.GPUContext->BindSR(dirLightShaderRegisterIndex, shadowPass->LastDirLightShadowMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
params.GPUContext->UnBindSR(dirLightShaderRegisterIndex);
|
||||
}
|
||||
GPUTexture* shadowMapAtlas;
|
||||
GPUBufferView* shadowsBuffer;
|
||||
ShadowsPass::GetShadowAtlas(params.RenderContext.Buffers, shadowMapAtlas, shadowsBuffer);
|
||||
const bool useShadow = shadowMapAtlas && canUseShadow && dirLight.HasShadow;
|
||||
dirLight.SetShaderData(data.DirectionalLight, useShadow);
|
||||
params.GPUContext->BindSR(shadowsBufferRegisterIndex, shadowsBuffer);
|
||||
params.GPUContext->BindSR(shadowMapShaderRegisterIndex, shadowMapAtlas);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.DirectionalLight.Color = Float3::Zero;
|
||||
data.DirectionalLight.CastShadows = 0.0f;
|
||||
params.GPUContext->UnBindSR(dirLightShaderRegisterIndex);
|
||||
Platform::MemoryClear(&data.DirectionalLight, sizeof(data.DirectionalLight));
|
||||
params.GPUContext->UnBindSR(shadowsBufferRegisterIndex);
|
||||
params.GPUContext->UnBindSR(shadowMapShaderRegisterIndex);
|
||||
}
|
||||
|
||||
// Set sky light
|
||||
|
||||
@@ -23,12 +23,11 @@ struct ForwardShadingFeature : MaterialShaderFeature
|
||||
{
|
||||
enum { MaxLocalLights = 4 };
|
||||
|
||||
enum { SRVs = 3 };
|
||||
enum { SRVs = 4 };
|
||||
|
||||
PACK_STRUCT(struct Data
|
||||
{
|
||||
ShaderLightData DirectionalLight;
|
||||
ShaderLightShadowData DirectionalLightShadow;
|
||||
ShaderLightData SkyLight;
|
||||
ShaderEnvProbeData EnvironmentProbe;
|
||||
ShaderExponentialHeightFogData ExponentialHeightFog;
|
||||
|
||||
@@ -38,7 +38,6 @@ void DirectionalLight::Draw(RenderContext& renderContext)
|
||||
data.VolumetricScatteringIntensity = VolumetricScatteringIntensity;
|
||||
data.IndirectLightingIntensity = IndirectLightingIntensity;
|
||||
data.CastVolumetricShadow = CastVolumetricShadow;
|
||||
data.RenderedVolumetricFog = 0;
|
||||
data.ShadowsMode = ShadowsMode;
|
||||
data.CascadeCount = CascadeCount;
|
||||
data.Cascade1Spacing = Cascade1Spacing;
|
||||
@@ -49,6 +48,7 @@ void DirectionalLight::Draw(RenderContext& renderContext)
|
||||
data.ContactShadowsLength = ContactShadowsLength;
|
||||
data.StaticFlags = GetStaticFlags();
|
||||
data.ID = GetID();
|
||||
data.ScreenSize = 1.0f;
|
||||
renderContext.List->DirectionalLights.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "PointLight.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
@@ -102,7 +103,6 @@ void PointLight::Draw(RenderContext& renderContext)
|
||||
data.ShadowsSharpness = ShadowsSharpness;
|
||||
data.VolumetricScatteringIntensity = VolumetricScatteringIntensity;
|
||||
data.CastVolumetricShadow = CastVolumetricShadow;
|
||||
data.RenderedVolumetricFog = 0;
|
||||
data.ShadowsMode = ShadowsMode;
|
||||
data.Radius = radius;
|
||||
data.FallOffExponent = FallOffExponent;
|
||||
@@ -114,6 +114,7 @@ void PointLight::Draw(RenderContext& renderContext)
|
||||
data.IESTexture = IESTexture ? IESTexture->GetTexture() : nullptr;
|
||||
data.StaticFlags = GetStaticFlags();
|
||||
data.ID = GetID();
|
||||
data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View)));
|
||||
renderContext.List->PointLights.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
|
||||
SkyLight::SkyLight(const SpawnParams& params)
|
||||
@@ -118,13 +119,13 @@ void SkyLight::Draw(RenderContext& renderContext)
|
||||
data.Color = Color.ToFloat3() * (Color.A * brightness);
|
||||
data.VolumetricScatteringIntensity = VolumetricScatteringIntensity;
|
||||
data.CastVolumetricShadow = CastVolumetricShadow;
|
||||
data.RenderedVolumetricFog = 0;
|
||||
data.AdditiveColor = AdditiveColor.ToFloat3() * (AdditiveColor.A * brightness);
|
||||
data.IndirectLightingIntensity = IndirectLightingIntensity;
|
||||
data.Radius = GetScaledRadius();
|
||||
data.Image = GetSource();
|
||||
data.StaticFlags = GetStaticFlags();
|
||||
data.ID = GetID();
|
||||
data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View)));
|
||||
renderContext.List->SkyLights.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Content/Assets/IESProfile.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
#include "Engine/Level/Scene/SceneRendering.h"
|
||||
|
||||
@@ -152,7 +153,6 @@ void SpotLight::Draw(RenderContext& renderContext)
|
||||
data.ShadowsSharpness = ShadowsSharpness;
|
||||
data.VolumetricScatteringIntensity = VolumetricScatteringIntensity;
|
||||
data.CastVolumetricShadow = CastVolumetricShadow;
|
||||
data.RenderedVolumetricFog = 0;
|
||||
data.ShadowsMode = ShadowsMode;
|
||||
data.Radius = radius;
|
||||
data.FallOffExponent = FallOffExponent;
|
||||
@@ -167,6 +167,7 @@ void SpotLight::Draw(RenderContext& renderContext)
|
||||
data.OuterConeAngle = outerConeAngle;
|
||||
data.StaticFlags = GetStaticFlags();
|
||||
data.ID = GetID();
|
||||
data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View)));
|
||||
renderContext.List->SpotLights.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,12 +399,6 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff
|
||||
lightData.ShadowsSharpness = 1.0f;
|
||||
lightData.UseInverseSquaredFalloff = false;
|
||||
lightData.VolumetricScatteringIntensity = 1.0f;
|
||||
lightData.CastVolumetricShadow = false;
|
||||
lightData.RenderedVolumetricFog = 0;
|
||||
lightData.ShadowsMode = ShadowsCastingMode::None;
|
||||
lightData.SourceRadius = 0.0f;
|
||||
lightData.SourceLength = 0.0f;
|
||||
lightData.IESTexture = nullptr;
|
||||
|
||||
for (int32 particleIndex = 0; particleIndex < count; particleIndex++)
|
||||
{
|
||||
|
||||
@@ -77,28 +77,13 @@ PACK_STRUCT(struct ShaderLightData {
|
||||
Float3 Color;
|
||||
float MinRoughness;
|
||||
Float3 Position;
|
||||
float CastShadows;
|
||||
uint32 ShadowsBufferAddress;
|
||||
Float3 Direction;
|
||||
float Radius;
|
||||
float FalloffExponent;
|
||||
float InverseSquared;
|
||||
float Dummy0;
|
||||
float RadiusInv;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Structure that contains information about light for shaders.
|
||||
/// </summary>
|
||||
PACK_STRUCT(struct ShaderLightShadowData {
|
||||
Float2 ShadowMapSize;
|
||||
float Sharpness;
|
||||
float Fade;
|
||||
float NormalOffsetScale;
|
||||
float Bias;
|
||||
float FadeDistance;
|
||||
uint32 NumCascades;
|
||||
Float4 CascadeSplits;
|
||||
Matrix ShadowVP[6];
|
||||
float Dummy0;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -97,27 +97,7 @@ struct GlobalSurfaceAtlasObject
|
||||
Platform::MemoryClear(this, sizeof(GlobalSurfaceAtlasObject));
|
||||
}
|
||||
|
||||
GlobalSurfaceAtlasObject(const GlobalSurfaceAtlasObject& other)
|
||||
{
|
||||
Platform::MemoryCopy(this, &other, sizeof(GlobalSurfaceAtlasObject));
|
||||
}
|
||||
|
||||
GlobalSurfaceAtlasObject(GlobalSurfaceAtlasObject&& other) noexcept
|
||||
{
|
||||
Platform::MemoryCopy(this, &other, sizeof(GlobalSurfaceAtlasObject));
|
||||
}
|
||||
|
||||
GlobalSurfaceAtlasObject& operator=(const GlobalSurfaceAtlasObject& other)
|
||||
{
|
||||
Platform::MemoryCopy(this, &other, sizeof(GlobalSurfaceAtlasObject));
|
||||
return *this;
|
||||
}
|
||||
|
||||
GlobalSurfaceAtlasObject& operator=(GlobalSurfaceAtlasObject&& other) noexcept
|
||||
{
|
||||
Platform::MemoryCopy(this, &other, sizeof(GlobalSurfaceAtlasObject));
|
||||
return *this;
|
||||
}
|
||||
POD_COPYABLE(GlobalSurfaceAtlasObject);
|
||||
};
|
||||
|
||||
struct GlobalSurfaceAtlasLight
|
||||
@@ -130,9 +110,9 @@ class GlobalSurfaceAtlasCustomBuffer : public RenderBuffers::CustomBuffer, publi
|
||||
{
|
||||
public:
|
||||
int32 Resolution = 0;
|
||||
int32 AtlasPixelsUsed = 0;
|
||||
uint64 LastFrameAtlasInsertFail = 0;
|
||||
uint64 LastFrameAtlasDefragmentation = 0;
|
||||
int32 AtlasPixelsUsed = 0;
|
||||
GPUTexture* AtlasDepth = nullptr;
|
||||
GPUTexture* AtlasEmissive = nullptr;
|
||||
GPUTexture* AtlasGBuffer0 = nullptr;
|
||||
@@ -163,7 +143,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
FORCE_INLINE void ClearObjects()
|
||||
void ClearObjects()
|
||||
{
|
||||
CulledObjectsCounterIndex = -1;
|
||||
CulledObjectsUsageHistory.Clear();
|
||||
@@ -174,7 +154,7 @@ public:
|
||||
Lights.Clear();
|
||||
}
|
||||
|
||||
FORCE_INLINE void Clear()
|
||||
void Reset()
|
||||
{
|
||||
RenderTargetPool::Release(AtlasDepth);
|
||||
RenderTargetPool::Release(AtlasEmissive);
|
||||
@@ -189,7 +169,7 @@ public:
|
||||
{
|
||||
SAFE_DELETE_GPU_RESOURCE(ChunksBuffer);
|
||||
SAFE_DELETE_GPU_RESOURCE(CulledObjectsBuffer);
|
||||
Clear();
|
||||
Reset();
|
||||
}
|
||||
|
||||
// [ISceneRenderingListener]
|
||||
@@ -400,7 +380,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
bool noCache = surfaceAtlasData.Resolution != resolution;
|
||||
if (noCache)
|
||||
{
|
||||
surfaceAtlasData.Clear();
|
||||
surfaceAtlasData.Reset();
|
||||
|
||||
auto desc = GPUTextureDescription::New2D(resolution, resolution, PixelFormat::Unknown);
|
||||
uint64 memUsage = 0;
|
||||
@@ -963,9 +943,9 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
if (_vertexBuffer->Data.Count() == 0)
|
||||
continue;
|
||||
|
||||
// Draw draw light
|
||||
// Draw light
|
||||
PROFILE_GPU_CPU_NAMED("Directional Light");
|
||||
const bool useShadow = CanRenderShadow(renderContext.View, light);
|
||||
const bool useShadow = light.CanRenderShadow(renderContext.View);
|
||||
// TODO: test perf/quality when using Shadow Map for directional light (ShadowsPass::Instance()->LastDirLightShadowMap) instead of Global SDF trace
|
||||
light.SetShaderData(data.Light, useShadow);
|
||||
data.Light.Color *= light.IndirectLightingIntensity;
|
||||
@@ -997,9 +977,9 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
if (_vertexBuffer->Data.Count() == 0)
|
||||
continue;
|
||||
|
||||
// Draw draw light
|
||||
// Draw light
|
||||
PROFILE_GPU_CPU_NAMED("Point Light");
|
||||
const bool useShadow = CanRenderShadow(renderContext.View, light);
|
||||
const bool useShadow = light.CanRenderShadow(renderContext.View);
|
||||
light.SetShaderData(data.Light, useShadow);
|
||||
data.Light.Color *= light.IndirectLightingIntensity;
|
||||
data.LightShadowsStrength = 1.0f - light.ShadowsStrength;
|
||||
@@ -1030,9 +1010,9 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
if (_vertexBuffer->Data.Count() == 0)
|
||||
continue;
|
||||
|
||||
// Draw draw light
|
||||
// Draw light
|
||||
PROFILE_GPU_CPU_NAMED("Spot Light");
|
||||
const bool useShadow = CanRenderShadow(renderContext.View, light);
|
||||
const bool useShadow = light.CanRenderShadow(renderContext.View);
|
||||
light.SetShaderData(data.Light, useShadow);
|
||||
data.Light.Color *= light.IndirectLightingIntensity;
|
||||
data.LightShadowsStrength = 1.0f - light.ShadowsStrength;
|
||||
@@ -1048,7 +1028,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
surfaceAtlasData.Lights.Remove(it);
|
||||
}
|
||||
|
||||
// Draw draw indirect light from Global Illumination
|
||||
// Draw indirect light from Global Illumination
|
||||
if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::GI))
|
||||
{
|
||||
switch (giSettings.Mode)
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
#include "LightPass.h"
|
||||
#include "ShadowsPass.h"
|
||||
#include "GBufferPass.h"
|
||||
#include "Engine/Core/Collections/Sorting.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/RenderTargetPool.h"
|
||||
#include "Engine/Content/Assets/CubeTexture.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
|
||||
PACK_STRUCT(struct PerLight{
|
||||
ShaderLightData Light;
|
||||
@@ -49,8 +50,9 @@ bool LightPass::Init()
|
||||
_shader.Get()->OnReloading.Bind<LightPass, &LightPass::OnShaderReloading>(this);
|
||||
#endif
|
||||
|
||||
// Pick the format for shadow mask (rendered shadow projection into screen-space)
|
||||
auto format = PixelFormat::R8G8_UNorm;
|
||||
if (EnumHasNoneFlags(GPUDevice::Instance->GetFormatFeatures(format).Support, (FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D)))
|
||||
if (EnumHasNoneFlags(GPUDevice::Instance->GetFormatFeatures(format).Support, FormatSupport::RenderTarget | FormatSupport::ShaderSample | FormatSupport::Texture2D))
|
||||
{
|
||||
format = PixelFormat::B8G8R8A8_UNorm;
|
||||
}
|
||||
@@ -151,27 +153,48 @@ void LightPass::Dispose()
|
||||
_sphereModel = nullptr;
|
||||
}
|
||||
|
||||
void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer)
|
||||
template<typename T = RenderLightData>
|
||||
bool SortLights(T const& p1, T const& p2)
|
||||
{
|
||||
// Compare by screen size
|
||||
int32 res = static_cast<int32>(p2.ScreenSize * 100 - p1.ScreenSize * 100);
|
||||
if (res == 0)
|
||||
{
|
||||
// Compare by brightness
|
||||
res = static_cast<int32>(p2.Color.SumValues() * 100 - p1.Color.SumValues() * 100);
|
||||
if (res == 0)
|
||||
{
|
||||
// Compare by ID to stabilize order
|
||||
res = GetHash(p2.ID) - GetHash(p1.ID);
|
||||
}
|
||||
}
|
||||
return res < 0;
|
||||
}
|
||||
|
||||
void LightPass::SetupLights(RenderContext& renderContext, RenderContextBatch& renderContextBatch)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
// Sort lights
|
||||
Sorting::QuickSort(renderContext.List->DirectionalLights.Get(), renderContext.List->DirectionalLights.Count(), &SortLights);
|
||||
Sorting::QuickSort(renderContext.List->PointLights.Get(), renderContext.List->PointLights.Count(), &SortLights);
|
||||
Sorting::QuickSort(renderContext.List->SpotLights.Get(), renderContext.List->SpotLights.Count(), &SortLights);
|
||||
}
|
||||
|
||||
void LightPass::RenderLights(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer)
|
||||
{
|
||||
const float sphereModelScale = 3.0f;
|
||||
|
||||
// Ensure to have valid data
|
||||
if (checkIfSkipPass())
|
||||
{
|
||||
// Resources are missing. Do not perform rendering.
|
||||
return;
|
||||
}
|
||||
|
||||
PROFILE_GPU_CPU("Lights");
|
||||
|
||||
// Cache data
|
||||
auto device = GPUDevice::Instance;
|
||||
auto context = device->GetMainContext();
|
||||
auto& renderContext = renderContextBatch.Contexts[0];
|
||||
auto& renderContext = renderContextBatch.GetMainContext();
|
||||
auto& view = renderContext.View;
|
||||
auto mainCache = renderContext.List;
|
||||
const auto lightShader = _shader->GetShader();
|
||||
const bool useShadows = ShadowsPass::Instance()->IsReady() && EnumHasAnyFlags(view.Flags, ViewFlags::Shadows);
|
||||
const bool disableSpecular = (view.Flags & ViewFlags::SpecularLight) == ViewFlags::None;
|
||||
|
||||
// Check if debug lights
|
||||
@@ -242,12 +265,9 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->PointLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU_NAMED("Point Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->PointLights[lightIndex];
|
||||
float lightRadius = light.Radius;
|
||||
Float3 lightPosition = light.Position;
|
||||
const bool renderShadow = useShadows && light.ShadowDataIndex != -1;
|
||||
bool useIES = light.IESTexture != nullptr;
|
||||
|
||||
// Get distance from view center to light center less radius (check if view is inside a sphere)
|
||||
@@ -261,23 +281,19 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
Matrix::Multiply(wvp, matrix, world);
|
||||
Matrix::Multiply(world, view.ViewProjection(), wvp);
|
||||
|
||||
// Check if render shadow
|
||||
if (renderShadow)
|
||||
// Fullscreen shadow mask rendering
|
||||
if (light.HasShadow)
|
||||
{
|
||||
GET_SHADOW_MASK();
|
||||
ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, shadowMaskView);
|
||||
|
||||
// Bind output
|
||||
ShadowsPass::Instance()->RenderShadowMask(renderContextBatch, light, shadowMaskView);
|
||||
context->SetRenderTarget(depthBufferRTV, lightBuffer);
|
||||
|
||||
// Set shadow mask
|
||||
context->BindSR(5, shadowMaskView);
|
||||
}
|
||||
else
|
||||
context->UnBindSR(5);
|
||||
|
||||
// Pack light properties buffer
|
||||
light.SetShaderData(perLight.Light, renderShadow);
|
||||
light.SetShaderData(perLight.Light, light.HasShadow);
|
||||
Matrix::Transpose(wvp, perLight.WVP);
|
||||
if (useIES)
|
||||
{
|
||||
@@ -299,12 +315,9 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->SpotLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU_NAMED("Spot Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->SpotLights[lightIndex];
|
||||
float lightRadius = light.Radius;
|
||||
Float3 lightPosition = light.Position;
|
||||
const bool renderShadow = useShadows && light.ShadowDataIndex != -1;
|
||||
bool useIES = light.IESTexture != nullptr;
|
||||
|
||||
// Get distance from view center to light center less radius (check if view is inside a sphere)
|
||||
@@ -318,23 +331,19 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
Matrix::Multiply(wvp, matrix, world);
|
||||
Matrix::Multiply(world, view.ViewProjection(), wvp);
|
||||
|
||||
// Check if render shadow
|
||||
if (renderShadow)
|
||||
// Fullscreen shadow mask rendering
|
||||
if (light.HasShadow)
|
||||
{
|
||||
GET_SHADOW_MASK();
|
||||
ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, shadowMaskView);
|
||||
|
||||
// Bind output
|
||||
ShadowsPass::Instance()->RenderShadowMask(renderContextBatch, light, shadowMaskView);
|
||||
context->SetRenderTarget(depthBufferRTV, lightBuffer);
|
||||
|
||||
// Set shadow mask
|
||||
context->BindSR(5, shadowMaskView);
|
||||
}
|
||||
else
|
||||
context->UnBindSR(5);
|
||||
|
||||
// Pack light properties buffer
|
||||
light.SetShaderData(perLight.Light, renderShadow);
|
||||
light.SetShaderData(perLight.Light, light.HasShadow);
|
||||
Matrix::Transpose(wvp, perLight.WVP);
|
||||
if (useIES)
|
||||
{
|
||||
@@ -356,28 +365,21 @@ void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureVi
|
||||
for (int32 lightIndex = 0; lightIndex < mainCache->DirectionalLights.Count(); lightIndex++)
|
||||
{
|
||||
PROFILE_GPU_CPU_NAMED("Directional Light");
|
||||
|
||||
// Cache data
|
||||
auto& light = mainCache->DirectionalLights[lightIndex];
|
||||
const bool renderShadow = useShadows && light.ShadowDataIndex != -1;
|
||||
|
||||
// Check if render shadow
|
||||
if (renderShadow)
|
||||
// Fullscreen shadow mask rendering
|
||||
if (light.HasShadow)
|
||||
{
|
||||
GET_SHADOW_MASK();
|
||||
ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, lightIndex, shadowMaskView);
|
||||
|
||||
// Bind output
|
||||
ShadowsPass::Instance()->RenderShadowMask(renderContextBatch, light, shadowMaskView);
|
||||
context->SetRenderTarget(depthBufferRTV, lightBuffer);
|
||||
|
||||
// Set shadow mask
|
||||
context->BindSR(5, shadowMaskView);
|
||||
}
|
||||
else
|
||||
context->UnBindSR(5);
|
||||
|
||||
// Pack light properties buffer
|
||||
light.SetShaderData(perLight.Light, renderShadow);
|
||||
light.SetShaderData(perLight.Light, light.HasShadow);
|
||||
|
||||
// Calculate lighting
|
||||
context->UpdateCB(cb0, &perLight);
|
||||
|
||||
@@ -27,15 +27,19 @@ private:
|
||||
PixelFormat _shadowMaskFormat;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Setups the lights rendering for batched scene drawing.
|
||||
/// </summary>
|
||||
void SetupLights(RenderContext& renderContext, RenderContextBatch& renderContextBatch);
|
||||
|
||||
/// <summary>
|
||||
/// Performs the lighting rendering for the input task.
|
||||
/// </summary>
|
||||
/// <param name="renderContextBatch">The rendering context batch.</param>
|
||||
/// <param name="lightBuffer">The light accumulation buffer (input and output).</param>
|
||||
void RenderLight(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer);
|
||||
void RenderLights(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer);
|
||||
|
||||
private:
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
{
|
||||
@@ -51,14 +55,12 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// [RendererPass]
|
||||
String ToString() const override;
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
|
||||
protected:
|
||||
|
||||
// [RendererPass]
|
||||
bool setupResources() override;
|
||||
};
|
||||
|
||||
@@ -39,6 +39,24 @@ namespace
|
||||
CriticalSection MemPoolLocker;
|
||||
}
|
||||
|
||||
bool RenderLightData::CanRenderShadow(const RenderView& view) const
|
||||
{
|
||||
bool result = false;
|
||||
switch (ShadowsMode)
|
||||
{
|
||||
case ShadowsCastingMode::StaticOnly:
|
||||
result = view.IsOfflinePass;
|
||||
break;
|
||||
case ShadowsCastingMode::DynamicOnly:
|
||||
result = !view.IsOfflinePass;
|
||||
break;
|
||||
case ShadowsCastingMode::All:
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
return result && ShadowsStrength > ZeroTolerance;
|
||||
}
|
||||
|
||||
void RenderDirectionalLightData::SetShaderData(ShaderLightData& data, bool useShadow) const
|
||||
{
|
||||
data.SpotAngles.X = -2.0f;
|
||||
@@ -48,7 +66,7 @@ void RenderDirectionalLightData::SetShaderData(ShaderLightData& data, bool useSh
|
||||
data.Color = Color;
|
||||
data.MinRoughness = Math::Max(MinRoughness, MIN_ROUGHNESS);
|
||||
data.Position = Float3::Zero;
|
||||
data.CastShadows = useShadow ? 1.0f : 0.0f;
|
||||
data.ShadowsBufferAddress = useShadow ? ShadowsBufferAddress : 0;
|
||||
data.Direction = -Direction;
|
||||
data.Radius = 0;
|
||||
data.FalloffExponent = 0;
|
||||
@@ -56,6 +74,15 @@ void RenderDirectionalLightData::SetShaderData(ShaderLightData& data, bool useSh
|
||||
data.RadiusInv = 0;
|
||||
}
|
||||
|
||||
bool RenderLocalLightData::CanRenderShadow(const RenderView& view) const
|
||||
{
|
||||
// Fade shadow on distance
|
||||
const float fadeDistance = Math::Max(ShadowsFadeDistance, 0.1f);
|
||||
const float dstLightToView = Float3::Distance(Position, view.Position);
|
||||
const float fade = 1 - Math::Saturate((dstLightToView - Radius - ShadowsDistance + fadeDistance) / fadeDistance);
|
||||
return fade > ZeroTolerance && RenderLightData::CanRenderShadow(view);
|
||||
}
|
||||
|
||||
void RenderSpotLightData::SetShaderData(ShaderLightData& data, bool useShadow) const
|
||||
{
|
||||
data.SpotAngles.X = CosOuterCone;
|
||||
@@ -65,7 +92,7 @@ void RenderSpotLightData::SetShaderData(ShaderLightData& data, bool useShadow) c
|
||||
data.Color = Color;
|
||||
data.MinRoughness = Math::Max(MinRoughness, MIN_ROUGHNESS);
|
||||
data.Position = Position;
|
||||
data.CastShadows = useShadow ? 1.0f : 0.0f;
|
||||
data.ShadowsBufferAddress = useShadow ? ShadowsBufferAddress : 0;
|
||||
data.Direction = Direction;
|
||||
data.Radius = Radius;
|
||||
data.FalloffExponent = FallOffExponent;
|
||||
@@ -82,7 +109,7 @@ void RenderPointLightData::SetShaderData(ShaderLightData& data, bool useShadow)
|
||||
data.Color = Color;
|
||||
data.MinRoughness = Math::Max(MinRoughness, MIN_ROUGHNESS);
|
||||
data.Position = Position;
|
||||
data.CastShadows = useShadow ? 1.0f : 0.0f;
|
||||
data.ShadowsBufferAddress = useShadow ? ShadowsBufferAddress : 0;
|
||||
data.Direction = Direction;
|
||||
data.Radius = Radius;
|
||||
data.FalloffExponent = FallOffExponent;
|
||||
@@ -99,7 +126,7 @@ void RenderSkyLightData::SetShaderData(ShaderLightData& data, bool useShadow) co
|
||||
data.Color = Color;
|
||||
data.MinRoughness = MIN_ROUGHNESS;
|
||||
data.Position = Position;
|
||||
data.CastShadows = useShadow ? 1.0f : 0.0f;
|
||||
data.ShadowsBufferAddress = useShadow ? ShadowsBufferAddress : 0;
|
||||
data.Direction = Float3::Forward;
|
||||
data.Radius = Radius;
|
||||
data.FalloffExponent = 0;
|
||||
|
||||
@@ -43,55 +43,82 @@ struct RenderLightData
|
||||
StaticFlags StaticFlags;
|
||||
ShadowsCastingMode ShadowsMode;
|
||||
float IndirectLightingIntensity;
|
||||
int16 ShadowDataIndex = -1;
|
||||
uint8 HasShadow : 1;
|
||||
uint8 CastVolumetricShadow : 1;
|
||||
uint8 RenderedVolumetricFog : 1;
|
||||
uint8 UseInverseSquaredFalloff : 1;
|
||||
uint8 IsDirectionalLight : 1;
|
||||
uint8 IsPointLight : 1;
|
||||
uint8 IsSpotLight : 1;
|
||||
uint8 IsSkyLight : 1;
|
||||
|
||||
float VolumetricScatteringIntensity;
|
||||
float ContactShadowsLength;
|
||||
float ScreenSize;
|
||||
uint32 ShadowsBufferAddress;
|
||||
|
||||
RenderLightData()
|
||||
{
|
||||
Platform::MemoryClear(this, sizeof(RenderLightData));
|
||||
}
|
||||
|
||||
POD_COPYABLE(RenderLightData);
|
||||
bool CanRenderShadow(const RenderView& view) const;
|
||||
};
|
||||
|
||||
struct RenderDirectionalLightData : RenderLightData
|
||||
{
|
||||
PartitionMode PartitionMode;
|
||||
int32 CascadeCount;
|
||||
|
||||
float Cascade1Spacing;
|
||||
float Cascade2Spacing;
|
||||
float Cascade3Spacing;
|
||||
float Cascade4Spacing;
|
||||
|
||||
PartitionMode PartitionMode;
|
||||
int32 CascadeCount;
|
||||
|
||||
RenderDirectionalLightData()
|
||||
{
|
||||
IsDirectionalLight = 1;
|
||||
}
|
||||
|
||||
void SetShaderData(ShaderLightData& data, bool useShadow) const;
|
||||
};
|
||||
|
||||
struct RenderSpotLightData : RenderLightData
|
||||
struct RenderLocalLightData : RenderLightData
|
||||
{
|
||||
GPUTexture* IESTexture;
|
||||
|
||||
float Radius;
|
||||
float SourceRadius;
|
||||
|
||||
bool CanRenderShadow(const RenderView& view) const;
|
||||
};
|
||||
|
||||
struct RenderSpotLightData : RenderLocalLightData
|
||||
{
|
||||
Float3 UpVector;
|
||||
float OuterConeAngle;
|
||||
|
||||
float CosOuterCone;
|
||||
float InvCosConeDifference;
|
||||
float FallOffExponent;
|
||||
uint8 UseInverseSquaredFalloff : 1;
|
||||
|
||||
GPUTexture* IESTexture;
|
||||
RenderSpotLightData()
|
||||
{
|
||||
IsSpotLight = 1;
|
||||
}
|
||||
|
||||
void SetShaderData(ShaderLightData& data, bool useShadow) const;
|
||||
};
|
||||
|
||||
struct RenderPointLightData : RenderLightData
|
||||
struct RenderPointLightData : RenderLocalLightData
|
||||
{
|
||||
float Radius;
|
||||
float SourceRadius;
|
||||
|
||||
float FallOffExponent;
|
||||
float SourceLength;
|
||||
uint8 UseInverseSquaredFalloff : 1;
|
||||
|
||||
GPUTexture* IESTexture;
|
||||
RenderPointLightData()
|
||||
{
|
||||
IsPointLight = 1;
|
||||
}
|
||||
|
||||
void SetShaderData(ShaderLightData& data, bool useShadow) const;
|
||||
};
|
||||
@@ -103,6 +130,11 @@ struct RenderSkyLightData : RenderLightData
|
||||
|
||||
CubeTexture* Image;
|
||||
|
||||
RenderSkyLightData()
|
||||
{
|
||||
IsSkyLight = 1;
|
||||
}
|
||||
|
||||
void SetShaderData(ShaderLightData& data, bool useShadow) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -348,7 +348,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
// Prepare
|
||||
renderContext.View.Prepare(renderContext);
|
||||
renderContext.Buffers->Prepare();
|
||||
ShadowsPass::Instance()->Prepare();
|
||||
|
||||
// Build batch of render contexts (main view and shadow projections)
|
||||
{
|
||||
@@ -371,6 +370,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
drawShadows = false;
|
||||
break;
|
||||
}
|
||||
LightPass::Instance()->SetupLights(renderContext, renderContextBatch);
|
||||
if (drawShadows)
|
||||
ShadowsPass::Instance()->SetupShadows(renderContext, renderContextBatch);
|
||||
#if USE_EDITOR
|
||||
@@ -404,7 +404,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::MotionVectors);
|
||||
for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++)
|
||||
{
|
||||
auto& shadowContext = renderContextBatch.Contexts[i];
|
||||
auto& shadowContext = renderContextBatch.Contexts.Get()[i];
|
||||
shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth, DrawPass::Depth);
|
||||
shadowContext.List->SortDrawCalls(shadowContext, false, shadowContext.List->ShadowDepthDrawCallsList, renderContext.List->DrawCalls, DrawPass::Depth);
|
||||
}
|
||||
@@ -487,7 +487,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont
|
||||
|
||||
// Render lighting
|
||||
renderContextBatch.GetMainContext() = renderContext; // Sync render context in batch with the current value
|
||||
LightPass::Instance()->RenderLight(renderContextBatch, *lightBuffer);
|
||||
ShadowsPass::Instance()->RenderShadowMaps(renderContextBatch);
|
||||
LightPass::Instance()->RenderLights(renderContextBatch, *lightBuffer);
|
||||
if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::GI))
|
||||
{
|
||||
switch (renderContext.List->Settings.GlobalIllumination.Mode)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,121 +9,31 @@
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
|
||||
/// <summary>
|
||||
/// Pixel format for fullscreen render target used for shadows calculations
|
||||
/// </summary>
|
||||
#define SHADOWS_PASS_SS_RR_FORMAT PixelFormat::R11G11B10_Float
|
||||
|
||||
template<typename T>
|
||||
bool CanRenderShadow(const RenderView& view, const T& light)
|
||||
{
|
||||
bool result = false;
|
||||
switch ((ShadowsCastingMode)light.ShadowsMode)
|
||||
{
|
||||
case ShadowsCastingMode::StaticOnly:
|
||||
result = view.IsOfflinePass;
|
||||
break;
|
||||
case ShadowsCastingMode::DynamicOnly:
|
||||
result = !view.IsOfflinePass;
|
||||
break;
|
||||
case ShadowsCastingMode::All:
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result && light.ShadowsStrength > ZeroTolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shadows rendering service.
|
||||
/// </summary>
|
||||
class ShadowsPass : public RendererPass<ShadowsPass>
|
||||
{
|
||||
private:
|
||||
|
||||
struct ShadowData
|
||||
{
|
||||
int32 ContextIndex;
|
||||
int32 ContextCount;
|
||||
bool BlendCSM;
|
||||
ShaderLightShadowData Constants;
|
||||
};
|
||||
|
||||
// Shader stuff
|
||||
AssetReference<Shader> _shader;
|
||||
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2 * 2> _psShadowDir;
|
||||
AssetReference<Model> _sphereModel;
|
||||
GPUPipelineState* _psDepthClear = nullptr;
|
||||
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowDir;
|
||||
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowPoint;
|
||||
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2> _psShadowSpot;
|
||||
PixelFormat _shadowMapFormat;
|
||||
|
||||
// Shadow maps stuff
|
||||
int32 _shadowMapsSizeCSM;
|
||||
int32 _shadowMapsSizeCube;
|
||||
GPUTexture* _shadowMapCSM;
|
||||
GPUTexture* _shadowMapCube;
|
||||
Quality _currentShadowMapsQuality;
|
||||
|
||||
// Shadow map rendering stuff
|
||||
AssetReference<Model> _sphereModel;
|
||||
Array<ShadowData> _shadowData;
|
||||
|
||||
// Cached state for the current frame rendering (setup via Prepare)
|
||||
int32 maxShadowsQuality;
|
||||
PixelFormat _shadowMapFormat; // Cached on initialization
|
||||
int32 maxShadowsQuality = 0; // Cached state for the current frame rendering (setup via Prepare)
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
ShadowsPass();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets current GPU memory usage by the shadow maps
|
||||
/// </summary>
|
||||
/// <returns>GPU memory used in bytes</returns>
|
||||
uint64 GetShadowMapsMemoryUsage() const;
|
||||
|
||||
public:
|
||||
|
||||
// TODO: use full scene shadow map atlas with dynamic slots allocation
|
||||
int32 LastDirLightIndex = -1;
|
||||
GPUTextureView* LastDirLightShadowMap = nullptr;
|
||||
ShaderLightShadowData LastDirLight;
|
||||
|
||||
public:
|
||||
void Prepare();
|
||||
|
||||
/// <summary>
|
||||
/// Setups the shadows rendering for batched scene drawing. Checks which lights will cast a shadow.
|
||||
/// </summary>
|
||||
void SetupShadows(RenderContext& renderContext, RenderContextBatch& renderContextBatch);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether can render shadow for the specified light.
|
||||
/// Renders the shadow maps for all lights (into atlas).
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <returns><c>true</c> if can render shadow for the specified light; otherwise, <c>false</c>.</returns>
|
||||
bool CanRenderShadow(const RenderContext& renderContext, const RenderPointLightData& light);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether can render shadow for the specified light.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <returns><c>true</c> if can render shadow for the specified light; otherwise, <c>false</c>.</returns>
|
||||
bool CanRenderShadow(const RenderContext& renderContext, const RenderSpotLightData& light);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether can render shadow for the specified light.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <returns><c>true</c> if can render shadow for the specified light; otherwise, <c>false</c>.</returns>
|
||||
bool CanRenderShadow(const RenderContext& renderContext, const RenderDirectionalLightData& light);
|
||||
void RenderShadowMaps(RenderContextBatch& renderContextBatch);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the shadow mask for the given light.
|
||||
@@ -131,32 +41,23 @@ public:
|
||||
/// <param name="renderContextBatch">The rendering context batch.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <param name="shadowMask">The shadow mask (output).</param>
|
||||
void RenderShadow(RenderContextBatch& renderContextBatch, RenderPointLightData& light, GPUTextureView* shadowMask);
|
||||
void RenderShadowMask(RenderContextBatch& renderContextBatch, RenderLightData& light, GPUTextureView* shadowMask);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the shadow mask for the given light.
|
||||
/// Gets the shadow atlas texture and shadows buffer for shadow projection in shaders.
|
||||
/// </summary>
|
||||
/// <param name="renderContextBatch">The rendering context batch.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <param name="shadowMask">The shadow mask (output).</param>
|
||||
void RenderShadow(RenderContextBatch& renderContextBatch, RenderSpotLightData& light, GPUTextureView* shadowMask);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the shadow mask for the given light.
|
||||
/// </summary>
|
||||
/// <param name="renderContextBatch">The rendering context batch.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <param name="index">The light index.</param>
|
||||
/// <param name="shadowMask">The shadow mask (output).</param>
|
||||
void RenderShadow(RenderContextBatch& renderContextBatch, RenderDirectionalLightData& light, int32 index, GPUTextureView* shadowMask);
|
||||
/// <param name="renderBuffers">The render buffers that store frame context.</param>
|
||||
/// <param name="shadowMapAtlas">The output shadow map atlas texture or null if unused.</param>
|
||||
/// <param name="shadowsBuffer">The output shadows buffer or null if unused.</param>
|
||||
static void GetShadowAtlas(const RenderBuffers* renderBuffers, GPUTexture*& shadowMapAtlas, GPUBufferView*& shadowsBuffer);
|
||||
|
||||
private:
|
||||
|
||||
void updateShadowMapSize();
|
||||
void SetupRenderContext(RenderContext& renderContext, RenderContext& shadowContext);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderDirectionalLightData& light);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderPointLightData& light);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderSpotLightData& light);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderLightData& light, struct ShadowAtlasLight& atlasLight);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderLocalLightData& light, ShadowAtlasLight& atlasLight);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderDirectionalLightData& light, ShadowAtlasLight& atlasLight);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderPointLightData& light, ShadowAtlasLight& atlasLight);
|
||||
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderSpotLightData& light, ShadowAtlasLight& atlasLight);
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
@@ -169,14 +70,12 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// [RendererPass]
|
||||
String ToString() const override;
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
|
||||
protected:
|
||||
|
||||
// [RendererPass]
|
||||
bool setupResources() override;
|
||||
};
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
#include "Engine/Graphics/Graphics.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderBuffers.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/RenderTargetPool.h"
|
||||
#include "Engine/Graphics/GPULimits.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Content/Assets/CubeTexture.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
|
||||
// Must match shader source
|
||||
int32 VolumetricFogGridInjectionGroupSize = 4;
|
||||
@@ -143,38 +143,30 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
|
||||
switch (quality)
|
||||
{
|
||||
case Quality::Low:
|
||||
{
|
||||
_cache.GridPixelSize = 16;
|
||||
_cache.GridSizeZ = 64;
|
||||
_cache.FogJitter = false;
|
||||
_cache.MissedHistorySamplesCount = 1;
|
||||
break;
|
||||
}
|
||||
case Quality::Medium:
|
||||
{
|
||||
_cache.GridPixelSize = 16;
|
||||
_cache.GridSizeZ = 64;
|
||||
_cache.FogJitter = true;
|
||||
_cache.MissedHistorySamplesCount = 4;
|
||||
break;
|
||||
}
|
||||
case Quality::High:
|
||||
{
|
||||
_cache.GridPixelSize = 16;
|
||||
_cache.GridSizeZ = 128;
|
||||
_cache.FogJitter = true;
|
||||
_cache.MissedHistorySamplesCount = 4;
|
||||
break;
|
||||
}
|
||||
case Quality::Ultra:
|
||||
{
|
||||
_cache.GridPixelSize = 8;
|
||||
_cache.GridSizeZ = 256;
|
||||
_cache.FogJitter = true;
|
||||
_cache.MissedHistorySamplesCount = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare
|
||||
const int32 width = renderContext.Buffers->GetWidth();
|
||||
@@ -202,7 +194,6 @@ bool VolumetricFogPass::Init(RenderContext& renderContext, GPUContext* context,
|
||||
_cache.Data.VolumetricFogMaxDistance = options.Distance;
|
||||
_cache.Data.MissedHistorySamplesCount = Math::Clamp(_cache.MissedHistorySamplesCount, 1, (int32)ARRAY_COUNT(_cache.Data.FrameJitterOffsets));
|
||||
Matrix::Transpose(view.PrevViewProjection, _cache.Data.PrevWorldToClip);
|
||||
_cache.Data.DirectionalLightShadow.NumCascades = 0;
|
||||
_cache.Data.SkyLight.VolumetricScatteringIntensity = 0;
|
||||
|
||||
// Fill frame jitter history
|
||||
@@ -262,83 +253,6 @@ GPUTextureView* VolumetricFogPass::GetLocalShadowedLightScattering(RenderContext
|
||||
return renderContext.Buffers->LocalShadowedLightScattering->ViewVolume();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUContext* context, T& light, ShaderLightShadowData& shadow)
|
||||
{
|
||||
// Prepare
|
||||
VolumetricFogOptions options;
|
||||
if (Init(renderContext, context, options))
|
||||
return;
|
||||
auto& view = renderContext.View;
|
||||
|
||||
// Calculate light volume bounds in camera frustum depth range (min and max)
|
||||
const Float3 center = light.Position;
|
||||
const float radius = light.Radius;
|
||||
Float3 viewSpaceLightBoundsOrigin = Float3::Transform(center, view.View);
|
||||
float furthestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z + radius, options, _cache.GridSizeZ);
|
||||
float closestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z - radius, options, _cache.GridSizeZ);
|
||||
int32 volumeZBoundsMin = (int32)Math::Clamp(closestSliceIndexUnclamped, 0.0f, _cache.GridSize.Z - 1.0f);
|
||||
int32 volumeZBoundsMax = (int32)Math::Clamp(furthestSliceIndexUnclamped, 0.0f, _cache.GridSize.Z - 1.0f);
|
||||
|
||||
// Cull light
|
||||
if ((view.Position - center).LengthSquared() >= (options.Distance + radius) * (options.Distance + radius) || volumeZBoundsMin >= volumeZBoundsMax)
|
||||
return;
|
||||
|
||||
PROFILE_GPU_CPU("Volumetric Fog Light");
|
||||
|
||||
// Allocate temporary buffer for light scattering injection
|
||||
auto localShadowedLightScattering = GetLocalShadowedLightScattering(renderContext, context, options);
|
||||
|
||||
// Prepare
|
||||
PerLight perLight;
|
||||
auto cb0 = _shader->GetShader()->GetCB(0);
|
||||
auto cb1 = _shader->GetShader()->GetCB(1);
|
||||
|
||||
// Bind the output
|
||||
context->SetRenderTarget(localShadowedLightScattering);
|
||||
context->SetViewportAndScissors(_cache.Data.GridSize.X, _cache.Data.GridSize.Y);
|
||||
|
||||
// Setup data
|
||||
perLight.SliceToDepth.X = _cache.Data.GridSize.Z;
|
||||
perLight.SliceToDepth.Y = _cache.Data.VolumetricFogMaxDistance;
|
||||
perLight.MinZ = volumeZBoundsMin;
|
||||
perLight.LocalLightScatteringIntensity = light.VolumetricScatteringIntensity;
|
||||
perLight.ViewSpaceBoundingSphere = Float4(viewSpaceLightBoundsOrigin, radius);
|
||||
Matrix::Transpose(view.Projection, perLight.ViewToVolumeClip);
|
||||
light.SetShaderData(perLight.LocalLight, true);
|
||||
perLight.LocalLightShadow = shadow;
|
||||
|
||||
// Upload data
|
||||
context->UpdateCB(cb1, &perLight);
|
||||
context->BindCB(0, cb0);
|
||||
context->BindCB(1, cb1);
|
||||
|
||||
// Ensure to have valid buffers created
|
||||
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
||||
InitCircleBuffer();
|
||||
|
||||
// Call rendering to the volume
|
||||
const int32 psIndex = 1;
|
||||
context->SetState(_psInjectLight.Get(psIndex));
|
||||
const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin;
|
||||
const int32 indexCount = _ibCircleRasterize->GetElementsCount();
|
||||
ASSERT(instanceCount > 0);
|
||||
context->BindVB(ToSpan(&_vbCircleRasterize, 1));
|
||||
context->BindIB(_ibCircleRasterize);
|
||||
context->DrawIndexedInstanced(indexCount, instanceCount, 0);
|
||||
|
||||
// Cleanup
|
||||
context->UnBindCB(0);
|
||||
context->UnBindCB(1);
|
||||
auto viewport = renderContext.Task->GetViewport();
|
||||
context->SetViewportAndScissors(viewport);
|
||||
context->ResetRenderTarget();
|
||||
context->FlushState();
|
||||
|
||||
// Mark as rendered
|
||||
light.RenderedVolumetricFog = 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb1)
|
||||
{
|
||||
@@ -353,106 +267,67 @@ void VolumetricFogPass::RenderRadialLight(RenderContext& renderContext, GPUConte
|
||||
const float closestSliceIndexUnclamped = ComputeZSliceFromDepth(viewSpaceLightBoundsOrigin.Z - radius, options, cache.GridSizeZ);
|
||||
const int32 volumeZBoundsMin = (int32)Math::Clamp(closestSliceIndexUnclamped, 0.0f, cache.GridSize.Z - 1.0f);
|
||||
const int32 volumeZBoundsMax = (int32)Math::Clamp(furthestSliceIndexUnclamped, 0.0f, cache.GridSize.Z - 1.0f);
|
||||
|
||||
if (volumeZBoundsMin < volumeZBoundsMax)
|
||||
{
|
||||
// TODO: use full scene shadows atlas and render point/spot lights with shadow into a fog volume
|
||||
bool withShadow = false;
|
||||
|
||||
// Setup data
|
||||
perLight.SliceToDepth.X = cache.Data.GridSize.Z;
|
||||
perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance;
|
||||
perLight.MinZ = volumeZBoundsMin;
|
||||
perLight.LocalLightScatteringIntensity = light.VolumetricScatteringIntensity;
|
||||
perLight.ViewSpaceBoundingSphere = Float4(viewSpaceLightBoundsOrigin, radius);
|
||||
Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip);
|
||||
light.SetShaderData(perLight.LocalLight, withShadow);
|
||||
|
||||
// Upload data
|
||||
context->UpdateCB(cb1, &perLight);
|
||||
context->BindCB(1, cb1);
|
||||
|
||||
// Ensure to have valid buffers created
|
||||
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
||||
InitCircleBuffer();
|
||||
|
||||
// Call rendering to the volume
|
||||
const int32 psIndex = withShadow ? 1 : 0;
|
||||
context->SetState(_psInjectLight.Get(psIndex));
|
||||
const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin;
|
||||
const int32 indexCount = _ibCircleRasterize->GetElementsCount();
|
||||
context->BindVB(ToSpan(&_vbCircleRasterize, 1));
|
||||
context->BindIB(_ibCircleRasterize);
|
||||
context->DrawIndexedInstanced(indexCount, instanceCount, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void VolumetricFogPass::RenderLight(RenderContext& renderContext, GPUContext* context, RenderPointLightData& light, GPUTextureView* shadowMap, ShaderLightShadowData& shadow)
|
||||
{
|
||||
// Skip lights with no volumetric light influence or not casting volumetric shadow
|
||||
if (light.VolumetricScatteringIntensity <= ZeroTolerance || !light.CastVolumetricShadow)
|
||||
if (volumeZBoundsMin >= volumeZBoundsMax)
|
||||
return;
|
||||
ASSERT(shadowMap);
|
||||
|
||||
context->BindSR(5, shadowMap);
|
||||
// Setup data
|
||||
perLight.SliceToDepth.X = cache.Data.GridSize.Z;
|
||||
perLight.SliceToDepth.Y = cache.Data.VolumetricFogMaxDistance;
|
||||
perLight.MinZ = volumeZBoundsMin;
|
||||
perLight.LocalLightScatteringIntensity = light.VolumetricScatteringIntensity;
|
||||
perLight.ViewSpaceBoundingSphere = Float4(viewSpaceLightBoundsOrigin, radius);
|
||||
Matrix::Transpose(renderContext.View.Projection, perLight.ViewToVolumeClip);
|
||||
const bool withShadow = light.CastVolumetricShadow && light.HasShadow;
|
||||
light.SetShaderData(perLight.LocalLight, withShadow);
|
||||
|
||||
RenderRadialLight(renderContext, context, light, shadow);
|
||||
// Upload data
|
||||
context->UpdateCB(cb1, &perLight);
|
||||
context->BindCB(1, cb1);
|
||||
|
||||
context->UnBindSR(5);
|
||||
}
|
||||
// Ensure to have valid buffers created
|
||||
if (_vbCircleRasterize == nullptr || _ibCircleRasterize == nullptr)
|
||||
InitCircleBuffer();
|
||||
|
||||
void VolumetricFogPass::RenderLight(RenderContext& renderContext, GPUContext* context, RenderSpotLightData& light, GPUTextureView* shadowMap, ShaderLightShadowData& shadow)
|
||||
{
|
||||
// Skip lights with no volumetric light influence or not casting volumetric shadow
|
||||
if (light.VolumetricScatteringIntensity <= ZeroTolerance || !light.CastVolumetricShadow)
|
||||
return;
|
||||
ASSERT(shadowMap);
|
||||
|
||||
context->BindSR(6, shadowMap);
|
||||
|
||||
RenderRadialLight(renderContext, context, light, shadow);
|
||||
|
||||
context->UnBindSR(6);
|
||||
// Call rendering to the volume
|
||||
const int32 psIndex = withShadow ? 1 : 0;
|
||||
context->SetState(_psInjectLight.Get(psIndex));
|
||||
const int32 instanceCount = volumeZBoundsMax - volumeZBoundsMin;
|
||||
const int32 indexCount = _ibCircleRasterize->GetElementsCount();
|
||||
context->BindVB(ToSpan(&_vbCircleRasterize, 1));
|
||||
context->BindIB(_ibCircleRasterize);
|
||||
context->DrawIndexedInstanced(indexCount, instanceCount, 0);
|
||||
}
|
||||
|
||||
void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
{
|
||||
// Prepare
|
||||
VolumetricFogOptions options;
|
||||
auto context = GPUDevice::Instance->GetMainContext();
|
||||
if (Init(renderContext, context, options))
|
||||
return;
|
||||
auto& view = renderContext.View;
|
||||
auto& cache = _cache;
|
||||
|
||||
PROFILE_GPU_CPU("Volumetric Fog");
|
||||
|
||||
// TODO: test exponential depth distribution (should give better quality near the camera)
|
||||
// TODO: use tiled light culling and render unshadowed lights in single pass
|
||||
|
||||
// Try to get shadows atlas
|
||||
GPUTexture* shadowMap;
|
||||
GPUBufferView* shadowsBuffer;
|
||||
ShadowsPass::GetShadowAtlas(renderContext.Buffers, shadowMap, shadowsBuffer);
|
||||
|
||||
// Init directional light data
|
||||
GPUTextureView* dirLightShadowMap = nullptr;
|
||||
Platform::MemoryClear(&_cache.Data.DirectionalLight, sizeof(_cache.Data.DirectionalLight));
|
||||
if (renderContext.List->DirectionalLights.HasItems())
|
||||
{
|
||||
const int32 dirLightIndex = (int32)renderContext.List->DirectionalLights.Count() - 1;
|
||||
const auto& dirLight = renderContext.List->DirectionalLights[dirLightIndex];
|
||||
const float brightness = dirLight.VolumetricScatteringIntensity;
|
||||
|
||||
if (brightness > ZeroTolerance)
|
||||
{
|
||||
const auto shadowPass = ShadowsPass::Instance();
|
||||
const bool useShadow = dirLight.CastVolumetricShadow && shadowPass->LastDirLightIndex == dirLightIndex;
|
||||
const bool useShadow = shadowMap && dirLight.CastVolumetricShadow && dirLight.HasShadow;
|
||||
dirLight.SetShaderData(_cache.Data.DirectionalLight, useShadow);
|
||||
_cache.Data.DirectionalLight.Color *= brightness;
|
||||
if (useShadow)
|
||||
{
|
||||
_cache.Data.DirectionalLightShadow = shadowPass->LastDirLight;
|
||||
dirLightShadowMap = shadowPass->LastDirLightShadowMap;
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache.Data.DirectionalLightShadow.NumCascades = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,6 +350,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
|
||||
// Init sky light data
|
||||
GPUTexture* skyLightImage = nullptr;
|
||||
Platform::MemoryClear(&_cache.Data.SkyLight, sizeof(_cache.Data.SkyLight));
|
||||
if (renderContext.List->SkyLights.HasItems() && !useDDGI)
|
||||
{
|
||||
const auto& skyLight = renderContext.List->SkyLights.Last();
|
||||
@@ -510,13 +386,10 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
// Initialize fog volume properties
|
||||
{
|
||||
PROFILE_GPU("Initialize");
|
||||
|
||||
context->ResetRenderTarget();
|
||||
context->BindUA(0, vBufferA->ViewVolume());
|
||||
context->BindUA(1, vBufferB->ViewVolume());
|
||||
|
||||
context->Dispatch(_csInitialize, groupCountX, groupCountY, groupCountZ);
|
||||
|
||||
context->ResetUA();
|
||||
}
|
||||
|
||||
@@ -557,7 +430,7 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
const int32 volumeZBoundsMax = (int32)Math::Clamp(furthestSliceIndexUnclamped, 0.0f, cache.GridSize.Z - 1.0f);
|
||||
|
||||
// Culling
|
||||
if ((view.Position - center).LengthSquared() >= (options.Distance + radius) * (options.Distance + radius) || volumeZBoundsMin >= volumeZBoundsMax)
|
||||
if ((view.Position - center).LengthSquared() >= Math::Square(options.Distance + radius) || volumeZBoundsMin >= volumeZBoundsMax)
|
||||
continue;
|
||||
|
||||
// Setup material shader data
|
||||
@@ -598,25 +471,17 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
Array<const RenderSpotLightData*, InlinedAllocation<64, RendererAllocation>> spotLights;
|
||||
for (int32 i = 0; i < renderContext.List->PointLights.Count(); i++)
|
||||
{
|
||||
const auto& light = renderContext.List->PointLights[i];
|
||||
if (light.VolumetricScatteringIntensity > ZeroTolerance && !light.RenderedVolumetricFog)
|
||||
{
|
||||
if ((view.Position - light.Position).LengthSquared() < (options.Distance + light.Radius) * (options.Distance + light.Radius))
|
||||
{
|
||||
pointLights.Add(&light);
|
||||
}
|
||||
}
|
||||
const auto& light = renderContext.List->PointLights.Get()[i];
|
||||
if (light.VolumetricScatteringIntensity > ZeroTolerance &&
|
||||
(view.Position - light.Position).LengthSquared() < Math::Square(options.Distance + light.Radius))
|
||||
pointLights.Add(&light);
|
||||
}
|
||||
for (int32 i = 0; i < renderContext.List->SpotLights.Count(); i++)
|
||||
{
|
||||
const auto& light = renderContext.List->SpotLights[i];
|
||||
if (light.VolumetricScatteringIntensity > ZeroTolerance && !light.RenderedVolumetricFog)
|
||||
{
|
||||
if ((view.Position - light.Position).LengthSquared() < (options.Distance + light.Radius) * (options.Distance + light.Radius))
|
||||
{
|
||||
spotLights.Add(&light);
|
||||
}
|
||||
}
|
||||
const auto& light = renderContext.List->SpotLights.Get()[i];
|
||||
if (light.VolumetricScatteringIntensity > ZeroTolerance &&
|
||||
(view.Position - light.Position).LengthSquared() < Math::Square(options.Distance + light.Radius))
|
||||
spotLights.Add(&light);
|
||||
}
|
||||
|
||||
// Skip if no lights to render
|
||||
@@ -638,6 +503,8 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
context->SetViewportAndScissors((float)volumeDesc.Width, (float)volumeDesc.Height);
|
||||
|
||||
// Render them to the volume
|
||||
context->BindSR(0, shadowMap);
|
||||
context->BindSR(1, shadowsBuffer);
|
||||
for (int32 i = 0; i < pointLights.Count(); i++)
|
||||
RenderRadialLight(renderContext, context, view, options, *pointLights[i], perLight, cb1);
|
||||
for (int32 i = 0; i < spotLights.Count(); i++)
|
||||
@@ -666,19 +533,19 @@ void VolumetricFogPass::Render(RenderContext& renderContext)
|
||||
context->BindSR(1, vBufferB->ViewVolume());
|
||||
context->BindSR(2, lightScatteringHistory ? lightScatteringHistory->ViewVolume() : nullptr);
|
||||
context->BindSR(3, localShadowedLightScattering);
|
||||
context->BindSR(4, dirLightShadowMap);
|
||||
|
||||
context->BindSR(4, shadowMap);
|
||||
context->BindSR(5, shadowsBuffer);
|
||||
int32 csIndex;
|
||||
if (useDDGI)
|
||||
{
|
||||
context->BindSR(5, bindingDataDDGI.ProbesData);
|
||||
context->BindSR(6, bindingDataDDGI.ProbesDistance);
|
||||
context->BindSR(7, bindingDataDDGI.ProbesIrradiance);
|
||||
context->BindSR(6, bindingDataDDGI.ProbesData);
|
||||
context->BindSR(7, bindingDataDDGI.ProbesDistance);
|
||||
context->BindSR(8, bindingDataDDGI.ProbesIrradiance);
|
||||
csIndex = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->BindSR(5, skyLightImage);
|
||||
context->BindSR(6, skyLightImage);
|
||||
csIndex = 0;
|
||||
}
|
||||
context->Dispatch(_csLightScattering.Get(csIndex), groupCountX, groupCountY, groupCountZ);
|
||||
|
||||
@@ -32,7 +32,7 @@ private:
|
||||
Float3 MultiplyColor;
|
||||
float VolumetricScatteringIntensity;
|
||||
Float3 AdditiveColor;
|
||||
float Dummt0;
|
||||
float Dummy0;
|
||||
});
|
||||
|
||||
PACK_STRUCT(struct Data {
|
||||
@@ -63,7 +63,6 @@ private:
|
||||
Float4 FrameJitterOffsets[8];
|
||||
|
||||
ShaderLightData DirectionalLight;
|
||||
ShaderLightShadowData DirectionalLightShadow;
|
||||
SkyLightData SkyLight;
|
||||
DynamicDiffuseGlobalIlluminationPass::ConstantsData DDGI;
|
||||
});
|
||||
@@ -77,7 +76,6 @@ private:
|
||||
Matrix ViewToVolumeClip;
|
||||
|
||||
ShaderLightData LocalLight;
|
||||
ShaderLightShadowData LocalLightShadow;
|
||||
});
|
||||
|
||||
// Shader stuff
|
||||
@@ -147,27 +145,6 @@ public:
|
||||
VolumetricFogPass();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Renders the light to the volumetric fog light scattering volume texture. Called by the light pass after shadow map rendering. Used by the shadows casting lights.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="context">The GPU commands context.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <param name="shadowMap">The shadow map.</param>
|
||||
/// <param name="shadow">The light shadow data.</param>
|
||||
void RenderLight(RenderContext& renderContext, GPUContext* context, RenderPointLightData& light, GPUTextureView* shadowMap, ShaderLightShadowData& shadow);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the light to the volumetric fog light scattering volume texture. Called by the light pass after shadow map rendering. Used by the shadows casting lights.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="context">The GPU commands context.</param>
|
||||
/// <param name="light">The light.</param>
|
||||
/// <param name="shadowMap">The shadow map.</param>
|
||||
/// <param name="shadow">The light shadow data.</param>
|
||||
void RenderLight(RenderContext& renderContext, GPUContext* context, RenderSpotLightData& light, GPUTextureView* shadowMap, ShaderLightShadowData& shadow);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the volumetric fog (generates integrated light scattering 3D texture). Does nothing if feature is disabled or not supported.
|
||||
/// </summary>
|
||||
@@ -180,8 +157,6 @@ private:
|
||||
GPUTextureView* GetLocalShadowedLightScattering(RenderContext& renderContext, GPUContext* context, VolumetricFogOptions& options) const;
|
||||
void InitCircleBuffer();
|
||||
template<typename T>
|
||||
void RenderRadialLight(RenderContext& renderContext, GPUContext* context, T& light, ShaderLightShadowData& shadow);
|
||||
template<typename T>
|
||||
void RenderRadialLight(RenderContext& renderContext, GPUContext* context, RenderView& view, VolumetricFogOptions& options, T& light, PerLight& perLight, GPUConstantBuffer* cb1);
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
|
||||
@@ -159,7 +159,7 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target
|
||||
float toLightDst = GLOBAL_SDF_WORLD_SIZE;
|
||||
#endif
|
||||
float4 shadowMask = 1;
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
float shadowBias = 10.0f;
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
|
||||
ShadowData GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
|
||||
ShadowSample GetShadow(LightData lightData, GBufferSample gBuffer, float4 shadowMask)
|
||||
{
|
||||
ShadowData shadow;
|
||||
ShadowSample shadow;
|
||||
shadow.SurfaceShadow = gBuffer.AO * shadowMask.r;
|
||||
shadow.TransmissionShadow = shadowMask.g;
|
||||
return shadow;
|
||||
}
|
||||
|
||||
LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample StandardShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
float3 diffuseColor = GetDiffuseColor(gBuffer);
|
||||
float3 H = normalize(V + L);
|
||||
@@ -22,7 +22,7 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
|
||||
float NoH = saturate(dot(N, H));
|
||||
float VoH = saturate(dot(V, H));
|
||||
|
||||
LightingData lighting;
|
||||
LightSample lighting;
|
||||
lighting.Diffuse = Diffuse_Lambert(diffuseColor);
|
||||
#if LIGHTING_NO_SPECULAR
|
||||
lighting.Specular = 0;
|
||||
@@ -37,9 +37,9 @@ LightingData StandardShading(GBufferSample gBuffer, float energy, float3 L, floa
|
||||
return lighting;
|
||||
}
|
||||
|
||||
LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
LightSample lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Fake effect of the light going through the material
|
||||
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
||||
@@ -53,9 +53,9 @@ LightingData SubsurfaceShading(GBufferSample gBuffer, float energy, float3 L, fl
|
||||
return lighting;
|
||||
}
|
||||
|
||||
LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample FoliageShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
LightingData lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
LightSample lighting = StandardShading(gBuffer, energy, L, V, N);
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Fake effect of the light going through the thin foliage
|
||||
float3 subsurfaceColor = gBuffer.CustomData.rgb;
|
||||
@@ -67,7 +67,7 @@ LightingData FoliageShading(GBufferSample gBuffer, float energy, float3 L, float
|
||||
return lighting;
|
||||
}
|
||||
|
||||
LightingData SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
LightSample SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float3 V, half3 N)
|
||||
{
|
||||
switch (gBuffer.ShadingModel)
|
||||
{
|
||||
@@ -79,7 +79,7 @@ LightingData SurfaceShading(GBufferSample gBuffer, float energy, float3 L, float
|
||||
case SHADING_MODEL_FOLIAGE:
|
||||
return FoliageShading(gBuffer, energy, L, V, N);
|
||||
default:
|
||||
return (LightingData)0;
|
||||
return (LightSample)0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
float3 toLight = lightData.Direction;
|
||||
|
||||
// Calculate shadow
|
||||
ShadowData shadow = GetShadow(lightData, gBuffer, shadowMask);
|
||||
ShadowSample shadow = GetShadow(lightData, gBuffer, shadowMask);
|
||||
|
||||
// Calculate attenuation
|
||||
if (isRadial)
|
||||
@@ -147,7 +147,7 @@ float4 GetLighting(float3 viewPos, LightData lightData, GBufferSample gBuffer, f
|
||||
float energy = AreaLightSpecular(lightData, gBuffer.Roughness, toLight, L, V, N);
|
||||
|
||||
// Calculate direct lighting
|
||||
LightingData lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
||||
LightSample lighting = SurfaceShading(gBuffer, energy, L, V, N);
|
||||
|
||||
// Calculate final light color
|
||||
float3 surfaceLight = (lighting.Diffuse + lighting.Specular) * shadow.SurfaceShadow;
|
||||
|
||||
@@ -27,26 +27,26 @@ struct LightData
|
||||
float MinRoughness;
|
||||
|
||||
float3 Position;
|
||||
float CastShadows;
|
||||
uint ShadowsBufferAddress;
|
||||
|
||||
float3 Direction;
|
||||
float Radius;
|
||||
|
||||
float FalloffExponent;
|
||||
float InverseSquared;
|
||||
float Dummy0;
|
||||
float RadiusInv;
|
||||
float Dummy0;
|
||||
};
|
||||
|
||||
// Structure that contains information about shadow
|
||||
struct ShadowData
|
||||
// Structure that contains information about shadow sampling result
|
||||
struct ShadowSample
|
||||
{
|
||||
float SurfaceShadow;
|
||||
float TransmissionShadow;
|
||||
};
|
||||
|
||||
// Structure that contains information about direct lighting calculations result
|
||||
struct LightingData
|
||||
struct LightSample
|
||||
{
|
||||
float3 Diffuse;
|
||||
float3 Specular;
|
||||
|
||||
@@ -61,7 +61,7 @@ void PS_Directional(Quad_VS2PS input, out float4 output : SV_Target0)
|
||||
// Sample shadow mask
|
||||
float4 shadowMask = 1;
|
||||
BRANCH
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
shadowMask = SAMPLE_RT(Shadow, input.TexCoord);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ void PS_Point(Model_VS2PS input, out float4 output : SV_Target0)
|
||||
// Sample shadow mask
|
||||
float4 shadowMask = 1;
|
||||
BRANCH
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
shadowMask = SAMPLE_RT(Shadow, uv);
|
||||
}
|
||||
@@ -140,7 +140,7 @@ void PS_Spot(Model_VS2PS input, out float4 output : SV_Target0)
|
||||
// Sample shadow mask
|
||||
float4 shadowMask = 1;
|
||||
BRANCH
|
||||
if (Light.CastShadows > 0)
|
||||
if (Light.ShadowsBufferAddress != 0)
|
||||
{
|
||||
shadowMask = SAMPLE_RT(Shadow, uv);
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#ifndef __PCF_KERNELS__
|
||||
#define __PCF_KERNELS__
|
||||
|
||||
// Cascades Shadow Mapping
|
||||
|
||||
#if FilterSizeCSM == 2
|
||||
|
||||
#elif FilterSizeCSM == 3
|
||||
|
||||
static const float CSMFilterWeightsSum = 7;
|
||||
static const float CSMFilterWeights[3][3] =
|
||||
{
|
||||
{ 0.5,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,0.5 }
|
||||
};
|
||||
|
||||
#elif FilterSizeCSM == 5
|
||||
|
||||
static const float CSMFilterWeightsSum = 17;
|
||||
static const float CSMFilterWeights[5][5] =
|
||||
{
|
||||
{ 0.0,0.5,1.0,0.5,0.0 },
|
||||
{ 0.5,1.0,1.0,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,1.0,1.0,0.5 },
|
||||
{ 0.0,0.5,1.0,0.5,0.0 }
|
||||
};
|
||||
|
||||
#elif FilterSizeCSM == 7
|
||||
|
||||
static const float CSMFilterWeightsSum = 33;
|
||||
static const float CSMFilterWeights[7][7] =
|
||||
{
|
||||
{ 0.0,0.0,0.5,1.0,0.5,0.0,0.0 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.0,0.0,0.5,1.0,0.5,0.0,0.0 }
|
||||
};
|
||||
|
||||
#elif FilterSizeCSM == 9
|
||||
|
||||
static const float CSMFilterWeightsSum = 53;
|
||||
static const float CSMFilterWeights[9][9] =
|
||||
{
|
||||
{ 0.0,0.0,0.0,0.5,1.0,0.5,0.0,0.0,0.0 },
|
||||
{ 0.0,0.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 },
|
||||
{ 0.5,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.5 },
|
||||
{ 0.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0 },
|
||||
{ 0.0,0.0,1.0,1.0,1.0,1.0,1.0,0.0,0.0 },
|
||||
{ 0.0,0.0,0.0,0.5,1.0,0.5,0.0,0.0,0.0 }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// Cube Map Shadows
|
||||
|
||||
#if FilterSizeCube == 5
|
||||
|
||||
// 5 random points in disc with radius 2.5
|
||||
static const float2 PCFDiscSamples[5] =
|
||||
{
|
||||
float2(0.000000, 2.500000),
|
||||
float2(2.377641, 0.772542),
|
||||
float2(1.469463, -2.022543),
|
||||
float2(-1.469463, -2.022542),
|
||||
float2(-2.377641, 0.772543),
|
||||
};
|
||||
|
||||
#elif FilterSizeCube == 12
|
||||
|
||||
// 12 random points in disc with radius 2.5
|
||||
static const float2 PCFDiscSamples[12] =
|
||||
{
|
||||
float2(0.000000, 2.500000),
|
||||
float2(1.767767, 1.767767),
|
||||
float2(2.500000, -0.000000),
|
||||
float2(1.767767, -1.767767),
|
||||
float2(-0.000000, -2.500000),
|
||||
float2(-1.767767, -1.767767),
|
||||
float2(-2.500000, 0.000000),
|
||||
float2(-1.767766, 1.767768),
|
||||
float2(-1.006119, -0.396207),
|
||||
float2(1.000015, 0.427335),
|
||||
float2(0.416807, -1.006577),
|
||||
float2(-0.408872, 1.024430),
|
||||
};
|
||||
|
||||
#elif FilterSizeCube == 29
|
||||
|
||||
// 29 random points in disc with radius 2.5
|
||||
static const float2 PCFDiscSamples[29] =
|
||||
{
|
||||
float2(0.000000, 2.500000),
|
||||
float2(1.016842, 2.283864),
|
||||
float2(1.857862, 1.672826),
|
||||
float2(2.377641, 0.772542),
|
||||
float2(2.486305, -0.261321),
|
||||
float2(2.165063, -1.250000),
|
||||
float2(1.469463, -2.022543),
|
||||
float2(0.519779, -2.445369),
|
||||
float2(-0.519779, -2.445369),
|
||||
float2(-1.469463, -2.022542),
|
||||
float2(-2.165064, -1.250000),
|
||||
float2(-2.486305, -0.261321),
|
||||
float2(-2.377641, 0.772543),
|
||||
float2(-1.857862, 1.672827),
|
||||
float2(-1.016841, 2.283864),
|
||||
float2(0.091021, -0.642186),
|
||||
float2(0.698035, 0.100940),
|
||||
float2(0.959731, -1.169393),
|
||||
float2(-1.053880, 1.180380),
|
||||
float2(-1.479156, -0.606937),
|
||||
float2(-0.839488, -1.320002),
|
||||
float2(1.438566, 0.705359),
|
||||
float2(0.067064, -1.605197),
|
||||
float2(0.728706, 1.344722),
|
||||
float2(1.521424, -0.380184),
|
||||
float2(-0.199515, 1.590091),
|
||||
float2(-1.524323, 0.364010),
|
||||
float2(-0.692694, -0.086749),
|
||||
float2(-0.082476, 0.654088),
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -56,8 +56,6 @@ float4 PS_CopyLinear(Quad_VS2PS input) : SV_Target
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_Clear
|
||||
|
||||
// Pixel Shader for clearing a render target with a solid color
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float4 PS_Clear(Quad_VS2PS input) : SV_Target
|
||||
@@ -65,4 +63,9 @@ float4 PS_Clear(Quad_VS2PS input) : SV_Target
|
||||
return Color;
|
||||
}
|
||||
|
||||
#endif
|
||||
// Pixel Shader for clearing depth buffer
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
float PS_DepthClear(Quad_VS2PS input) : SV_Depth
|
||||
{
|
||||
return Color.r;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
META_CB_BEGIN(0, PerLight)
|
||||
GBufferData GBuffer;
|
||||
LightData Light;
|
||||
LightShadowData LightShadow;
|
||||
float4x4 WVP;
|
||||
float4x4 ViewProjectionMatrix;
|
||||
float2 Dummy0;
|
||||
@@ -18,8 +17,10 @@ float ContactShadowsDistance;
|
||||
float ContactShadowsLength;
|
||||
META_CB_END
|
||||
|
||||
Buffer<float4> ShadowsBuffer : register(t5);
|
||||
Texture2D<float> ShadowMap : register(t6);
|
||||
|
||||
DECLARE_GBUFFERDATA_ACCESS(GBuffer)
|
||||
DECLARE_LIGHTSHADOWDATA_ACCESS(LightShadow);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
|
||||
@@ -67,10 +68,6 @@ Model_VS2PS VS_Model(ModelInput_PosOnly input)
|
||||
return output;
|
||||
}
|
||||
|
||||
#ifdef _PS_PointLight
|
||||
|
||||
TextureCube<float> ShadowMapPoint : register(t5);
|
||||
|
||||
// Pixel shader for point light shadow rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
|
||||
@@ -83,9 +80,6 @@ META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
|
||||
float4 PS_PointLight(Model_VS2PS input) : SV_Target0
|
||||
{
|
||||
float shadow = 1;
|
||||
float subsurfaceShadow = 1;
|
||||
|
||||
// Obtain texture coordinates corresponding to the current pixel
|
||||
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
|
||||
|
||||
@@ -94,68 +88,43 @@ float4 PS_PointLight(Model_VS2PS input) : SV_Target0
|
||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
|
||||
|
||||
// Sample shadow
|
||||
LightShadowData lightShadowData = GetLightShadowData();
|
||||
shadow = SampleShadow(Light, lightShadowData, ShadowMapPoint, gBuffer, subsurfaceShadow);
|
||||
ShadowSample shadow = SamplePointLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
// Calculate screen-space contact shadow
|
||||
shadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
#endif
|
||||
|
||||
return float4(shadow, subsurfaceShadow, 1, 1);
|
||||
return GetShadowMask(shadow);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_DirLight
|
||||
|
||||
Texture2DArray ShadowMapDir : register(t5);
|
||||
|
||||
// Pixel shader for directional light shadow rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=0,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=1,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=2,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_3(SHADOWS_QUALITY=3,CSM_BLENDING=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=0)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=1,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
|
||||
float4 PS_DirLight(Quad_VS2PS input) : SV_Target0
|
||||
{
|
||||
float shadow = 1;
|
||||
float subsurfaceShadow = 1;
|
||||
|
||||
// Sample GBuffer
|
||||
GBufferData gBufferData = GetGBufferData();
|
||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
|
||||
|
||||
// Sample shadow
|
||||
LightShadowData lightShadowData = GetLightShadowData();
|
||||
shadow = SampleShadow(Light, lightShadowData, ShadowMapDir, gBuffer, subsurfaceShadow);
|
||||
ShadowSample shadow = SampleDirectionalLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
// Calculate screen-space contact shadow
|
||||
shadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, Light.Direction, ContactShadowsLength);
|
||||
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, Light.Direction, ContactShadowsLength);
|
||||
#endif
|
||||
|
||||
return float4(shadow, subsurfaceShadow, 1, 1);
|
||||
return GetShadowMask(shadow);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _PS_SpotLight
|
||||
|
||||
Texture2D ShadowMapSpot : register(t5);
|
||||
|
||||
// Pixel shader for spot light shadow rendering
|
||||
META_PS(true, FEATURE_LEVEL_ES2)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=0,CONTACT_SHADOWS=0)
|
||||
@@ -168,9 +137,6 @@ META_PERMUTATION_2(SHADOWS_QUALITY=2,CONTACT_SHADOWS=1)
|
||||
META_PERMUTATION_2(SHADOWS_QUALITY=3,CONTACT_SHADOWS=1)
|
||||
float4 PS_SpotLight(Model_VS2PS input) : SV_Target0
|
||||
{
|
||||
float shadow = 1;
|
||||
float subsurfaceShadow = 1;
|
||||
|
||||
// Obtain texture coordinates corresponding to the current pixel
|
||||
float2 uv = (input.ScreenPos.xy / input.ScreenPos.w) * float2(0.5, -0.5) + float2(0.5, 0.5);
|
||||
|
||||
@@ -179,15 +145,12 @@ float4 PS_SpotLight(Model_VS2PS input) : SV_Target0
|
||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
|
||||
|
||||
// Sample shadow
|
||||
LightShadowData lightShadowData = GetLightShadowData();
|
||||
shadow = SampleShadow(Light, lightShadowData, ShadowMapSpot, gBuffer, subsurfaceShadow);
|
||||
ShadowSample shadow = SampleSpotLightShadow(Light, ShadowsBuffer, ShadowMap, gBuffer);
|
||||
|
||||
#if CONTACT_SHADOWS
|
||||
// Calculate screen-space contact shadow
|
||||
shadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
shadow.SurfaceShadow *= RayCastScreenSpaceShadow(gBufferData, gBuffer, gBuffer.WorldPos, normalize(Light.Position - gBuffer.WorldPos), ContactShadowsLength);
|
||||
#endif
|
||||
|
||||
return float4(shadow, subsurfaceShadow, 1, 1);
|
||||
return GetShadowMask(shadow);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,32 +12,57 @@
|
||||
#ifndef SHADOWS_QUALITY
|
||||
#define SHADOWS_QUALITY 0
|
||||
#endif
|
||||
#ifndef CSM_BLENDING
|
||||
#define CSM_BLENDING 0
|
||||
#endif
|
||||
|
||||
// Structure that contains information about light
|
||||
struct LightShadowData
|
||||
// Shadow data for the light
|
||||
struct ShadowData
|
||||
{
|
||||
float2 ShadowMapSize;
|
||||
float Sharpness;
|
||||
float Fade;
|
||||
|
||||
float FadeDistance;
|
||||
float NormalOffsetScale;
|
||||
float Bias;
|
||||
float FadeDistance;
|
||||
uint NumCascades;
|
||||
|
||||
uint TilesCount;
|
||||
float4 CascadeSplits;
|
||||
float4x4 ShadowVP[6];
|
||||
};
|
||||
|
||||
#ifdef PLATFORM_ANDROID
|
||||
// #AdrenoVK_CB_STRUCT_MEMBER_ACCESS_BUG
|
||||
#define DECLARE_LIGHTSHADOWDATA_ACCESS(uniformName) LightShadowData Get##uniformName##Data() { LightShadowData tmp; tmp.ShadowMapSize = uniformName.ShadowMapSize; tmp.Sharpness = uniformName.Sharpness; tmp.Fade = uniformName.Fade; tmp.NormalOffsetScale = uniformName.NormalOffsetScale; tmp.Bias = uniformName.Bias; tmp.FadeDistance = uniformName.FadeDistance; tmp.NumCascades = uniformName.NumCascades; tmp.CascadeSplits = uniformName.CascadeSplits; tmp.ShadowVP[0] = uniformName.ShadowVP[0]; tmp.ShadowVP[1] = uniformName.ShadowVP[1]; tmp.ShadowVP[2] = uniformName.ShadowVP[2]; tmp.ShadowVP[3] = uniformName.ShadowVP[3]; tmp.ShadowVP[4] = uniformName.ShadowVP[4]; tmp.ShadowVP[5] = uniformName.ShadowVP[5]; return tmp; }
|
||||
#else
|
||||
#define DECLARE_LIGHTSHADOWDATA_ACCESS(uniformName) LightShadowData Get##uniformName##Data() { return uniformName; }
|
||||
#endif
|
||||
// Shadow projection tile data for the light
|
||||
struct ShadowTileData
|
||||
{
|
||||
float4 ShadowToAtlas;
|
||||
float4x4 WorldToShadow;
|
||||
};
|
||||
|
||||
// Loads the shadow data of the light in the shadow buffer
|
||||
ShadowData LoadShadowsBuffer(Buffer<float4> shadowsBuffer, uint shadowsBufferAddress)
|
||||
{
|
||||
// This must match C++
|
||||
float4 vector0 = shadowsBuffer.Load(shadowsBufferAddress + 0);
|
||||
float4 vector1 = shadowsBuffer.Load(shadowsBufferAddress + 1);
|
||||
ShadowData shadow;
|
||||
uint packed0x = asuint(vector0.x);
|
||||
shadow.Sharpness = (packed0x & 0x000000ff) * (10.0f / 255.0f);
|
||||
shadow.Fade = ((packed0x & 0x0000ff00) >> 8) * (1.0f / 255.0f);
|
||||
shadow.TilesCount = ((packed0x & 0x00ff0000) >> 16);
|
||||
shadow.FadeDistance = vector0.y;
|
||||
shadow.NormalOffsetScale = vector0.z;
|
||||
shadow.Bias = vector0.w;
|
||||
shadow.CascadeSplits = vector1;
|
||||
return shadow;
|
||||
}
|
||||
|
||||
// Loads the shadow tile data of the light in the shadow buffer
|
||||
ShadowTileData LoadShadowsBufferTile(Buffer<float4> shadowsBuffer, uint shadowsBufferAddress, uint tileIndex)
|
||||
{
|
||||
// This must match C++
|
||||
shadowsBufferAddress += tileIndex * 5 + 2;
|
||||
ShadowTileData tile;
|
||||
tile.ShadowToAtlas = shadowsBuffer.Load(shadowsBufferAddress + 0);
|
||||
tile.WorldToShadow[0] = shadowsBuffer.Load(shadowsBufferAddress + 1);
|
||||
tile.WorldToShadow[1] = shadowsBuffer.Load(shadowsBufferAddress + 2);
|
||||
tile.WorldToShadow[2] = shadowsBuffer.Load(shadowsBufferAddress + 3);
|
||||
tile.WorldToShadow[3] = shadowsBuffer.Load(shadowsBufferAddress + 4);
|
||||
return tile;
|
||||
}
|
||||
|
||||
float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
|
||||
{
|
||||
@@ -48,8 +73,16 @@ float3 GetShadowPositionOffset(float offsetScale, float NoL, float3 normal)
|
||||
float CalculateSubsurfaceOcclusion(float opacity, float sceneDepth, float shadowMapDepth)
|
||||
{
|
||||
float thickness = max(sceneDepth - shadowMapDepth, 0);
|
||||
float occlusion = 1 - thickness * lerp(1.0f, 100.0f, opacity);
|
||||
float occlusion = 1 - saturate(thickness * lerp(1.0f, 100.0f, opacity));
|
||||
return shadowMapDepth > 0.99f ? 1 : occlusion;
|
||||
}
|
||||
|
||||
float PostProcessShadow(ShadowData lightShadow, float shadow)
|
||||
{
|
||||
// Apply shadow fade and sharpness
|
||||
shadow = saturate((shadow - 0.5) * lightShadow.Sharpness + 0.5);
|
||||
shadow = lerp(1.0f, shadow, lightShadow.Fade);
|
||||
return shadow;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,46 +7,24 @@
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
#include "./Flax/LightingCommon.hlsl"
|
||||
|
||||
// Select shadows filter based on quality
|
||||
// Supported sampling kernel sizes fo each shadowing technique:
|
||||
// CSM: 2, 3, 5, 7, 9
|
||||
// Cube: 2, 5, 12, 29
|
||||
// Spot: 2, 5, 12, 29
|
||||
#if SHADOWS_QUALITY == 0
|
||||
|
||||
#define FilterSizeCSM 2
|
||||
#define FilterSizeCube 2
|
||||
#define FilterSizeSpot 2
|
||||
|
||||
#elif SHADOWS_QUALITY == 1
|
||||
|
||||
#define FilterSizeCSM 3
|
||||
#define FilterSizeCube 5
|
||||
#define FilterSizeSpot 5
|
||||
|
||||
#elif SHADOWS_QUALITY == 2
|
||||
|
||||
#define FilterSizeCSM 5
|
||||
#define FilterSizeCube 12
|
||||
#define FilterSizeSpot 12
|
||||
|
||||
#else // SHADOWS_QUALITY == 3
|
||||
|
||||
#define FilterSizeCSM 7
|
||||
#define FilterSizeCube 12
|
||||
#define FilterSizeSpot 12
|
||||
|
||||
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
||||
#define SAMPLE_SHADOW_MAP(shadowMap, shadowUV, sceneDepth) shadowMap.SampleCmpLevelZero(ShadowSamplerLinear, shadowUV, sceneDepth)
|
||||
#define SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowUV, texelOffset, sceneDepth) shadowMap.SampleCmpLevelZero(ShadowSamplerLinear, shadowUV, sceneDepth, texelOffset)
|
||||
#else
|
||||
#define SAMPLE_SHADOW_MAP(shadowMap, shadowUV, sceneDepth) (sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0).r)
|
||||
#define SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowUV, texelOffset, sceneDepth) (sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, shadowUV, 0, texelOffset).r)
|
||||
#endif
|
||||
|
||||
#if SHADOWS_QUALITY != 0
|
||||
#include "./Flax/PCFKernels.hlsl"
|
||||
#endif
|
||||
float4 GetShadowMask(ShadowSample shadow)
|
||||
{
|
||||
return float4(shadow.SurfaceShadow, shadow.TransmissionShadow, 1, 1);
|
||||
}
|
||||
|
||||
// Gets the cube texture face index to use for shadow map sampling for the given view-to-light direction vector
|
||||
// Where: direction = normalize(worldPosition - lightPosition)
|
||||
int GetCubeFaceIndex(float3 direction)
|
||||
uint GetCubeFaceIndex(float3 direction)
|
||||
{
|
||||
int cubeFaceIndex;
|
||||
uint cubeFaceIndex;
|
||||
float3 absDirection = abs(direction);
|
||||
float maxDirection = max(absDirection.x, max(absDirection.y, absDirection.z));
|
||||
if (maxDirection == absDirection.x)
|
||||
@@ -58,666 +36,230 @@ int GetCubeFaceIndex(float3 direction)
|
||||
return cubeFaceIndex;
|
||||
}
|
||||
|
||||
// Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmpRed.
|
||||
// Uses code from "Fast Conventional Shadow Filtering" by Holger Gruen, in GPU Pro.
|
||||
float SampleShadowMapFixedSizePCF(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPos, uint cascadeIndex)
|
||||
float2 GetLightShadowAtlasUV(ShadowData shadow, ShadowTileData shadowTile, float3 samplePosition, out float4 shadowPosition)
|
||||
{
|
||||
#if FilterSizeCSM == 2
|
||||
|
||||
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
|
||||
|
||||
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
|
||||
|
||||
#else
|
||||
|
||||
return sceneDepth < shadowMap.SampleLevel(SamplerLinearClamp, float3(shadowPos.xy, cascadeIndex), 0).r;
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
const int FS_2 = FilterSizeCSM / 2;
|
||||
float2 tc = shadowPos.xy;
|
||||
float4 s = 0.0f;
|
||||
float2 stc = (shadowMapSize * tc.xy) + float2(0.5f, 0.5f);
|
||||
float2 tcs = floor(stc);
|
||||
float2 fc;
|
||||
int row;
|
||||
int col;
|
||||
float4 v1[FS_2 + 1];
|
||||
float2 v0[FS_2 + 1];
|
||||
float3 baseUV = float3(tc.xy, cascadeIndex);
|
||||
float2 shadowMapSizeInv = 1.0f / shadowMapSize;
|
||||
|
||||
fc.xy = stc - tcs;
|
||||
tc.xy = tcs * shadowMapSizeInv;
|
||||
|
||||
// Loop over the rows
|
||||
UNROLL
|
||||
for (row = -FS_2; row <= FS_2; row += 2)
|
||||
{
|
||||
UNROLL
|
||||
for (col = -FS_2; col <= FS_2; col += 2)
|
||||
{
|
||||
float value = CSMFilterWeights[row + FS_2][col + FS_2];
|
||||
|
||||
if (col > -FS_2)
|
||||
value += CSMFilterWeights[row + FS_2][col + FS_2 - 1];
|
||||
|
||||
if (col < FS_2)
|
||||
value += CSMFilterWeights[row + FS_2][col + FS_2 + 1];
|
||||
|
||||
if (row > -FS_2) {
|
||||
value += CSMFilterWeights[row + FS_2 - 1][col + FS_2];
|
||||
|
||||
if (col < FS_2)
|
||||
value += CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1];
|
||||
|
||||
if (col > -FS_2)
|
||||
value += CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1];
|
||||
}
|
||||
|
||||
if (value != 0.0f)
|
||||
{
|
||||
// Gather returns xyzw which is counter clockwise order starting with the sample to the lower left of the queried location
|
||||
#if CAN_USE_GATHER
|
||||
|
||||
v1[(col + FS_2) / 2] = shadowMap.GatherCmp(ShadowSampler, baseUV, sceneDepth, int2(col, row));
|
||||
|
||||
#else
|
||||
|
||||
float4 gather;
|
||||
|
||||
gather.x = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(0, 1) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
gather.y = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(1, 1) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
gather.z = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(1, 0) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
gather.w = sceneDepth < shadowMap.SampleLevel(SamplerPointClamp, float3(tc.xy + float2(0, 0) * shadowMapSizeInv, cascadeIndex), 0, int2(col, row)).r;
|
||||
|
||||
v1[(col + FS_2) / 2] = gather;
|
||||
|
||||
#endif
|
||||
}
|
||||
else
|
||||
v1[(col + FS_2) / 2] = 0.0f;
|
||||
|
||||
if (col == -FS_2)
|
||||
{
|
||||
s.x += (1.0f - fc.y) * (v1[0].w * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2] * fc.x)
|
||||
+ v1[0].z * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
s.y += fc.y * (v1[0].x * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2] * fc.x)
|
||||
+ v1[0].y * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
if(row > -FS_2)
|
||||
{
|
||||
s.z += (1.0f - fc.y) * (v0[0].x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2] * fc.x)
|
||||
+ v0[0].y * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
s.w += fc.y * (v1[0].w * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2] * fc.x)
|
||||
+ v1[0].z * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
}
|
||||
}
|
||||
else if (col == FS_2)
|
||||
{
|
||||
s.x += (1 - fc.y) * (v1[FS_2].w * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2]) + CSMFilterWeights[row + FS_2][col + FS_2])
|
||||
+ v1[FS_2].z * fc.x * CSMFilterWeights[row + FS_2][col + FS_2]);
|
||||
s.y += fc.y * (v1[FS_2].x * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2] ) + CSMFilterWeights[row + FS_2][col + FS_2])
|
||||
+ v1[FS_2].y * fc.x * CSMFilterWeights[row + FS_2][col + FS_2]);
|
||||
if(row > -FS_2) {
|
||||
s.z += (1 - fc.y) * (v0[FS_2].x * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ v0[FS_2].y * fc.x * CSMFilterWeights[row + FS_2 - 1][col + FS_2]);
|
||||
s.w += fc.y * (v1[FS_2].w * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ CSMFilterWeights[row + FS_2 - 1][col + FS_2])
|
||||
+ v1[FS_2].z * fc.x * CSMFilterWeights[row + FS_2 - 1][col + FS_2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.x += (1 - fc.y) * (v1[(col + FS_2) / 2].w * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 0] ) + CSMFilterWeights[row + FS_2][col + FS_2 + 0])
|
||||
+ v1[(col + FS_2) / 2].z * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
s.y += fc.y * (v1[(col + FS_2) / 2].x * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2-1]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 0]) + CSMFilterWeights[row + FS_2][col + FS_2 + 0])
|
||||
+ v1[(col + FS_2) / 2].y * (fc.x * (CSMFilterWeights[row + FS_2][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2][col + FS_2 + 1]));
|
||||
if(row > -FS_2) {
|
||||
s.z += (1 - fc.y) * (v0[(col + FS_2) / 2].x * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0])
|
||||
+ v0[(col + FS_2) / 2].y * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
s.w += fc.y * (v1[(col + FS_2) / 2].w * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 1]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 0])
|
||||
+ v1[(col + FS_2) / 2].z * (fc.x * (CSMFilterWeights[row + FS_2 - 1][col + FS_2 - 0]
|
||||
- CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]) + CSMFilterWeights[row + FS_2 - 1][col + FS_2 + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
if (row != FS_2)
|
||||
v0[(col + FS_2) / 2] = v1[(col + FS_2) / 2].xy;
|
||||
}
|
||||
}
|
||||
|
||||
return dot(s, 1.0f) / CSMFilterWeightsSum;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper function for SampleShadowMapOptimizedPCF
|
||||
float SampleShadowMap(Texture2DArray shadowMap, float2 baseUv, float u, float v, float2 shadowMapSizeInv, uint cascadeIndex, float depth)
|
||||
{
|
||||
float2 uv = baseUv + float2(u, v) * shadowMapSizeInv;
|
||||
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(uv, cascadeIndex), depth);
|
||||
}
|
||||
|
||||
// The method used in The Witness
|
||||
float SampleShadowMapOptimizedPCF(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPos, uint cascadeIndex)
|
||||
{
|
||||
float2 uv = shadowPos.xy * shadowMapSize; // 1 unit - 1 texel
|
||||
float2 shadowMapSizeInv = 1.0f / shadowMapSize;
|
||||
|
||||
float2 baseUv;
|
||||
baseUv.x = floor(uv.x + 0.5);
|
||||
baseUv.y = floor(uv.y + 0.5);
|
||||
float s = (uv.x + 0.5 - baseUv.x);
|
||||
float t = (uv.y + 0.5 - baseUv.y);
|
||||
baseUv -= float2(0.5, 0.5);
|
||||
baseUv *= shadowMapSizeInv;
|
||||
|
||||
float sum = 0;
|
||||
|
||||
#if FilterSizeCSM == 2
|
||||
|
||||
return shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, float3(shadowPos.xy, cascadeIndex), sceneDepth);
|
||||
|
||||
#elif FilterSizeCSM == 3
|
||||
|
||||
float uw0 = (3 - 2 * s);
|
||||
float uw1 = (1 + 2 * s);
|
||||
|
||||
float u0 = (2 - s) / uw0 - 1;
|
||||
float u1 = s / uw1 + 1;
|
||||
|
||||
float vw0 = (3 - 2 * t);
|
||||
float vw1 = (1 + 2 * t);
|
||||
|
||||
float v0 = (2 - t) / vw0 - 1;
|
||||
float v1 = t / vw1 + 1;
|
||||
|
||||
sum += uw0 * vw0 * SampleShadowMap(shadowMap, baseUv, u0, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw0 * SampleShadowMap(shadowMap, baseUv, u1, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw0 * vw1 * SampleShadowMap(shadowMap, baseUv, u0, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw1 * SampleShadowMap(shadowMap, baseUv, u1, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
return sum * 1.0f / 16;
|
||||
|
||||
#elif FilterSizeCSM == 5
|
||||
|
||||
float uw0 = (4 - 3 * s);
|
||||
float uw1 = 7;
|
||||
float uw2 = (1 + 3 * s);
|
||||
|
||||
float u0 = (3 - 2 * s) / uw0 - 2;
|
||||
float u1 = (3 + s) / uw1;
|
||||
float u2 = s / uw2 + 2;
|
||||
|
||||
float vw0 = (4 - 3 * t);
|
||||
float vw1 = 7;
|
||||
float vw2 = (1 + 3 * t);
|
||||
|
||||
float v0 = (3 - 2 * t) / vw0 - 2;
|
||||
float v1 = (3 + t) / vw1;
|
||||
float v2 = t / vw2 + 2;
|
||||
|
||||
sum += uw0 * vw0 * SampleShadowMap(shadowMap, baseUv, u0, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw0 * SampleShadowMap(shadowMap, baseUv, u1, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw0 * SampleShadowMap(shadowMap, baseUv, u2, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw1 * SampleShadowMap(shadowMap, baseUv, u0, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw1 * SampleShadowMap(shadowMap, baseUv, u1, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw1 * SampleShadowMap(shadowMap, baseUv, u2, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw2 * SampleShadowMap(shadowMap, baseUv, u0, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw2 * SampleShadowMap(shadowMap, baseUv, u1, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw2 * SampleShadowMap(shadowMap, baseUv, u2, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
return sum * 1.0f / 144;
|
||||
|
||||
#else // FilterSizeCSM == 7
|
||||
|
||||
float uw0 = (5 * s - 6);
|
||||
float uw1 = (11 * s - 28);
|
||||
float uw2 = -(11 * s + 17);
|
||||
float uw3 = -(5 * s + 1);
|
||||
|
||||
float u0 = (4 * s - 5) / uw0 - 3;
|
||||
float u1 = (4 * s - 16) / uw1 - 1;
|
||||
float u2 = -(7 * s + 5) / uw2 + 1;
|
||||
float u3 = -s / uw3 + 3;
|
||||
|
||||
float vw0 = (5 * t - 6);
|
||||
float vw1 = (11 * t - 28);
|
||||
float vw2 = -(11 * t + 17);
|
||||
float vw3 = -(5 * t + 1);
|
||||
|
||||
float v0 = (4 * t - 5) / vw0 - 3;
|
||||
float v1 = (4 * t - 16) / vw1 - 1;
|
||||
float v2 = -(7 * t + 5) / vw2 + 1;
|
||||
float v3 = -t / vw3 + 3;
|
||||
|
||||
sum += uw0 * vw0 * SampleShadowMap(shadowMap, baseUv, u0, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw0 * SampleShadowMap(shadowMap, baseUv, u1, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw0 * SampleShadowMap(shadowMap, baseUv, u2, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw0 * SampleShadowMap(shadowMap, baseUv, u3, v0, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw1 * SampleShadowMap(shadowMap, baseUv, u0, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw1 * SampleShadowMap(shadowMap, baseUv, u1, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw1 * SampleShadowMap(shadowMap, baseUv, u2, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw1 * SampleShadowMap(shadowMap, baseUv, u3, v1, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw2 * SampleShadowMap(shadowMap, baseUv, u0, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw2 * SampleShadowMap(shadowMap, baseUv, u1, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw2 * SampleShadowMap(shadowMap, baseUv, u2, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw2 * SampleShadowMap(shadowMap, baseUv, u3, v2, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
sum += uw0 * vw3 * SampleShadowMap(shadowMap, baseUv, u0, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw1 * vw3 * SampleShadowMap(shadowMap, baseUv, u1, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw2 * vw3 * SampleShadowMap(shadowMap, baseUv, u2, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
sum += uw3 * vw3 * SampleShadowMap(shadowMap, baseUv, u3, v3, shadowMapSizeInv, cascadeIndex, sceneDepth);
|
||||
|
||||
return sum * (1.0f / 2704);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Samples the shadow from the shadow map cascade
|
||||
float SampleShadowCascade(Texture2DArray shadowMap, float2 shadowMapSize, float sceneDepth, float2 shadowPosition, uint cascadeIndex)
|
||||
{
|
||||
float shadow = SampleShadowMapFixedSizePCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
|
||||
//float shadow = SampleShadowMapOptimizedPCF(shadowMap, shadowMapSize, sceneDepth, shadowPosition, cascadeIndex);
|
||||
return shadow;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given directional light (cascaded shadow map sampling)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, float3 worldPosition, float viewDepth)
|
||||
{
|
||||
// Create a blend factor which is one before and at the fade plane
|
||||
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
|
||||
BRANCH
|
||||
if (fade >= 1.0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Figure out which cascade to sample from
|
||||
uint cascadeIndex = 0;
|
||||
for (uint i = 0; i < shadow.NumCascades - 1; i++)
|
||||
{
|
||||
if (viewDepth > shadow.CascadeSplits[i])
|
||||
cascadeIndex = i + 1;
|
||||
}
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex]);
|
||||
shadowPosition.xy /= shadowPosition.w;
|
||||
// Project into shadow space (WorldToShadow is pre-multiplied to convert Clip Space to UV Space)
|
||||
shadowPosition = mul(float4(samplePosition, 1.0f), shadowTile.WorldToShadow);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
// Sample shadow
|
||||
float result = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex);
|
||||
// UV Space -> Atlas Tile UV Space
|
||||
float2 shadowMapUV = saturate(shadowPosition.xy);
|
||||
shadowMapUV = shadowMapUV * shadowTile.ShadowToAtlas.xy + shadowTile.ShadowToAtlas.zw;
|
||||
return shadowMapUV;
|
||||
}
|
||||
|
||||
// Increase the sharpness for higher cascades to match the filter radius
|
||||
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
|
||||
float sharpness = shadow.Sharpness * SharpnessScale[cascadeIndex];
|
||||
|
||||
#if CSM_BLENDING
|
||||
// Sample the next cascade, and blend between the two results to smooth the transition
|
||||
const float BlendThreshold = 0.1f;
|
||||
float nextSplit = shadow.CascadeSplits[cascadeIndex];
|
||||
float splitSize = cascadeIndex == 0 ? nextSplit : nextSplit - shadow.CascadeSplits[cascadeIndex - 1];
|
||||
float splitDist = (nextSplit - viewDepth) / splitSize;
|
||||
BRANCH
|
||||
if (splitDist <= BlendThreshold && cascadeIndex != shadow.NumCascades - 1)
|
||||
{
|
||||
// Find the position of this pixel in light space of next cascade
|
||||
shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cascadeIndex + 1]);
|
||||
shadowPosition.xy /= shadowPosition.w;
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
|
||||
// Sample next cascade and blur result
|
||||
float nextSplitShadow = SampleShadowCascade(shadowMap, shadow.ShadowMapSize, shadowPosition.z, shadowPosition.xy, cascadeIndex + 1);
|
||||
float lerpAmount = smoothstep(0.0f, BlendThreshold, splitDist);
|
||||
lerpAmount = splitDist / BlendThreshold;
|
||||
result = lerp(nextSplitShadow, result, lerpAmount);
|
||||
|
||||
// Blur sharpness as well
|
||||
sharpness = lerp(shadow.Sharpness * SharpnessScale[cascadeIndex + 1], sharpness, lerpAmount);
|
||||
}
|
||||
float SampleShadowMap(Texture2D<float> shadowMap, float2 shadowMapUV, float sceneDepth)
|
||||
{
|
||||
// Single hardware sample with filtering
|
||||
float result = SAMPLE_SHADOW_MAP(shadowMap, shadowMapUV, sceneDepth);
|
||||
|
||||
#if SHADOWS_QUALITY == 1
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, 0), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, 1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, 0), sceneDepth);
|
||||
result = result * (1.0f / 4.0);
|
||||
#elif SHADOWS_QUALITY == 2 || SHADOWS_QUALITY == 3
|
||||
// TODO: implement Percentage-Closer Soft Shadows (PCSS) for Ultra quality
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, 0), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(-1, 1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(0, 1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, -1), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, 0), sceneDepth);
|
||||
result += SAMPLE_SHADOW_MAP_OFFSET(shadowMap, shadowMapUV, int2(1, 1), sceneDepth);
|
||||
result = result * (1.0f / 9.0);
|
||||
#endif
|
||||
|
||||
// Apply shadow fade and sharpness
|
||||
result = saturate((result - 0.5) * sharpness + 0.5);
|
||||
result = lerp(1.0f, result, (1 - fade) * shadow.Fade);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given directional light (cascaded shadow map sampling) for the material surface (supports subsurface shadowing)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2DArray shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
|
||||
// Samples the shadow for the given directional light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SampleDirectionalLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer)
|
||||
{
|
||||
subsurfaceShadow = 1;
|
||||
|
||||
// Create a blend factor which is one before and at the fade plane
|
||||
float viewDepth = gBuffer.ViewPos.z;
|
||||
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.NumCascades - 1] + shadow.FadeDistance) / shadow.FadeDistance);
|
||||
BRANCH
|
||||
if (fade >= 1.0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Figure out which cascade to sample from
|
||||
uint cascadeIndex = 0;
|
||||
for (uint i = 0; i < shadow.NumCascades - 1; i++)
|
||||
{
|
||||
if (viewDepth > shadow.CascadeSplits[i])
|
||||
cascadeIndex = i + 1;
|
||||
}
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
// Get subsurface material info
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(gBuffer.WorldPos, 1.0f), shadow.ShadowVP[cascadeIndex]);
|
||||
shadowPosition.xy /= shadowPosition.w;
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
|
||||
// Sample shadow map (single hardware sample with hardware filtering)
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, float3(shadowPosition.xy, cascadeIndex), 0).r;
|
||||
subsurfaceShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
|
||||
// Apply shadow fade
|
||||
subsurfaceShadow = lerp(1.0f, subsurfaceShadow, (1 - fade) * shadow.Fade);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 samplePosWS = gBuffer.WorldPos;
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, light.Direction);
|
||||
BRANCH
|
||||
if (NoL <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply normal offset bias
|
||||
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
if (NoL <= 0
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
&& !IsSubsurfaceMode(gBuffer.ShadingModel)
|
||||
#endif
|
||||
)
|
||||
return (ShadowSample)0;
|
||||
#endif
|
||||
|
||||
// Sample shadow
|
||||
return SampleShadow(light, shadow, shadowMap, samplePosWS, viewDepth);
|
||||
}
|
||||
ShadowSample result;
|
||||
result.SurfaceShadow = 1;
|
||||
result.TransmissionShadow = 1;
|
||||
|
||||
// Load shadow data
|
||||
if (light.ShadowsBufferAddress == 0)
|
||||
return result; // No shadow assigned
|
||||
ShadowData shadow = LoadShadowsBuffer(shadowsBuffer, light.ShadowsBufferAddress);
|
||||
|
||||
// Samples the shadow for the given spot light (PCF shadow map sampling)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, float3 worldPosition)
|
||||
{
|
||||
float3 toLight = light.Position - worldPosition;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
float dirCheck = 1.0f;
|
||||
#else
|
||||
float dirCheck = dot(-light.Direction, L);
|
||||
#endif
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
// Create a blend factor which is one before and at the fade plane
|
||||
float viewDepth = gBuffer.ViewPos.z;
|
||||
float fade = saturate((viewDepth - shadow.CascadeSplits[shadow.TilesCount - 1] + shadow.FadeDistance) / shadow.FadeDistance);
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius || dirCheck < 0)
|
||||
if (fade >= 1.0)
|
||||
return result;
|
||||
|
||||
// Figure out which cascade to sample from
|
||||
uint cascadeIndex = 0;
|
||||
for (uint i = 0; i < shadow.TilesCount - 1; i++)
|
||||
{
|
||||
return 1;
|
||||
if (viewDepth > shadow.CascadeSplits[i])
|
||||
cascadeIndex = i + 1;
|
||||
}
|
||||
ShadowTileData shadowTile = LoadShadowsBufferTile(shadowsBuffer, light.ShadowsBufferAddress, cascadeIndex);
|
||||
|
||||
// Negate direction and use normalized value
|
||||
toLight = -L;
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[0]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
float2 shadowMapUVs = shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f);
|
||||
|
||||
#if FilterSizeSpot == 2
|
||||
|
||||
// Use single hardware sample with filtering
|
||||
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, shadowMapUVs, shadowPosition.z);
|
||||
|
||||
#else
|
||||
|
||||
float3 sideVector = normalize(cross(toLight, float3(0, 0, 1)));
|
||||
float3 upVector = cross(sideVector, toLight);
|
||||
|
||||
float shadowMapSizeInv = 1.0f / shadow.ShadowMapSize.x;
|
||||
sideVector *= shadowMapSizeInv;
|
||||
upVector *= shadowMapSizeInv;
|
||||
|
||||
// Use PCF filter
|
||||
float result = 0;
|
||||
UNROLL
|
||||
for(int i = 0; i < FilterSizeCube; i++)
|
||||
{
|
||||
float2 samplePos = shadowMapUVs + sideVector.xy * PCFDiscSamples[i].x + upVector.xy * PCFDiscSamples[i].y;
|
||||
result += shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, samplePos, shadowPosition.z);
|
||||
}
|
||||
result *= (1.0f / FilterSizeCube);
|
||||
|
||||
float3 samplePosition = gBuffer.WorldPos;
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Apply normal offset bias
|
||||
samplePosition += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Apply shadow fade and sharpness
|
||||
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
|
||||
result = lerp(1.0f, result, shadow.Fade);
|
||||
// Project position into shadow atlas UV
|
||||
float4 shadowPosition;
|
||||
float2 shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, samplePosition, shadowPosition);
|
||||
|
||||
// Sample shadow map
|
||||
result.SurfaceShadow = SampleShadowMap(shadowMap, shadowMapUV, shadowPosition.z);
|
||||
|
||||
// Increase the sharpness for higher cascades to match the filter radius
|
||||
const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f };
|
||||
shadow.Sharpness *= SharpnessScale[cascadeIndex];
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, gBuffer.WorldPos, shadowPosition);
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, shadowMapUV, 0).r;
|
||||
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
|
||||
}
|
||||
#endif
|
||||
|
||||
result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given spot light (PCF shadow map sampling) for the material surface (supports subsurface shadowing)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, Texture2D shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
|
||||
// Samples the shadow for the given local light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SampleLocalLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer, float3 L, float toLightLength, uint tileIndex)
|
||||
{
|
||||
subsurfaceShadow = 1;
|
||||
float3 toLight = light.Position - gBuffer.WorldPos;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
#if LIGHTING_NO_DIRECTIONAL
|
||||
float dirCheck = 1.0f;
|
||||
#else
|
||||
float dirCheck = dot(-light.Direction, L);
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
BRANCH
|
||||
if (NoL <= 0
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
&& !IsSubsurfaceMode(gBuffer.ShadingModel)
|
||||
#endif
|
||||
)
|
||||
return (ShadowSample)0;
|
||||
#endif
|
||||
|
||||
ShadowSample result;
|
||||
result.SurfaceShadow = 1;
|
||||
result.TransmissionShadow = 1;
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius || dirCheck < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (toLightLength > light.Radius)
|
||||
return result;
|
||||
|
||||
// Load shadow data
|
||||
if (light.ShadowsBufferAddress == 0)
|
||||
return result; // No shadow assigned
|
||||
ShadowData shadow = LoadShadowsBuffer(shadowsBuffer, light.ShadowsBufferAddress);
|
||||
ShadowTileData shadowTile = LoadShadowsBufferTile(shadowsBuffer, light.ShadowsBufferAddress, tileIndex);
|
||||
|
||||
float3 samplePosition = gBuffer.WorldPos;
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Apply normal offset bias
|
||||
samplePosition += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Project position into shadow atlas UV
|
||||
float4 shadowPosition;
|
||||
float2 shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, samplePosition, shadowPosition);
|
||||
|
||||
// Sample shadow map
|
||||
result.SurfaceShadow = SampleShadowMap(shadowMap, shadowMapUV, shadowPosition.z);
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
// Get subsurface material info
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(gBuffer.WorldPos, 1.0f), shadow.ShadowVP[0]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
// Sample shadow map (use single hardware sample with filtering)
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, shadowPosition.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f), 0).r;
|
||||
subsurfaceShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
|
||||
// Apply shadow fade
|
||||
subsurfaceShadow = lerp(1.0f, subsurfaceShadow, shadow.Fade);
|
||||
shadowMapUV = GetLightShadowAtlasUV(shadow, shadowTile, gBuffer.WorldPos, shadowPosition);
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, shadowMapUV, 0).r;
|
||||
result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 samplePosWS = gBuffer.WorldPos;
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
BRANCH
|
||||
if (NoL <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply normal offset bias
|
||||
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Sample shadow
|
||||
return SampleShadow(light, shadow, shadowMap, samplePosWS);
|
||||
}
|
||||
|
||||
// Samples the shadow for the given point light (PCF shadow map sampling)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> shadowMap, float3 worldPosition)
|
||||
{
|
||||
float3 toLight = light.Position - worldPosition;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Negate direction and use normalized value
|
||||
toLight = -L;
|
||||
|
||||
// Figure out which cube face we're sampling from
|
||||
int cubeFaceIndex = GetCubeFaceIndex(toLight);
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(worldPosition, 1.0f), shadow.ShadowVP[cubeFaceIndex]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
|
||||
#if FilterSizeCube == 2
|
||||
|
||||
// Use single hardware sample with filtering
|
||||
float result = shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, toLight, shadowPosition.z);
|
||||
|
||||
#else
|
||||
|
||||
float3 sideVector = normalize(cross(toLight, float3(0, 0, 1)));
|
||||
float3 upVector = cross(sideVector, toLight);
|
||||
|
||||
float shadowMapSizeInv = 1.0f / shadow.ShadowMapSize.x;
|
||||
sideVector *= shadowMapSizeInv;
|
||||
upVector *= shadowMapSizeInv;
|
||||
|
||||
// Use PCF filter
|
||||
float result = 0;
|
||||
UNROLL
|
||||
for (int i = 0; i < FilterSizeCube; i++)
|
||||
{
|
||||
float3 cubeSamplePos = toLight + sideVector * PCFDiscSamples[i].x + upVector * PCFDiscSamples[i].y;
|
||||
result += shadowMap.SampleCmpLevelZero(ShadowSamplerPCF, cubeSamplePos, shadowPosition.z);
|
||||
}
|
||||
result *= (1.0f / FilterSizeCube);
|
||||
|
||||
#endif
|
||||
|
||||
// Apply shadow fade and sharpness
|
||||
result = saturate((result - 0.5) * shadow.Sharpness + 0.5);
|
||||
result = lerp(1.0f, result, shadow.Fade);
|
||||
|
||||
result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Samples the shadow for the given point light (PCF shadow map sampling) for the material surface (supports subsurface shadowing)
|
||||
float SampleShadow(LightData light, LightShadowData shadow, TextureCube<float> shadowMap, GBufferSample gBuffer, out float subsurfaceShadow)
|
||||
// Samples the shadow for the given spot light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SampleSpotLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer)
|
||||
{
|
||||
float3 toLight = light.Position - gBuffer.WorldPos;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
return SampleLocalLightShadow(light, shadowsBuffer, shadowMap, gBuffer, L, toLightLength, 0);
|
||||
}
|
||||
|
||||
// Samples the shadow for the given point light on the material surface (supports subsurface shadowing)
|
||||
ShadowSample SamplePointLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, GBufferSample gBuffer)
|
||||
{
|
||||
subsurfaceShadow = 1;
|
||||
float3 toLight = light.Position - gBuffer.WorldPos;
|
||||
float toLightLength = length(toLight);
|
||||
float3 L = toLight / toLightLength;
|
||||
|
||||
// Skip pixels outside of the light influence
|
||||
BRANCH
|
||||
if (toLightLength > light.Radius)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Negate direction and use normalized value
|
||||
toLight = -L;
|
||||
|
||||
// Figure out which cube face we're sampling from
|
||||
int cubeFaceIndex = GetCubeFaceIndex(toLight);
|
||||
uint cubeFaceIndex = GetCubeFaceIndex(-L);
|
||||
|
||||
#if defined(USE_GBUFFER_CUSTOM_DATA)
|
||||
// Subsurface shadowing
|
||||
BRANCH
|
||||
if (IsSubsurfaceMode(gBuffer.ShadingModel))
|
||||
{
|
||||
// Get subsurface material info
|
||||
float opacity = gBuffer.CustomData.a;
|
||||
return SampleLocalLightShadow(light, shadowsBuffer, shadowMap, gBuffer, L, toLightLength, cubeFaceIndex);
|
||||
}
|
||||
|
||||
// Project into shadow space
|
||||
float4 shadowPosition = mul(float4(gBuffer.WorldPos, 1.0f), shadow.ShadowVP[cubeFaceIndex]);
|
||||
shadowPosition.z -= shadow.Bias;
|
||||
shadowPosition.xyz /= shadowPosition.w;
|
||||
GBufferSample GetDummyGBufferSample(float3 worldPosition)
|
||||
{
|
||||
GBufferSample gBuffer = (GBufferSample)0;
|
||||
gBuffer.ShadingModel = SHADING_MODEL_LIT;
|
||||
gBuffer.WorldPos = worldPosition;
|
||||
return gBuffer;
|
||||
}
|
||||
|
||||
// Sample shadow map (use single hardware sample with filtering)
|
||||
float shadowMapDepth = shadowMap.SampleLevel(SamplerLinearClamp, toLight, 0).r;
|
||||
subsurfaceShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth);
|
||||
// Samples the shadow for the given directional light at custom location
|
||||
ShadowSample SampleDirectionalLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, float3 worldPosition, float viewDepth)
|
||||
{
|
||||
GBufferSample gBuffer = GetDummyGBufferSample(worldPosition);
|
||||
gBuffer.ViewPos.z = viewDepth;
|
||||
return SampleDirectionalLightShadow(light, shadowsBuffer, shadowMap, gBuffer);
|
||||
}
|
||||
|
||||
// Apply shadow fade
|
||||
subsurfaceShadow = lerp(1.0f, subsurfaceShadow, shadow.Fade);
|
||||
}
|
||||
#endif
|
||||
|
||||
float3 samplePosWS = gBuffer.WorldPos;
|
||||
// Samples the shadow for the given spot light at custom location
|
||||
ShadowSample SampleSpotLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, float3 worldPosition)
|
||||
{
|
||||
GBufferSample gBuffer = GetDummyGBufferSample(worldPosition);
|
||||
return SampleSpotLightShadow(light, shadowsBuffer, shadowMap, gBuffer);
|
||||
}
|
||||
|
||||
#if !LIGHTING_NO_DIRECTIONAL
|
||||
// Skip if surface is in a full shadow
|
||||
float NoL = dot(gBuffer.Normal, L);
|
||||
BRANCH
|
||||
if (NoL <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply normal offset bias
|
||||
samplePosWS += GetShadowPositionOffset(shadow.NormalOffsetScale, NoL, gBuffer.Normal);
|
||||
#endif
|
||||
|
||||
// Sample shadow
|
||||
return SampleShadow(light, shadow, shadowMap, samplePosWS);
|
||||
// Samples the shadow for the given point light at custom location
|
||||
ShadowSample SamplePointLightShadow(LightData light, Buffer<float4> shadowsBuffer, Texture2D<float> shadowMap, float3 worldPosition)
|
||||
{
|
||||
GBufferSample gBuffer = GetDummyGBufferSample(worldPosition);
|
||||
return SamplePointLightShadow(light, shadowsBuffer, shadowMap, gBuffer);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// "Physically Based and Unified Volumetric Rendering in Frostbite" - Sebastien Hillaire at Siggraph 2015
|
||||
|
||||
#define NO_GBUFFER_SAMPLING
|
||||
#define LIGHTING_NO_DIRECTIONAL 1
|
||||
|
||||
// Debug voxels world space positions
|
||||
#define DEBUG_VOXEL_WS_POS 0
|
||||
@@ -24,11 +25,10 @@ struct SkyLightData
|
||||
float3 MultiplyColor;
|
||||
float VolumetricScatteringIntensity;
|
||||
float3 AdditiveColor;
|
||||
float Dummt0;
|
||||
float Dummy0;
|
||||
};
|
||||
|
||||
META_CB_BEGIN(0, Data)
|
||||
|
||||
GBufferData GBuffer;
|
||||
|
||||
float3 GlobalAlbedo;
|
||||
@@ -54,14 +54,11 @@ float4x4 PrevWorldToClip;
|
||||
float4 FrameJitterOffsets[8];
|
||||
|
||||
LightData DirectionalLight;
|
||||
LightShadowData DirectionalLightShadow;
|
||||
SkyLightData SkyLight;
|
||||
DDGIData DDGI;
|
||||
|
||||
META_CB_END
|
||||
|
||||
META_CB_BEGIN(1, PerLight)
|
||||
|
||||
float2 SliceToDepth;
|
||||
int MinZ;
|
||||
float LocalLightScatteringIntensity;
|
||||
@@ -70,8 +67,6 @@ float4 ViewSpaceBoundingSphere;
|
||||
float4x4 ViewToVolumeClip;
|
||||
|
||||
LightData LocalLight;
|
||||
LightShadowData LocalLightShadow;
|
||||
|
||||
META_CB_END
|
||||
|
||||
// The Henyey-Greenstein phase function
|
||||
@@ -162,28 +157,8 @@ void GS_WriteToSlice(triangle Quad_VS2GS input[3], inout TriangleStream<Quad_GS2
|
||||
}
|
||||
|
||||
#if USE_SHADOW
|
||||
|
||||
// Shadow Maps
|
||||
TextureCube<float> ShadowMapCube : register(t5);
|
||||
Texture2D ShadowMapSpot : register(t6);
|
||||
|
||||
float ComputeVolumeShadowing(float3 worldPosition, bool isSpotLight)
|
||||
{
|
||||
float shadow = 1;
|
||||
|
||||
// TODO: use single shadowmaps atlas for whole scene (with slots) - same code path for spot and point lights
|
||||
if (isSpotLight)
|
||||
{
|
||||
shadow = SampleShadow(LocalLight, LocalLightShadow, ShadowMapSpot, worldPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
shadow = SampleShadow(LocalLight, LocalLightShadow, ShadowMapCube, worldPosition);
|
||||
}
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
Texture2D<float> ShadowMap : register(t0);
|
||||
Buffer<float4> ShadowsBuffer : register(t1);
|
||||
#endif
|
||||
|
||||
META_PS(true, FEATURE_LEVEL_SM5)
|
||||
@@ -225,15 +200,22 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0
|
||||
GetRadialLightAttenuation(LocalLight, isSpotLight, float3(0, 0, 1), distanceSqr, distanceBias * distanceBias, toLight, L, NoL, attenuation);
|
||||
|
||||
// Peek the shadow
|
||||
float shadowFactor = 1.0f;
|
||||
float shadow = 1.0f;
|
||||
#if USE_SHADOW
|
||||
if (attenuation > 0)
|
||||
{
|
||||
shadowFactor = ComputeVolumeShadowing(positionWS, isSpotLight);
|
||||
if (isSpotLight)
|
||||
{
|
||||
shadow = SampleSpotLightShadow(LocalLight, ShadowsBuffer, ShadowMap, positionWS).SurfaceShadow;
|
||||
}
|
||||
else
|
||||
{
|
||||
shadow = SamplePointLightShadow(LocalLight, ShadowsBuffer, ShadowMap, positionWS).SurfaceShadow;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * attenuation * shadowFactor * LocalLightScatteringIntensity);
|
||||
scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * attenuation * shadow * LocalLightScatteringIntensity);
|
||||
}
|
||||
|
||||
scattering.rgb /= (float)samplesCount;
|
||||
@@ -281,13 +263,14 @@ Texture3D<float4> VBufferA : register(t0);
|
||||
Texture3D<float4> VBufferB : register(t1);
|
||||
Texture3D<float4> LightScatteringHistory : register(t2);
|
||||
Texture3D<float4> LocalShadowedLightScattering : register(t3);
|
||||
Texture2DArray ShadowMapCSM : register(t4);
|
||||
Texture2D<float> ShadowMap : register(t4);
|
||||
Buffer<float4> ShadowsBuffer : register(t5);
|
||||
#if USE_DDGI
|
||||
Texture2D<snorm float4> ProbesData : register(t5);
|
||||
Texture2D<float4> ProbesDistance : register(t6);
|
||||
Texture2D<float4> ProbesIrradiance : register(t7);
|
||||
Texture2D<snorm float4> ProbesData : register(t6);
|
||||
Texture2D<float4> ProbesDistance : register(t7);
|
||||
Texture2D<float4> ProbesIrradiance : register(t8);
|
||||
#else
|
||||
TextureCube SkyLightImage : register(t5);
|
||||
TextureCube SkyLightImage : register(t6);
|
||||
#endif
|
||||
|
||||
META_CS(true, FEATURE_LEVEL_SM5)
|
||||
@@ -319,16 +302,8 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_
|
||||
float3 cameraVectorNormalized = cameraVector / cameraVectorLength;
|
||||
|
||||
// Directional light
|
||||
BRANCH
|
||||
if (DirectionalLightShadow.NumCascades < 10) // NumCascades==10 if no dir light
|
||||
{
|
||||
// Try to sample CSM shadow at the voxel position
|
||||
float shadow = 1;
|
||||
if (DirectionalLightShadow.NumCascades > 0)
|
||||
{
|
||||
shadow = SampleShadow(DirectionalLight, DirectionalLightShadow, ShadowMapCSM, positionWS, cameraVectorLength);
|
||||
}
|
||||
|
||||
{
|
||||
float shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, positionWS, cameraVectorLength).SurfaceShadow;
|
||||
lightScattering += DirectionalLight.Color * (8 * shadow * GetPhase(PhaseG, dot(DirectionalLight.Direction, cameraVectorNormalized)));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user