initial soft shadow implementation

pull/2/head
cosmonaut 2020-09-20 20:50:55 -07:00
parent 7ea2b57315
commit 7e82f2f32e
6 changed files with 126 additions and 44 deletions

View File

@ -22,6 +22,8 @@ namespace Kav
EffectParameter cascadeFarPlanesParam; EffectParameter cascadeFarPlanesParam;
EffectParameter shadowMapSizeParam;
EffectParameter lightSpaceMatrixOneParam; EffectParameter lightSpaceMatrixOneParam;
EffectParameter lightSpaceMatrixTwoParam; EffectParameter lightSpaceMatrixTwoParam;
EffectParameter lightSpaceMatrixThreeParam; EffectParameter lightSpaceMatrixThreeParam;
@ -46,6 +48,8 @@ namespace Kav
public readonly float[] CascadeFarPlanes; public readonly float[] CascadeFarPlanes;
public int ShadowMapSize { get; set; }
public Matrix LightSpaceMatrixOne { get; set; } public Matrix LightSpaceMatrixOne { get; set; }
public Matrix LightSpaceMatrixTwo { get; set; } public Matrix LightSpaceMatrixTwo { get; set; }
public Matrix LightSpaceMatrixThree { get; set; } public Matrix LightSpaceMatrixThree { get; set; }
@ -82,6 +86,8 @@ namespace Kav
CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i]; CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i];
} }
ShadowMapSize = cloneSource.ShadowMapSize;
LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne; LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne;
LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo; LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo;
LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree; LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree;
@ -113,6 +119,7 @@ namespace Kav
directionalLightColorParam.SetValue(DirectionalLightColor); directionalLightColorParam.SetValue(DirectionalLightColor);
cascadeFarPlanesParam.SetValue(CascadeFarPlanes); cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
shadowMapSizeParam.SetValue(ShadowMapSize);
lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne); lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo); lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
@ -141,6 +148,8 @@ namespace Kav
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
shadowMapSizeParam = Parameters["ShadowMapSize"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"]; lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"]; lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];

View File

@ -21,18 +21,40 @@ BEGIN_CONSTANTS
float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c3) _cb(c3); float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c3) _cb(c3);
float ShadowMapSize _ps(c7) _cb(c7);
MATRIX_CONSTANTS MATRIX_CONSTANTS
float4x4 LightSpaceMatrixOne _ps(c7) _cb(c7); float4x4 LightSpaceMatrixOne _ps(c8) _cb(c8);
float4x4 LightSpaceMatrixTwo _ps(c11) _cb(c11); float4x4 LightSpaceMatrixTwo _ps(c12) _cb(c12);
float4x4 LightSpaceMatrixThree _ps(c15) _cb(c15); float4x4 LightSpaceMatrixThree _ps(c16) _cb(c16);
float4x4 LightSpaceMatrixFour _ps(c19) _cb(c19); float4x4 LightSpaceMatrixFour _ps(c20) _cb(c20);
// used to select shadow cascade // used to select shadow cascade
float4x4 ViewMatrix _ps(c23) _cb(c23); float4x4 ViewMatrix _ps(c24) _cb(c24);
END_CONSTANTS 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 struct VertexInput
{ {
float4 Position : POSITION; float4 Position : POSITION;
@ -57,6 +79,30 @@ PixelInput main_vs(VertexInput input)
// Pixel Shader // 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 ComputeShadow(float3 positionWorldSpace, float3 N, float3 L)
{ {
float bias = 0.005 * tan(acos(dot(N, 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; projectionCoords.y *= -1;
// in XNA clip z is 0 to 1 already // in XNA clip z is 0 to 1 already
float inc = 1.0 / 1024.0; if (projectionCoords.z > 1.0)
{
return 1.0;
}
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;
float shadowFactor = 0;
for (int row = -1; row <= 1; row++)
{
for (int col = -1; col <= 1; col++)
{
float closestDepth;
if (shadowCascadeIndex == 0) if (shadowCascadeIndex == 0)
{ {
closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r; visibility = PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
} }
else if (shadowCascadeIndex == 1) else if (shadowCascadeIndex == 1)
{ {
closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r; visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
} }
else if (shadowCascadeIndex == 2) else if (shadowCascadeIndex == 2)
{ {
closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r; visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
} }
else else
{ {
closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r; visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
}
shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0;
}
} }
shadowFactor /= 9.0; return visibility;
if (projectionCoords.z > 1.0)
{
shadowFactor = 1.0;
}
return shadowFactor;
} }
float4 ComputeColor( float4 ComputeColor(
@ -159,7 +221,7 @@ float4 ComputeColor(
float3 radiance = DirectionalLightColor; float3 radiance = DirectionalLightColor;
float shadow = ComputeShadow(worldPosition, N, L); 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); return float4(color, 1.0);
} }

View File

@ -49,7 +49,7 @@ float3 ComputeLight(
float3 albedo, float3 albedo,
float metallic, float metallic,
float roughness, float roughness,
float shadow float visibility
) { ) {
float3 H = normalize(V + L); float3 H = normalize(V + L);
@ -67,5 +67,5 @@ float3 ComputeLight(
kD *= 1.0 - metallic; kD *= 1.0 - metallic;
float NdotL = max(dot(N, L), 0.0); float NdotL = max(dot(N, L), 0.0);
return (kD * albedo / PI + specular) * radiance * NdotL * shadow; return (kD * albedo / PI + specular) * radiance * NdotL * visibility;
} }

View File

@ -54,4 +54,5 @@
#define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord) #define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord)
#define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord)
#define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord) #define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord)
#define SAMPLER(Name) Name##Sampler
#endif #endif

View File

@ -7,6 +7,7 @@ namespace Kav
public class Renderer public class Renderer
{ {
private const int MAX_SHADOW_CASCADES = 4; private const int MAX_SHADOW_CASCADES = 4;
private int ShadowMapSize { get; }
private GraphicsDevice GraphicsDevice { get; } private GraphicsDevice GraphicsDevice { get; }
private int RenderDimensionsX { get; } private int RenderDimensionsX { get; }
@ -34,12 +35,19 @@ namespace Kav
private SpriteBatch SpriteBatch { get; } 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; GraphicsDevice = graphicsDevice;
RenderDimensionsX = renderDimensionsX; RenderDimensionsX = renderDimensionsX;
RenderDimensionsY = renderDimensionsY; RenderDimensionsY = renderDimensionsY;
ShadowMapSize = shadowMapSize;
NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES); NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES);
ShadowRenderTargets = new RenderTarget2D[numShadowCascades]; ShadowRenderTargets = new RenderTarget2D[numShadowCascades];
@ -47,8 +55,8 @@ namespace Kav
{ {
ShadowRenderTargets[i] = new RenderTarget2D( ShadowRenderTargets[i] = new RenderTarget2D(
GraphicsDevice, GraphicsDevice,
1024, ShadowMapSize,
1024, ShadowMapSize,
false, false,
SurfaceFormat.Single, SurfaceFormat.Single,
DepthFormat.Depth24 DepthFormat.Depth24
@ -126,6 +134,8 @@ namespace Kav
DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice); DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice); DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize;
FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly); FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly);
FullscreenTriangle.SetData(new VertexPositionTexture[3] { FullscreenTriangle.SetData(new VertexPositionTexture[3] {
new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)), new VertexPositionTexture(new Vector3(-1, -3, 0), new Vector2(0, 2)),