toon shadows

pull/3/head
cosmonaut 2020-10-02 12:28:28 -07:00
parent 632f0a5b06
commit d370d4e2e4
6 changed files with 250 additions and 45 deletions

View File

@ -0,0 +1,14 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface ShadowCascadeEffect
{
Matrix LightSpaceMatrixOne { get; set; }
Matrix LightSpaceMatrixTwo { get; set; }
Matrix LightSpaceMatrixThree { get; set; }
Matrix LightSpaceMatrixFour { get; set; }
float[] CascadeFarPlanes { get; }
}
}

View File

@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav namespace Kav
{ {
public class DeferredPBR_DirectionalLightEffect : Effect public class DeferredPBR_DirectionalLightEffect : Effect, ShadowCascadeEffect
{ {
EffectParameter gPositionParam; EffectParameter gPositionParam;
EffectParameter gAlbedoParam; EffectParameter gAlbedoParam;
@ -46,7 +46,7 @@ namespace Kav
public Vector3 DirectionalLightDirection { get; set; } public Vector3 DirectionalLightDirection { get; set; }
public Vector3 DirectionalLightColor { get; set; } public Vector3 DirectionalLightColor { get; set; }
public readonly float[] CascadeFarPlanes; public float[] CascadeFarPlanes { get; }
public int ShadowMapSize { get; set; } public int ShadowMapSize { get; set; }
@ -147,7 +147,6 @@ namespace Kav
directionalLightColorParam = Parameters["DirectionalLightColor"]; directionalLightColorParam = Parameters["DirectionalLightColor"];
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"]; cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
shadowMapSizeParam = Parameters["ShadowMapSize"]; shadowMapSizeParam = Parameters["ShadowMapSize"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"]; lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];

View File

@ -3,30 +3,61 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav namespace Kav
{ {
public class Deferred_ToonEffect : Effect public class Deferred_ToonEffect : Effect, ShadowCascadeEffect
{ {
EffectParameter gPositionParam; EffectParameter gPositionParam;
EffectParameter gAlbedoParam; EffectParameter gAlbedoParam;
EffectParameter gNormalParam; EffectParameter gNormalParam;
EffectParameter shadowMapOneParam;
EffectParameter shadowMapTwoParam;
EffectParameter shadowMapThreeParam;
EffectParameter shadowMapFourParam;
EffectParameter eyePositionParam; EffectParameter eyePositionParam;
EffectParameter directionalLightDirectionParam; EffectParameter directionalLightDirectionParam;
EffectParameter directionalLightColorParam; EffectParameter directionalLightColorParam;
EffectParameter softnessParam; EffectParameter softnessParam;
EffectParameter cascadeFarPlanesParam;
EffectParameter shadowMapSizeParam;
EffectParameter lightSpaceMatrixOneParam;
EffectParameter lightSpaceMatrixTwoParam;
EffectParameter lightSpaceMatrixThreeParam;
EffectParameter lightSpaceMatrixFourParam;
EffectParameter viewMatrixParam;
public Texture2D GPosition { get; set; } public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; } public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; } public Texture2D GNormal { get; set; }
public Texture2D ShadowMapOne { get; set; }
public Texture2D ShadowMapTwo { get; set; }
public Texture2D ShadowMapThree { get; set; }
public Texture2D ShadowMapFour { get; set; }
public Vector3 EyePosition { get; set; } public Vector3 EyePosition { get; set; }
public Vector3 DirectionalLightDirection { get; set; } public Vector3 DirectionalLightDirection { get; set; }
public Vector3 DirectionalLightColor { get; set; } public Vector3 DirectionalLightColor { get; set; }
public float Softness { get; set; } public float Softness { get; set; }
public float[] CascadeFarPlanes { get; }
public float ShadowMapSize { get; set; }
public Matrix LightSpaceMatrixOne { get; set; }
public Matrix LightSpaceMatrixTwo { get; set; }
public Matrix LightSpaceMatrixThree { get; set; }
public Matrix LightSpaceMatrixFour { get; set; }
public Matrix ViewMatrix { get; set; }
public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect) public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect)
{ {
CascadeFarPlanes = new float[4];
CacheEffectParameters(); CacheEffectParameters();
} }
@ -36,11 +67,26 @@ namespace Kav
gAlbedoParam.SetValue(GAlbedo); gAlbedoParam.SetValue(GAlbedo);
gNormalParam.SetValue(GNormal); gNormalParam.SetValue(GNormal);
shadowMapOneParam.SetValue(ShadowMapOne);
shadowMapTwoParam.SetValue(ShadowMapTwo);
shadowMapThreeParam.SetValue(ShadowMapThree);
shadowMapFourParam.SetValue(ShadowMapFour);
eyePositionParam.SetValue(EyePosition); eyePositionParam.SetValue(EyePosition);
directionalLightDirectionParam.SetValue(DirectionalLightDirection); directionalLightDirectionParam.SetValue(DirectionalLightDirection);
directionalLightColorParam.SetValue(DirectionalLightColor); directionalLightColorParam.SetValue(DirectionalLightColor);
softnessParam.SetValue(Softness); softnessParam.SetValue(Softness);
cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
shadowMapSizeParam.SetValue(ShadowMapSize);
lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
viewMatrixParam.SetValue(ViewMatrix);
} }
void CacheEffectParameters() void CacheEffectParameters()
@ -49,11 +95,26 @@ namespace Kav
gAlbedoParam = Parameters["gAlbedo"]; gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"]; gNormalParam = Parameters["gNormal"];
shadowMapOneParam = Parameters["shadowMapOne"];
shadowMapTwoParam = Parameters["shadowMapTwo"];
shadowMapThreeParam = Parameters["shadowMapThree"];
shadowMapFourParam = Parameters["shadowMapFour"];
eyePositionParam = Parameters["EyePosition"]; eyePositionParam = Parameters["EyePosition"];
directionalLightDirectionParam = Parameters["DirectionalLightDirection"]; directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
directionalLightColorParam = Parameters["DirectionalLightColor"]; directionalLightColorParam = Parameters["DirectionalLightColor"];
softnessParam = Parameters["Softness"]; softnessParam = Parameters["Softness"];
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
shadowMapSizeParam = Parameters["ShadowMapSize"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
viewMatrixParam = Parameters["ViewMatrix"];
} }
} }
} }

BIN
Effects/FXB/Deferred_ToonEffect.fxb (Stored with Git LFS)

Binary file not shown.

View File

@ -1,17 +1,37 @@
#include "Macros.fxh" #include "Macros.fxh"
#include "Shadow.fxh"
static const int NUM_SHADOW_CASCADES = 4;
DECLARE_TEXTURE(gPosition, 0); DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1); DECLARE_TEXTURE(gAlbedo, 1);
DECLARE_TEXTURE(gNormal, 2); DECLARE_TEXTURE(gNormal, 2);
DECLARE_TEXTURE(shadowMapOne, 4);
DECLARE_TEXTURE(shadowMapTwo, 5);
DECLARE_TEXTURE(shadowMapThree, 6);
DECLARE_TEXTURE(shadowMapFour, 7);
BEGIN_CONSTANTS BEGIN_CONSTANTS
float3 EyePosition _ps(c0) _cb(c0); float3 EyePosition _ps(c0) _cb(c0);
float3 DirectionalLightDirection _ps(c1) _cb(c1); float3 DirectionalLightDirection _ps(c1) _cb(c1);
float3 DirectionalLightColor _ps(c2) _cb(c2); float3 DirectionalLightColor _ps(c2) _cb(c2);
float Softness _ps(c3) _cb(c3); float Softness _ps(c3) _cb(c3);
float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c4) _cb(c4);
float ShadowMapSize _ps(c8) _cb(c8);
MATRIX_CONSTANTS
float4x4 LightSpaceMatrixOne _ps(c9) _cb(c9);
float4x4 LightSpaceMatrixTwo _ps(c13) _cb(c13);
float4x4 LightSpaceMatrixThree _ps(c17) _cb(c17);
float4x4 LightSpaceMatrixFour _ps(c21) _cb(c21);
float4x4 ViewMatrix _ps(c25) _cb(c25);
END_CONSTANTS END_CONSTANTS
@ -37,6 +57,88 @@ PixelInput main_vs(VertexInput input)
return output; return output;
} }
float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L)
{
float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix);
int shadowCascadeIndex = 0; // 0 is closest
for (int i = 0; i < NUM_SHADOW_CASCADES; i++)
{
if (abs(positionCameraSpace.z) < CascadeFarPlanes[i])
{
shadowCascadeIndex = i;
break;
}
}
float4x4 lightSpaceMatrix;
if (shadowCascadeIndex == 0)
{
lightSpaceMatrix = LightSpaceMatrixOne;
}
else if (shadowCascadeIndex == 1)
{
lightSpaceMatrix = LightSpaceMatrixTwo;
}
else if (shadowCascadeIndex == 2)
{
lightSpaceMatrix = LightSpaceMatrixThree;
}
else
{
lightSpaceMatrix = LightSpaceMatrixFour;
}
// PCF + Poisson soft shadows
if (shadowCascadeIndex == 0)
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapOne),
ShadowMapSize
);
}
else if (shadowCascadeIndex == 1)
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapTwo),
ShadowMapSize
);
}
else if (shadowCascadeIndex == 2)
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapThree),
ShadowMapSize
);
}
else
{
return PoissonShadow(
positionWorldSpace,
N,
L,
lightSpaceMatrix,
SAMPLER(shadowMapFour),
ShadowMapSize
);
}
}
// FIXME: organize this
float4 main_ps(PixelInput input) : SV_TARGET0 float4 main_ps(PixelInput input) : SV_TARGET0
{ {
float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb; float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
@ -76,7 +178,8 @@ float4 main_ps(PixelInput input) : SV_TARGET0
rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity); rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity);
float3 rim = rimIntensity * rimColor; float3 rim = rimIntensity * rimColor;
float3 color = albedo * (light + specular + rim); float shadow = ComputeShadow(worldPosition, N, L);
float3 color = albedo * (light + specular + rim) * shadow;
return float4(color, 1.0); return float4(color, 1.0);
} }

View File

@ -195,11 +195,7 @@ namespace Kav
GraphicsDevice.Clear(Color.Black); GraphicsDevice.Clear(Color.Black);
AmbientLightRender(ambientLight); AmbientLightRender(ambientLight);
DirectionalLightToonRender(camera, modelTransforms, directionalLight);
Deferred_ToonEffect.GPosition = gPosition;
Deferred_ToonEffect.GAlbedo = gAlbedo;
Deferred_ToonEffect.GNormal = gNormal;
DirectionalLightToonRender(camera, directionalLight);
GraphicsDevice.SetRenderTarget(null); GraphicsDevice.SetRenderTarget(null);
GraphicsDevice.Clear(Color.Black); GraphicsDevice.Clear(Color.Black);
@ -298,28 +294,7 @@ namespace Kav
IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight DirectionalLight directionalLight
) { ) {
// render the individual shadow cascades RenderShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect);
var previousFarPlane = camera.NearPlane;
for (var i = 0; i < NumShadowCascades; i++)
{
var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f));
// divide the view frustum
var shadowCamera = new PerspectiveCamera(
camera.Position,
camera.Forward,
camera.Up,
camera.FieldOfView,
camera.AspectRatio,
previousFarPlane,
farPlane
);
// TODO: This is tightly coupled to the effect and it sucks
RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i);
previousFarPlane = farPlane;
}
DeferredDirectionalLightEffect.GPosition = gPosition; DeferredDirectionalLightEffect.GPosition = gPosition;
DeferredDirectionalLightEffect.GAlbedo = gAlbedo; DeferredDirectionalLightEffect.GAlbedo = gAlbedo;
@ -345,7 +320,7 @@ namespace Kav
directionalLight.Color.ToVector3() * directionalLight.Intensity; directionalLight.Color.ToVector3() * directionalLight.Intensity;
DeferredDirectionalLightEffect.ViewMatrix = camera.View; DeferredDirectionalLightEffect.ViewMatrix = camera.View;
DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation; DeferredDirectionalLightEffect.EyePosition = camera.Position;
GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.BlendState = BlendState.Additive; GraphicsDevice.BlendState = BlendState.Additive;
@ -360,11 +335,18 @@ namespace Kav
private void DirectionalLightToonRender( private void DirectionalLightToonRender(
PerspectiveCamera camera, PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight DirectionalLight directionalLight
) { ) {
RenderShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect);
GraphicsDevice.SetRenderTarget(ColorRenderTarget); GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.BlendState = BlendState.Additive; GraphicsDevice.BlendState = BlendState.Additive;
Deferred_ToonEffect.GPosition = gPosition;
Deferred_ToonEffect.GAlbedo = gAlbedo;
Deferred_ToonEffect.GNormal = gNormal;
Deferred_ToonEffect.EyePosition = camera.Position; Deferred_ToonEffect.EyePosition = camera.Position;
Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction; Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction;
Deferred_ToonEffect.DirectionalLightColor = Deferred_ToonEffect.DirectionalLightColor =
@ -372,6 +354,22 @@ namespace Kav
Deferred_ToonEffect.Softness = 0.01f; Deferred_ToonEffect.Softness = 0.01f;
Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0];
if (NumShadowCascades > 1)
{
Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1];
}
if (NumShadowCascades > 2)
{
Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2];
}
if (NumShadowCascades > 3)
{
Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3];
}
Deferred_ToonEffect.ViewMatrix = camera.View;
foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes) foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes)
{ {
pass.Apply(); pass.Apply();
@ -380,10 +378,42 @@ namespace Kav
} }
} }
private void RenderShadows(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
ShadowCascadeEffect effect
) {
// render the individual shadow cascades
var previousFarPlane = camera.NearPlane;
for (var i = 0; i < NumShadowCascades; i++)
{
var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f));
// divide the view frustum
var shadowCamera = new PerspectiveCamera(
camera.Position,
camera.Forward,
camera.Up,
camera.FieldOfView,
camera.AspectRatio,
previousFarPlane,
farPlane
);
// TODO: This is tightly coupled to the effect and it sucks
RenderShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
effect.CascadeFarPlanes[i] = farPlane;
previousFarPlane = farPlane;
}
}
private void RenderShadowMap( private void RenderShadowMap(
PerspectiveCamera camera, PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms, IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight, DirectionalLight directionalLight,
ShadowCascadeEffect effect,
int shadowCascadeIndex int shadowCascadeIndex
) { ) {
GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]); GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
@ -424,23 +454,21 @@ namespace Kav
if (shadowCascadeIndex == 0) if (shadowCascadeIndex == 0)
{ {
DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix; effect.LightSpaceMatrixOne = lightSpaceMatrix;
} }
else if (shadowCascadeIndex == 1) else if (shadowCascadeIndex == 1)
{ {
DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix; effect.LightSpaceMatrixTwo = lightSpaceMatrix;
} }
else if (shadowCascadeIndex == 2) else if (shadowCascadeIndex == 2)
{ {
DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix; effect.LightSpaceMatrixThree = lightSpaceMatrix;
} }
else if (shadowCascadeIndex == 3) else if (shadowCascadeIndex == 3)
{ {
DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix; effect.LightSpaceMatrixFour = lightSpaceMatrix;
} }
DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane;
foreach (var (model, transform) in modelTransforms) foreach (var (model, transform) in modelTransforms)
{ {
foreach (var modelMesh in model.Meshes) foreach (var modelMesh in model.Meshes)