diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs index fb24e78..5e1f919 100644 --- a/Effects/DeferredPBR_DirectionalLightEffect.cs +++ b/Effects/DeferredPBR_DirectionalLightEffect.cs @@ -22,6 +22,8 @@ namespace Kav EffectParameter cascadeFarPlanesParam; + EffectParameter shadowMapSizeParam; + EffectParameter lightSpaceMatrixOneParam; EffectParameter lightSpaceMatrixTwoParam; EffectParameter lightSpaceMatrixThreeParam; @@ -46,6 +48,8 @@ namespace Kav public readonly float[] CascadeFarPlanes; + public int ShadowMapSize { get; set; } + public Matrix LightSpaceMatrixOne { get; set; } public Matrix LightSpaceMatrixTwo { get; set; } public Matrix LightSpaceMatrixThree { get; set; } @@ -82,6 +86,8 @@ namespace Kav CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i]; } + ShadowMapSize = cloneSource.ShadowMapSize; + LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne; LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo; LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree; @@ -113,6 +119,7 @@ namespace Kav directionalLightColorParam.SetValue(DirectionalLightColor); cascadeFarPlanesParam.SetValue(CascadeFarPlanes); + shadowMapSizeParam.SetValue(ShadowMapSize); lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); @@ -141,6 +148,8 @@ namespace Kav cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; + shadowMapSizeParam = Parameters["ShadowMapSize"]; + lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb index e47ec40..adbc9a7 100644 Binary files a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb and b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb differ diff --git a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx index 77a3860..06f3a6b 100644 --- a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx +++ b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx @@ -21,18 +21,40 @@ BEGIN_CONSTANTS float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c3) _cb(c3); + float ShadowMapSize _ps(c7) _cb(c7); + MATRIX_CONSTANTS - float4x4 LightSpaceMatrixOne _ps(c7) _cb(c7); - float4x4 LightSpaceMatrixTwo _ps(c11) _cb(c11); - float4x4 LightSpaceMatrixThree _ps(c15) _cb(c15); - float4x4 LightSpaceMatrixFour _ps(c19) _cb(c19); + float4x4 LightSpaceMatrixOne _ps(c8) _cb(c8); + float4x4 LightSpaceMatrixTwo _ps(c12) _cb(c12); + float4x4 LightSpaceMatrixThree _ps(c16) _cb(c16); + float4x4 LightSpaceMatrixFour _ps(c20) _cb(c20); // used to select shadow cascade - float4x4 ViewMatrix _ps(c23) _cb(c23); + float4x4 ViewMatrix _ps(c24) _cb(c24); END_CONSTANTS +static float2 poissonDisk[16] = +{ + float2( -0.94201624, -0.39906216 ), + float2( 0.94558609, -0.76890725 ), + float2( -0.094184101, -0.92938870 ), + float2( 0.34495938, 0.29387760 ), + float2( -0.91588581, 0.45771432 ), + float2( -0.81544232, -0.87912464 ), + float2( -0.38277543, 0.27676845 ), + float2( 0.97484398, 0.75648379 ), + float2( 0.44323325, -0.97511554 ), + float2( 0.53742981, -0.47373420 ), + float2( -0.26496911, -0.41893023 ), + float2( 0.79197514, 0.19090188 ), + float2( -0.24188840, 0.99706507 ), + float2( -0.81409955, 0.91437590 ), + float2( 0.19984126, 0.78641367 ), + float2( 0.14383161, -0.14100790 ) +}; + struct VertexInput { float4 Position : POSITION; @@ -57,6 +79,30 @@ PixelInput main_vs(VertexInput input) // Pixel Shader +// Returns a random number based on a vec3 and an int. +float random(float3 seed, int i){ + float4 seed4 = float4(seed, i); + float dot_product = dot(seed4, float4(12.9898,78.233,45.164,94.673)); + return frac(sin(dot_product) * 43758.5453); +} + +float PoissonCoord(sampler shadowMap, float3 worldPosition, float2 texCoord, float fragmentDepth, float bias) +{ + float visibility = 1.0; + + for (int i = 0; i < 16; i++) + { + int index = int(16.0 * random(floor(worldPosition * 1000.0), i)) % 16; + + if (tex2D(shadowMap, texCoord + poissonDisk[i] / 700.0).r < fragmentDepth - bias) + { + visibility -= 0.2; + } + } + + return visibility; +} + float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) { float bias = 0.005 * tan(acos(dot(N, L))); @@ -104,42 +150,58 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L) projectionCoords.y *= -1; // in XNA clip z is 0 to 1 already - float inc = 1.0 / 1024.0; - - float shadowFactor = 0; - for (int row = -1; row <= 1; row++) - { - for (int col = -1; col <= 1; col++) - { - float closestDepth; - if (shadowCascadeIndex == 0) - { - closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r; - } - else if (shadowCascadeIndex == 1) - { - closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r; - } - else if (shadowCascadeIndex == 2) - { - closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r; - } - else - { - closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r; - } - shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0; - } - } - - shadowFactor /= 9.0; - if (projectionCoords.z > 1.0) { - shadowFactor = 1.0; + return 1.0; } - return shadowFactor; + float inc = 1.0 / ShadowMapSize; // TODO: shadow map size uniform + + // PCF + Poisson soft shadows + float visibility = 0.0; + // for (int row = -1; row <= 1; row++) + // { + // for (int col = -1; col <= 1; col++) + // { + // if (shadowCascadeIndex == 0) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else if (shadowCascadeIndex == 1) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else if (shadowCascadeIndex == 2) + // { + // visibility += PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // else + // { + // visibility += PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy + float2(row, col) * inc, projectionCoords.z, bias); + // } + // } + // } + + // visibility /= 9.0; + + if (shadowCascadeIndex == 0) + { + visibility = PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else if (shadowCascadeIndex == 1) + { + visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else if (shadowCascadeIndex == 2) + { + visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + else + { + visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias); + } + + return visibility; } float4 ComputeColor( @@ -159,7 +221,7 @@ float4 ComputeColor( float3 radiance = DirectionalLightColor; float shadow = ComputeShadow(worldPosition, N, L); - float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow)); + float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow); return float4(color, 1.0); } diff --git a/Effects/HLSL/Lighting.fxh b/Effects/HLSL/Lighting.fxh index 1cc6ae8..e57b391 100644 --- a/Effects/HLSL/Lighting.fxh +++ b/Effects/HLSL/Lighting.fxh @@ -49,7 +49,7 @@ float3 ComputeLight( float3 albedo, float metallic, float roughness, - float shadow + float visibility ) { float3 H = normalize(V + L); @@ -67,5 +67,5 @@ float3 ComputeLight( kD *= 1.0 - metallic; float NdotL = max(dot(N, L), 0.0); - return (kD * albedo / PI + specular) * radiance * NdotL * shadow; + return (kD * albedo / PI + specular) * radiance * NdotL * visibility; } diff --git a/Effects/HLSL/Macros.fxh b/Effects/HLSL/Macros.fxh index 91d1702..c0aaaac 100644 --- a/Effects/HLSL/Macros.fxh +++ b/Effects/HLSL/Macros.fxh @@ -54,4 +54,5 @@ #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord) +#define SAMPLER(Name) Name##Sampler #endif diff --git a/Renderer.cs b/Renderer.cs index 82a9a0f..37eead5 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -7,6 +7,7 @@ namespace Kav public class Renderer { private const int MAX_SHADOW_CASCADES = 4; + private int ShadowMapSize { get; } private GraphicsDevice GraphicsDevice { get; } private int RenderDimensionsX { get; } @@ -34,12 +35,19 @@ namespace Kav private SpriteBatch SpriteBatch { get; } - public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY, int numShadowCascades) - { + public Renderer( + GraphicsDevice graphicsDevice, + int renderDimensionsX, + int renderDimensionsY, + int numShadowCascades, + int shadowMapSize + ) { GraphicsDevice = graphicsDevice; RenderDimensionsX = renderDimensionsX; RenderDimensionsY = renderDimensionsY; + ShadowMapSize = shadowMapSize; + NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES); ShadowRenderTargets = new RenderTarget2D[numShadowCascades]; @@ -47,8 +55,8 @@ namespace Kav { ShadowRenderTargets[i] = new RenderTarget2D( GraphicsDevice, - 1024, - 1024, + ShadowMapSize, + ShadowMapSize, false, SurfaceFormat.Single, DepthFormat.Depth24 @@ -126,6 +134,8 @@ namespace Kav DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); + DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize; + FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle.SetData(new VertexPositionTexture[3] { new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)),