initial soft shadow implementation
parent
7ea2b57315
commit
7e82f2f32e
|
@ -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"];
|
||||
|
|
Binary file not shown.
|
@ -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;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r;
|
||||
visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
|
||||
}
|
||||
else
|
||||
{
|
||||
closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r;
|
||||
}
|
||||
shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0;
|
||||
}
|
||||
visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
|
||||
}
|
||||
|
||||
shadowFactor /= 9.0;
|
||||
|
||||
if (projectionCoords.z > 1.0)
|
||||
{
|
||||
shadowFactor = 1.0;
|
||||
}
|
||||
|
||||
return shadowFactor;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
18
Renderer.cs
18
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)),
|
||||
|
|
Loading…
Reference in New Issue