diff --git a/EffectInterfaces/ShadowCascadeEffect.cs b/EffectInterfaces/ShadowCascadeEffect.cs
new file mode 100644
index 0000000..e55ba70
--- /dev/null
+++ b/EffectInterfaces/ShadowCascadeEffect.cs
@@ -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; }
+ }
+}
diff --git a/Effects/DeferredPBR_AmbientLightEffect.cs b/Effects/DeferredPBR_AmbientLightEffect.cs
index f1d5e31..8595426 100644
--- a/Effects/DeferredPBR_AmbientLightEffect.cs
+++ b/Effects/DeferredPBR_AmbientLightEffect.cs
@@ -1,3 +1,4 @@
+using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Kav
@@ -8,9 +9,13 @@ namespace Kav
EffectParameter gPositionParam;
EffectParameter gAlbedoParam;
+ EffectParameter ambientColorParam;
+
public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; }
+ public Vector3 AmbientColor { get; set; }
+
public DeferredPBR_AmbientLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_AmbientLightEffect)
{
CacheEffectParameters();
@@ -20,12 +25,14 @@ namespace Kav
{
gPositionParam.SetValue(GPosition);
gAlbedoParam.SetValue(GAlbedo);
+ ambientColorParam.SetValue(AmbientColor);
}
void CacheEffectParameters()
{
- gPositionParam = Parameters["gPosition"];
- gAlbedoParam = Parameters["gAlbedo"];
+ gPositionParam = Parameters["gPosition"];
+ gAlbedoParam = Parameters["gAlbedo"];
+ ambientColorParam = Parameters["AmbientLightColor"];
}
}
}
diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs
index 5e1f919..a30c1fe 100644
--- a/Effects/DeferredPBR_DirectionalLightEffect.cs
+++ b/Effects/DeferredPBR_DirectionalLightEffect.cs
@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
- public class DeferredPBR_DirectionalLightEffect : Effect
+ public class DeferredPBR_DirectionalLightEffect : Effect, ShadowCascadeEffect
{
EffectParameter gPositionParam;
EffectParameter gAlbedoParam;
@@ -46,7 +46,7 @@ namespace Kav
public Vector3 DirectionalLightDirection { get; set; }
public Vector3 DirectionalLightColor { get; set; }
- public readonly float[] CascadeFarPlanes;
+ public float[] CascadeFarPlanes { get; }
public int ShadowMapSize { get; set; }
@@ -147,7 +147,6 @@ namespace Kav
directionalLightColorParam = Parameters["DirectionalLightColor"];
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
-
shadowMapSizeParam = Parameters["ShadowMapSize"];
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
diff --git a/Effects/DeferredPBR_PointLightEffect.cs b/Effects/DeferredPBR_PointLightEffect.cs
index 6f959ea..82a73ea 100644
--- a/Effects/DeferredPBR_PointLightEffect.cs
+++ b/Effects/DeferredPBR_PointLightEffect.cs
@@ -9,22 +9,28 @@ namespace Kav
EffectParameter gAlbedoParam;
EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam;
+ EffectParameter shadowMapParam;
EffectParameter eyePositionParam;
EffectParameter pointLightColorParam;
EffectParameter pointLightPositionParam;
+ EffectParameter farPlaneParam;
+
public Texture2D GPosition { get; set; }
public Texture2D GAlbedo { get; set; }
public Texture2D GNormal { get; set; }
public Texture2D GMetallicRoughness { get; set; }
+ public TextureCube ShadowMap { get; set; }
public Vector3 EyePosition { get; set; }
public Vector3 PointLightPosition { get; set; }
public Vector3 PointLightColor { get; set; }
+ public float FarPlane { get; set; }
+
public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect)
{
CacheEffectParameters();
@@ -32,15 +38,20 @@ namespace Kav
public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource)
{
+ CacheEffectParameters();
+
GPosition = cloneSource.GPosition;
GAlbedo = cloneSource.GAlbedo;
GNormal = cloneSource.GNormal;
GMetallicRoughness = cloneSource.GMetallicRoughness;
+ ShadowMap = cloneSource.ShadowMap;
EyePosition = cloneSource.EyePosition;
PointLightPosition = cloneSource.PointLightPosition;
PointLightColor = cloneSource.PointLightColor;
+
+ FarPlane = cloneSource.FarPlane;
}
public override Effect Clone()
@@ -54,11 +65,14 @@ namespace Kav
gAlbedoParam.SetValue(GAlbedo);
gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
+ shadowMapParam.SetValue(ShadowMap);
eyePositionParam.SetValue(EyePosition);
pointLightPositionParam.SetValue(PointLightPosition);
pointLightColorParam.SetValue(PointLightColor);
+
+ farPlaneParam.SetValue(FarPlane);
}
void CacheEffectParameters()
@@ -67,11 +81,14 @@ namespace Kav
gAlbedoParam = Parameters["gAlbedo"];
gNormalParam = Parameters["gNormal"];
gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
+ shadowMapParam = Parameters["shadowMap"];
eyePositionParam = Parameters["EyePosition"];
pointLightPositionParam = Parameters["PointLightPosition"];
pointLightColorParam = Parameters["PointLightColor"];
+
+ farPlaneParam = Parameters["FarPlane"];
}
}
}
diff --git a/Effects/Deferred_ToonEffect.cs b/Effects/Deferred_ToonEffect.cs
new file mode 100644
index 0000000..031f8e7
--- /dev/null
+++ b/Effects/Deferred_ToonEffect.cs
@@ -0,0 +1,147 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class Deferred_ToonEffect : Effect, ShadowCascadeEffect
+ {
+ EffectParameter gPositionParam;
+ EffectParameter gAlbedoParam;
+ EffectParameter gNormalParam;
+ EffectParameter gMetallicRoughnessParam;
+
+ EffectParameter shadowMapOneParam;
+ EffectParameter shadowMapTwoParam;
+ EffectParameter shadowMapThreeParam;
+ EffectParameter shadowMapFourParam;
+
+ EffectParameter eyePositionParam;
+ EffectParameter directionalLightDirectionParam;
+ EffectParameter directionalLightColorParam;
+
+ EffectParameter cascadeFarPlanesParam;
+ EffectParameter shadowMapSizeParam;
+
+ EffectParameter lightSpaceMatrixOneParam;
+ EffectParameter lightSpaceMatrixTwoParam;
+ EffectParameter lightSpaceMatrixThreeParam;
+ EffectParameter lightSpaceMatrixFourParam;
+
+ EffectParameter viewMatrixParam;
+
+ EffectParameter shaderIndexParam;
+
+ public Texture2D GPosition { get; set; }
+ public Texture2D GAlbedo { get; set; }
+ public Texture2D GNormal { get; set; }
+ public Texture2D GMetallicRoughness { 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 DirectionalLightDirection { get; set; }
+ public Vector3 DirectionalLightColor { 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; }
+
+ private bool ditheredShadowValue = false;
+ public bool DitheredShadows
+ {
+ get { return ditheredShadowValue; }
+ set
+ {
+ ditheredShadowValue = value;
+ CalculateShaderIndex();
+ }
+ }
+
+ private int shaderIndex = 0;
+
+ public Deferred_ToonEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.Deferred_ToonEffect)
+ {
+ CascadeFarPlanes = new float[4];
+ CacheEffectParameters();
+ }
+
+ protected override void OnApply()
+ {
+ gPositionParam.SetValue(GPosition);
+ gAlbedoParam.SetValue(GAlbedo);
+ gNormalParam.SetValue(GNormal);
+ gMetallicRoughnessParam.SetValue(GMetallicRoughness);
+
+ shadowMapOneParam.SetValue(ShadowMapOne);
+ shadowMapTwoParam.SetValue(ShadowMapTwo);
+ shadowMapThreeParam.SetValue(ShadowMapThree);
+ shadowMapFourParam.SetValue(ShadowMapFour);
+
+ eyePositionParam.SetValue(EyePosition);
+ directionalLightDirectionParam.SetValue(DirectionalLightDirection);
+ directionalLightColorParam.SetValue(DirectionalLightColor);
+
+ cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
+ shadowMapSizeParam.SetValue(ShadowMapSize);
+
+ lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
+ lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
+ lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
+ lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
+
+ viewMatrixParam.SetValue(ViewMatrix);
+
+ shaderIndexParam.SetValue(shaderIndex);
+ }
+
+ void CacheEffectParameters()
+ {
+ gPositionParam = Parameters["gPosition"];
+ gAlbedoParam = Parameters["gAlbedo"];
+ gNormalParam = Parameters["gNormal"];
+ gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
+
+ shadowMapOneParam = Parameters["shadowMapOne"];
+ shadowMapTwoParam = Parameters["shadowMapTwo"];
+ shadowMapThreeParam = Parameters["shadowMapThree"];
+ shadowMapFourParam = Parameters["shadowMapFour"];
+
+ eyePositionParam = Parameters["EyePosition"];
+ directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
+ directionalLightColorParam = Parameters["DirectionalLightColor"];
+
+ cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
+ shadowMapSizeParam = Parameters["ShadowMapSize"];
+
+ lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
+ lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
+ lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
+ lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
+
+ viewMatrixParam = Parameters["ViewMatrix"];
+
+ shaderIndexParam = Parameters["ShaderIndex"];
+ }
+
+ private void CalculateShaderIndex()
+ {
+ if (ditheredShadowValue)
+ {
+ shaderIndex = 1;
+ }
+ else
+ {
+ shaderIndex = 0;
+ }
+ }
+ }
+}
diff --git a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb
index 62de278..a439c6a 100644
--- a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb
+++ b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0ea0cb071a1e53fec5058fae9919d376e408d33cd3de9485d42e1ebcbee85546
-size 1016
+oid sha256:244ded9c443bc10e538958075cabd14d2b066b9fd0743186ce620823380cb488
+size 1168
diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb
index 9f6cf27..1ec8b6e 100644
--- a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb
+++ b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:be44f16057328acf2bf9d2e4ff1e2d448df720711a2900daa39f5fc8c8732711
-size 21692
+oid sha256:3eb5c7dfc3e166c9baf57be77405a644fdf390aa8deb4bb7d39e044b7b3d338b
+size 21776
diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb
index 398f66b..9511784 100644
--- a/Effects/FXB/DeferredPBR_PointLightEffect.fxb
+++ b/Effects/FXB/DeferredPBR_PointLightEffect.fxb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e51395e0f0dd1e1b86272dde1e4fb5aea1fdf9b86399416015ad33cfb7260691
-size 3108
+oid sha256:033598dc2f22c2766a8b0d46215e5a9764b5445a520d67bc68f1566dbdd15035
+size 3740
diff --git a/Effects/FXB/Deferred_ToonEffect.fxb b/Effects/FXB/Deferred_ToonEffect.fxb
new file mode 100644
index 0000000..529d086
--- /dev/null
+++ b/Effects/FXB/Deferred_ToonEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78552dce830d09b83694863355e51d4b2eaa31fafb11a16a207f8b42a6d004e5
+size 10964
diff --git a/Effects/FXB/LinearDepthEffect.fxb b/Effects/FXB/LinearDepthEffect.fxb
new file mode 100644
index 0000000..c92681f
--- /dev/null
+++ b/Effects/FXB/LinearDepthEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e91546d1db9b0bb2eb93ab2dcdeaaa260c5eb08d9acf6460c091f8fdf4c88ee
+size 1344
diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb
index c60bf15..8281157 100644
--- a/Effects/FXB/SimpleDepthEffect.fxb
+++ b/Effects/FXB/SimpleDepthEffect.fxb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b7e52830f9c3e89d230f12b98bc42cbc3af7a488c584c9ff48cd571115d57d85
-size 832
+oid sha256:e0984ae92245afe3e2bda5fab0135293b5121c3ee7b5f146e7d4ddd1684ee74b
+size 848
diff --git a/Effects/FXB/SkyboxEffect.fxb b/Effects/FXB/SkyboxEffect.fxb
new file mode 100644
index 0000000..10d5b22
--- /dev/null
+++ b/Effects/FXB/SkyboxEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:847ffdb1472a2c6ca32b7bbf0f41e2b972d2435d8fe4d72e82d97bc8c88b4b59
+size 1072
diff --git a/Effects/HLSL/Conversion.fxh b/Effects/HLSL/Conversion.fxh
new file mode 100644
index 0000000..3d828bc
--- /dev/null
+++ b/Effects/HLSL/Conversion.fxh
@@ -0,0 +1,34 @@
+float3 HUEtoRGB(in float H)
+{
+ float R = abs(H * 6 - 3) - 1;
+ float G = 2 - abs(H * 6 - 2);
+ float B = 2 - abs(H * 6 - 4);
+ return saturate(float3(R,G,B));
+}
+
+float Epsilon = 1e-10;
+
+float3 RGBtoHCV(in float3 RGB)
+{
+ // Based on work by Sam Hocevar and Emil Persson
+ float4 P = (RGB.g < RGB.b) ? float4(RGB.bg, -1.0, 2.0/3.0) : float4(RGB.gb, 0.0, -1.0/3.0);
+ float4 Q = (RGB.r < P.x) ? float4(P.xyw, RGB.r) : float4(RGB.r, P.yzx);
+ float C = Q.x - min(Q.w, Q.y);
+ float H = abs((Q.w - Q.y) / (6 * C + Epsilon) + Q.z);
+ return float3(H, C, Q.x);
+}
+
+float3 RGBtoHSL(float3 RGB)
+{
+ float3 HCV = RGBtoHCV(RGB);
+ float L = HCV.z - HCV.y * 0.5;
+ float S = HCV.y / (1 - abs(L * 2 - 1) + Epsilon);
+ return float3(HCV.x, S, L);
+}
+
+float3 HSLtoRGB(float3 HSL)
+{
+ float3 RGB = HUEtoRGB(HSL.x);
+ float C = (1 - abs(2 * HSL.z - 1)) * HSL.y;
+ return (RGB - 0.5) * C + HSL.z;
+}
diff --git a/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx
index 4144c8d..6c6e173 100644
--- a/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx
+++ b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx
@@ -3,6 +3,12 @@
DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1);
+BEGIN_CONSTANTS
+
+float3 AmbientLightColor _ps(c0) _cb(c0);
+
+END_CONSTANTS
+
struct VertexInput
{
float4 Position : POSITION;
@@ -29,7 +35,7 @@ float4 ComputeColor(
float3 worldPosition,
float3 albedo
) {
- float3 color = float3(0.03, 0.03, 0.03) * albedo;
+ float3 color = AmbientLightColor * albedo;
return float4(color, 1.0);
}
diff --git a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx
index 0982521..de683cd 100644
--- a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx
+++ b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx
@@ -1,5 +1,6 @@
#include "Macros.fxh" //from FNA
#include "Lighting.fxh"
+#include "Shadow.fxh"
static const int NUM_SHADOW_CASCADES = 4;
@@ -35,26 +36,6 @@ MATRIX_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
{
float4 Position : POSITION;
@@ -79,35 +60,8 @@ 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[index] / 1024.0).r < fragmentDepth - bias)
- {
- visibility -= 0.05;
- }
- }
-
- return visibility;
-}
-
float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L)
{
- float bias = 0.005 * tan(acos(dot(N, L)));
- bias = clamp(bias, 0, 0.01);
-
float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix);
int shadowCascadeIndex = 0; // 0 is closest
@@ -139,69 +93,52 @@ float ComputeShadow(float3 positionWorldSpace, float3 N, float3 L)
lightSpaceMatrix = LightSpaceMatrixFour;
}
- float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix);
-
- // maps to [-1, 1]
- float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
-
- // maps to [0, 1]
- projectionCoords.x = (projectionCoords.x * 0.5) + 0.5;
- projectionCoords.y = (projectionCoords.y * 0.5) + 0.5;
- projectionCoords.y *= -1;
- // in XNA clip z is 0 to 1 already
-
- 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;
if (shadowCascadeIndex == 0)
{
- visibility = PoissonCoord(SAMPLER(shadowMapOne), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ return PoissonShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapOne),
+ ShadowMapSize
+ );
}
else if (shadowCascadeIndex == 1)
{
- visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ return PoissonShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapTwo),
+ ShadowMapSize
+ );
}
else if (shadowCascadeIndex == 2)
{
- visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ return PoissonShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapThree),
+ ShadowMapSize
+ );
}
else
{
- visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ return PoissonShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapFour),
+ ShadowMapSize
+ );
}
-
- return visibility;
}
float4 ComputeColor(
diff --git a/Effects/HLSL/DeferredPBR_PointLightEffect.fx b/Effects/HLSL/DeferredPBR_PointLightEffect.fx
index fba3c6e..d6e1933 100644
--- a/Effects/HLSL/DeferredPBR_PointLightEffect.fx
+++ b/Effects/HLSL/DeferredPBR_PointLightEffect.fx
@@ -1,10 +1,12 @@
#include "Macros.fxh" //from FNA
#include "Lighting.fxh"
+#include "Shadow.fxh"
DECLARE_TEXTURE(gPosition, 0);
DECLARE_TEXTURE(gAlbedo, 1);
DECLARE_TEXTURE(gNormal, 2);
DECLARE_TEXTURE(gMetallicRoughness, 3);
+DECLARE_CUBEMAP(shadowMap, 4);
BEGIN_CONSTANTS
@@ -13,6 +15,8 @@ BEGIN_CONSTANTS
float3 PointLightPosition _ps(c1) _cb(c1);
float3 PointLightColor _ps(c2) _cb(c2);
+ float FarPlane _ps(c3) _cb(c3);
+
MATRIX_CONSTANTS
END_CONSTANTS
@@ -60,9 +64,11 @@ float4 ComputeColor(
float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColor * attenuation;
- float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
+ float shadow = HardPointShadow(worldPosition, N, L, PointLightPosition, SAMPLER(shadowMap), FarPlane);
+ float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow);
return float4(color, 1.0);
+ //return float4(shadow, shadow, shadow, 1.0);
}
float4 main_ps(PixelInput input) : SV_TARGET0
diff --git a/Effects/HLSL/Deferred_ToonEffect.fx b/Effects/HLSL/Deferred_ToonEffect.fx
new file mode 100644
index 0000000..62fbcad
--- /dev/null
+++ b/Effects/HLSL/Deferred_ToonEffect.fx
@@ -0,0 +1,285 @@
+#include "Macros.fxh"
+#include "Shadow.fxh"
+#include "Dither.fxh"
+
+static const int NUM_SHADOW_CASCADES = 4;
+
+DECLARE_TEXTURE(gPosition, 0);
+DECLARE_TEXTURE(gAlbedo, 1);
+DECLARE_TEXTURE(gNormal, 2);
+DECLARE_TEXTURE(gMetallicRoughness, 3);
+DECLARE_TEXTURE(shadowMapOne, 4);
+DECLARE_TEXTURE(shadowMapTwo, 5);
+DECLARE_TEXTURE(shadowMapThree, 6);
+DECLARE_TEXTURE(shadowMapFour, 7);
+
+BEGIN_CONSTANTS
+
+float3 EyePosition _ps(c0) _cb(c0);
+
+float3 DirectionalLightDirection _ps(c1) _cb(c1);
+float3 DirectionalLightColor _ps(c2) _cb(c2);
+
+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
+
+struct VertexInput
+{
+ float4 Position : POSITION;
+ float2 TexCoord : TEXCOORD;
+};
+
+struct PixelInput
+{
+ float4 Position : SV_POSITION;
+ float2 TexCoord : TEXCOORD0;
+};
+
+PixelInput main_vs(VertexInput input)
+{
+ PixelInput output;
+
+ output.Position = input.Position;
+ output.TexCoord = input.TexCoord;
+
+ 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;
+ }
+
+ if (shadowCascadeIndex == 0)
+ {
+ return HardShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapOne),
+ ShadowMapSize
+ );
+ }
+ else if (shadowCascadeIndex == 1)
+ {
+ return HardShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapTwo),
+ ShadowMapSize
+ );
+ }
+ else if (shadowCascadeIndex == 2)
+ {
+ return HardShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapThree),
+ ShadowMapSize
+ );
+ }
+ else
+ {
+ return HardShadow(
+ positionWorldSpace,
+ N,
+ L,
+ lightSpaceMatrix,
+ SAMPLER(shadowMapFour),
+ ShadowMapSize
+ );
+ }
+}
+
+float IntensityBanding(float NdotL)
+{
+ // if (NdotL > 0.5)
+ // {
+ // return 1.0;
+ // }
+ // else if (NdotL > 0.25)
+ // {
+ // return 0.5;
+ // }
+ // else if (NdotL > 0.0)
+ // {
+ // return 0.25;
+ // }
+ // else
+ // {
+ // return 0.0;
+ // }
+ if (NdotL > 0)
+ {
+ return 1.0;
+ }
+ else
+ {
+ return 0.25;
+ }
+}
+
+float4 FlatShadow(PixelInput input) : SV_TARGET0
+{
+ float2 screenPosition = input.Position.xy;
+ float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
+ float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz;
+ float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
+ float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg;
+
+ // the lower the glossiness, the sharper the specular highlight
+ float glossiness = lerp(64, 16, 1.0 - metallicRoughness.r);
+
+ float3 V = normalize(EyePosition - worldPosition);
+ float3 L = normalize(DirectionalLightDirection);
+ float3 N = normalize(normal);
+ float3 H = normalize(V + L);
+
+ float NdotL = dot(N, L);
+ float NdotH = max(dot(N, H), 0.0);
+
+ float lightIntensity = IntensityBanding(NdotL);
+ float3 light = lightIntensity * DirectionalLightColor;
+
+ float specularIntensity = pow(NdotH * lightIntensity, glossiness * glossiness);
+ float specularSmooth = smoothstep(0.005, 0.01, specularIntensity);
+
+ float3 specular = specularSmooth * float3(1.0, 1.0, 1.0);
+
+ if (metallicRoughness.r == 0.0) { specular = float3(0.0, 0.0, 0.0); }
+
+ float3 rimColor = float3(1.0, 1.0, 1.0);
+ float rimThreshold = 0.1;
+ float rimAmount = 1 - metallicRoughness.g;
+ float rimDot = 1 - dot(V, N);
+ float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold);
+ rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity);
+ float3 rim = rimIntensity * rimColor;
+
+ float shadow = ComputeShadow(worldPosition, N, L);
+ float3 color = albedo * (light + specular + rim) * shadow;
+
+ return float4(color, 1.0);
+}
+
+// FIXME: organize this
+float4 DitheredShadow(PixelInput input) : SV_TARGET0
+{
+ float2 screenPosition = input.Position.xy;
+ float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
+ float3 normal = SAMPLE_TEXTURE(gNormal, input.TexCoord).xyz;
+ float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
+ float2 metallicRoughness = SAMPLE_TEXTURE(gMetallicRoughness, input.TexCoord).rg;
+
+ // the lower the glossiness, the sharper the specular highlight
+ float glossiness = lerp(64, 16, 1.0 - metallicRoughness.r);
+
+ float3 V = normalize(EyePosition - worldPosition);
+ float3 L = normalize(DirectionalLightDirection);
+ float3 N = normalize(normal);
+ float3 H = normalize(V + L);
+
+ float NdotL = dot(N, L);
+ float NdotH = max(dot(N, H), 0.0);
+
+ float lightIntensity = IntensityBanding(NdotL);
+ //float3 light = lightIntensity * DirectionalLightColor;
+ float3 light = DirectionalLightColor;
+
+ if (lightIntensity < 1)
+ {
+ light *= dither(lightIntensity, screenPosition);
+ }
+
+ float specularIntensity = pow(NdotH * lightIntensity, glossiness * glossiness);
+ float specularSmooth = smoothstep(0.005, 0.01, specularIntensity);
+
+ float3 specular = specularSmooth * float3(1.0, 1.0, 1.0);
+
+ if (metallicRoughness.r == 0.0) { specular = float3(0.0, 0.0, 0.0); }
+
+ float3 rimColor = float3(1.0, 1.0, 1.0);
+ float rimThreshold = 0.1;
+ float rimAmount = 1 - metallicRoughness.g;
+ float rimDot = 1 - dot(V, N);
+ float rimIntensity = rimDot * pow(max(NdotL, 0.0), rimThreshold);
+ rimIntensity = smoothstep(rimAmount - 0.01, rimAmount + 0.01, rimIntensity);
+ float3 rim = rimIntensity * rimColor;
+
+ float shadow = ComputeShadow(worldPosition, N, L);
+ float3 color = albedo * (light + specular + rim); // * shadow;
+
+ if (shadow < 1)
+ {
+ color *= dither(shadow, screenPosition);
+ }
+
+ return float4(color, 1.0);
+}
+
+PixelShader PSArray[2] =
+{
+ compile ps_3_0 FlatShadow(),
+ compile ps_3_0 DitheredShadow()
+};
+
+int PSIndices[2] =
+{
+ 0, 1
+};
+
+int ShaderIndex = 0;
+
+Technique Deferred_Toon
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 main_vs();
+ PixelShader = (PSArray[PSIndices[ShaderIndex]]);
+ }
+}
diff --git a/Effects/HLSL/Dither.fxh b/Effects/HLSL/Dither.fxh
new file mode 100644
index 0000000..e0cf119
--- /dev/null
+++ b/Effects/HLSL/Dither.fxh
@@ -0,0 +1,89 @@
+#include "Conversion.fxh"
+
+// technique from http://alex-charlton.com/posts/Dithering_on_the_GPU/
+
+uniform float3 palette[8];
+static const int paletteSize = 8;
+
+static const int indexMatrix4x4[16] =
+{
+ 0, 8, 2, 10,
+ 12, 4, 14, 6,
+ 3, 11, 1, 9,
+ 15, 7, 13, 5
+};
+
+float indexValue(float2 screenCoords)
+{
+ int x = int(screenCoords.x % 4);
+ int y = int(screenCoords.y % 4);
+
+ return indexMatrix4x4[(x + y * 4)] / 16.0;
+}
+
+float hueDistance(float h1, float h2)
+{
+ float diff = abs(h1 - h2);
+ return min(abs(1.0 - diff), diff);
+}
+
+void closestColors(float hue, out float3 ret[2])
+{
+ float3 closest = float3(-2, 0, 0);
+ float3 secondClosest = float3(-2, 0, 0);
+ float3 temp;
+
+ for (int i = 0; i < paletteSize; i++)
+ {
+ temp = palette[i];
+ float tempDistance = hueDistance(temp.x, hue);
+ if (tempDistance < hueDistance(closest.x, hue))
+ {
+ secondClosest = closest;
+ closest = temp;
+ }
+ else
+ {
+ if (tempDistance < hueDistance(secondClosest.x, hue))
+ {
+ secondClosest = temp;
+ }
+ }
+ }
+
+ ret[0] = closest;
+ ret[1] = secondClosest;
+}
+
+float3 dither(float3 color, float2 screenCoords)
+{
+ float3 colors[2];
+ float3 hsl = RGBtoHSL(color);
+ closestColors(hsl.x, colors);
+ float3 closestColor = colors[0];
+ float3 secondClosestColor = colors[1];
+ float d = indexValue(screenCoords);
+ float hueDiff = hueDistance(hsl.x, closestColor.x) / hueDistance(secondClosestColor.x, secondClosestColor.x);
+ return HSLtoRGB(hueDiff < d ? closestColor : secondClosestColor);
+}
+
+// brightColor refers to undithered max color
+// float3 dither(float3 color, float3 brightColor, float2 screenCoords)
+// {
+// float brightHue = RGBtoHSL(brightColor.x);
+// float colorHue = RGBtoHSL(color.x);
+// float halfDistance = hueDistance(0.0, brightHue) / 2.0;
+// float3 closestColor = (colorHue < halfDistance) ? float3(0.0, 0.0, 0.0) : brightColor;
+// float3 secondClosestColor = brightColor - closestColor;
+// float d = indexValue(screenCoords);
+// float distance = abs(closestColor - color);
+// return (distance < d) ? closestColor : secondClosestColor;
+// }
+
+float3 dither(float color, float2 screenCoords) {
+ float closestColor = (color < 0.5) ? 0 : 1;
+ float secondClosestColor = 1 - closestColor;
+ float d = indexValue(screenCoords);
+ float distance = abs(closestColor - color);
+ return (distance < d) ? closestColor : secondClosestColor;
+}
diff --git a/Effects/HLSL/LinearDepthEffect.fx b/Effects/HLSL/LinearDepthEffect.fx
new file mode 100644
index 0000000..67db0e2
--- /dev/null
+++ b/Effects/HLSL/LinearDepthEffect.fx
@@ -0,0 +1,47 @@
+#include "Macros.fxh"
+
+BEGIN_CONSTANTS
+
+ float4x4 Model _vs(c0) _cb(c0);
+ float4x4 ModelViewProjection _vs(c4) _cb(c4);
+
+ float3 LightPosition _ps(c0) _cb(c8);
+ float FarPlane _ps(c1) _cb(c9);
+
+END_CONSTANTS
+
+struct VertexShaderInput
+{
+ float4 Position : POSITION;
+};
+
+struct VertexShaderOutput
+{
+ float4 Position : SV_Position;
+ float3 PositionWorld : TEXCOORD0;
+};
+
+VertexShaderOutput main_vs(VertexShaderInput input)
+{
+ VertexShaderOutput output;
+ output.Position = mul(input.Position, ModelViewProjection);
+ output.Position.x *= -1; // otherwise cube map render will be horizontally flipped
+ output.PositionWorld = mul(input.Position, Model);
+ return output;
+}
+
+float4 main_ps(VertexShaderOutput input) : SV_TARGET0
+{
+ float lightDistance = length(input.PositionWorld - LightPosition);
+ lightDistance /= FarPlane;
+ return float4(lightDistance, 0.0, 0.0, 0.0);
+}
+
+Technique SimpleDepth
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 main_vs();
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/HLSL/Shadow.fxh b/Effects/HLSL/Shadow.fxh
new file mode 100644
index 0000000..5f8351f
--- /dev/null
+++ b/Effects/HLSL/Shadow.fxh
@@ -0,0 +1,141 @@
+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 )
+};
+
+// TODO: this should probably sample a noise texture instead
+// 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[index] / 1024.0).r < fragmentDepth - bias)
+ {
+ visibility -= 0.05;
+ }
+ }
+
+ return visibility;
+}
+
+float PoissonShadow(
+ float3 positionWorldSpace,
+ float3 N,
+ float3 L,
+ float4x4 lightSpaceMatrix,
+ sampler shadowMap,
+ int shadowMapSize
+) {
+ float bias = 0.005 * tan(acos(dot(N, L)));
+ bias = clamp(bias, 0, 0.01);
+
+ float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix);
+
+ // maps to [-1, 1]
+ float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
+
+ // maps to [0, 1]
+ // NOTE: In XNA, clip space z is [0, 1] range
+ projectionCoords.x = (projectionCoords.x * 0.5) + 0.5;
+ projectionCoords.y = (projectionCoords.y * 0.5) + 0.5;
+ projectionCoords.y *= -1;
+
+ if (projectionCoords.z > 1.0)
+ {
+ return 1.0;
+ }
+
+ float inc = 1.0 / shadowMapSize;
+
+ // Poisson soft shadows
+ float visibility = 0.0;
+
+ visibility = PoissonCoord(
+ shadowMap,
+ positionWorldSpace,
+ projectionCoords.xy,
+ projectionCoords.z,
+ bias
+ );
+
+ return visibility;
+}
+
+float HardShadow(
+ float3 positionWorldSpace,
+ float3 N,
+ float3 L,
+ float4x4 lightSpaceMatrix,
+ sampler shadowMap,
+ int shadowMapSize
+) {
+ // float bias = 0.005 * tan(acos(dot(N, L)));
+ // bias = clamp(bias, 0, 0.01);
+
+ float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
+
+ float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix);
+
+ // maps to [-1, 1]
+ float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
+
+ // maps to [0, 1]
+ // NOTE: In XNA, clip space z is [0, 1] range
+ projectionCoords.x = (projectionCoords.x * 0.5) + 0.5;
+ projectionCoords.y = (projectionCoords.y * 0.5) + 0.5;
+ projectionCoords.y *= -1;
+
+ if (projectionCoords.z > 1.0)
+ {
+ return 1.0;
+ }
+
+ float closestDepth = tex2D(shadowMap, projectionCoords.xy);
+ float currentDepth = projectionCoords.z;
+
+ return (currentDepth - bias > closestDepth ? 0.25 : 1.0);
+}
+
+float HardPointShadow(
+ float3 positionWorldSpace,
+ float3 N,
+ float3 L,
+ float3 lightPosition,
+ sampler shadowMap,
+ float farPlane
+) {
+ float3 lightToFrag = positionWorldSpace - lightPosition;
+ float closestDepth = texCUBE(shadowMap, lightToFrag).r;
+ closestDepth *= farPlane;
+
+ float currentDepth = length(lightToFrag);
+
+ float bias = max(0.05 * (1.0 - dot(N, L)), 0.005);
+
+ return (currentDepth - bias > closestDepth ? 0 : 1.0);
+}
diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx
index 3598eb5..f813bd7 100644
--- a/Effects/HLSL/SimpleDepthEffect.fx
+++ b/Effects/HLSL/SimpleDepthEffect.fx
@@ -24,7 +24,7 @@ VertexShaderOutput main_vs(VertexShaderInput input)
VertexShaderOutput output;
output.Position = mul(input.Position, ModelViewProjection);
- output.Depth = output.Position.z;
+ output.Depth = output.Position.z / output.Position.w;
return output;
}
diff --git a/Effects/HLSL/SkyboxEffect.fx b/Effects/HLSL/SkyboxEffect.fx
new file mode 100644
index 0000000..88ac7f3
--- /dev/null
+++ b/Effects/HLSL/SkyboxEffect.fx
@@ -0,0 +1,45 @@
+#include "Macros.fxh"
+
+DECLARE_CUBEMAP(skybox, 0);
+
+BEGIN_CONSTANTS
+
+ float4x4 ViewProjection _vs(c0) _cb(c0);
+
+END_CONSTANTS
+
+struct VertexShaderInput
+{
+ float3 Position : POSITION;
+};
+
+struct VertexShaderOutput
+{
+ float4 Position : SV_POSITION;
+ float3 TexCoord : TEXCOORD;
+};
+
+VertexShaderOutput main_vs(VertexShaderInput input)
+{
+ VertexShaderOutput output;
+
+ output.Position = mul(float4(input.Position, 1.0), ViewProjection);
+ output.Position = output.Position.xyww;
+ output.TexCoord = input.Position;
+
+ return output;
+}
+
+float4 main_ps(VertexShaderOutput input) : SV_TARGET0
+{
+ return SAMPLE_CUBEMAP(skybox, input.TexCoord);
+}
+
+Technique Skybox
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 main_vs();
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/LinearDepthEffect.cs b/Effects/LinearDepthEffect.cs
new file mode 100644
index 0000000..2a6a76a
--- /dev/null
+++ b/Effects/LinearDepthEffect.cs
@@ -0,0 +1,90 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class LinearDepthEffect : Effect
+ {
+ EffectParameter modelParam;
+ EffectParameter modelViewProjectionParam;
+ EffectParameter lightPositionParam;
+ EffectParameter farPlaneParam;
+
+ EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
+
+ Matrix model;
+ Matrix view;
+ Matrix projection;
+
+ public Vector3 LightPosition { get; set; }
+ public float FarPlane { get; set; }
+
+ public Matrix Model
+ {
+ get { return model; }
+ set
+ {
+ model = value;
+ dirtyFlags |= EffectDirtyFlags.World;
+ dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ }
+ }
+
+ public Matrix View
+ {
+ get { return view; }
+ set
+ {
+ view = value;
+ dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ }
+ }
+
+ public Matrix Projection
+ {
+ get { return projection; }
+ set
+ {
+ projection = value;
+ dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ }
+ }
+
+ public LinearDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.LinearDepthEffect)
+ {
+ CacheEffectParameters();
+ }
+
+ protected override void OnApply()
+ {
+ if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
+ {
+ Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
+ Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj);
+
+ modelViewProjectionParam.SetValue(worldViewProj);
+
+ dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
+ }
+
+ if ((dirtyFlags & EffectDirtyFlags.World) != 0)
+ {
+ modelParam.SetValue(model);
+
+ dirtyFlags &= ~EffectDirtyFlags.World;
+ }
+
+ lightPositionParam.SetValue(LightPosition);
+ farPlaneParam.SetValue(FarPlane);
+ }
+
+ private void CacheEffectParameters()
+ {
+ modelParam = Parameters["Model"];
+ modelViewProjectionParam = Parameters["ModelViewProjection"];
+
+ lightPositionParam = Parameters["LightPosition"];
+ farPlaneParam = Parameters["FarPlane"];
+ }
+ }
+}
diff --git a/Effects/SkyboxEffect.cs b/Effects/SkyboxEffect.cs
new file mode 100644
index 0000000..b4f4427
--- /dev/null
+++ b/Effects/SkyboxEffect.cs
@@ -0,0 +1,77 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class SkyboxEffect : Effect
+ {
+ EffectParameter viewProjectionParam;
+ EffectParameter skyboxParam;
+
+ Matrix view;
+ Matrix projection;
+ TextureCube skybox;
+
+ EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
+
+ public Matrix View
+ {
+ get { return view; }
+ set
+ {
+ view = value;
+ dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ }
+ }
+
+ public Matrix Projection
+ {
+ get { return projection; }
+ set
+ {
+ projection = value;
+ dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ }
+ }
+
+ public TextureCube Skybox
+ {
+ get { return skybox; }
+ set
+ {
+ skybox = value;
+ dirtyFlags |= EffectDirtyFlags.World; // hack flag but whatever
+ }
+ }
+
+ public SkyboxEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SkyboxEffect)
+ {
+ CacheEffectParameters();
+ }
+
+ protected override void OnApply()
+ {
+ if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
+ {
+ Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
+
+ viewProjectionParam.SetValue(viewProjection);
+
+ dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
+ }
+
+ if ((dirtyFlags & EffectDirtyFlags.World) != 0)
+ {
+ skyboxParam.SetValue(skybox);
+
+ dirtyFlags &= ~EffectDirtyFlags.World;
+ }
+ }
+
+ private void CacheEffectParameters()
+ {
+ viewProjectionParam = Parameters["ViewProjection"];
+ skyboxParam = Parameters["skybox"];
+ }
+ }
+}
diff --git a/Geometry/MeshPart.cs b/Geometry/MeshPart.cs
index e97b49c..248fb1a 100644
--- a/Geometry/MeshPart.cs
+++ b/Geometry/MeshPart.cs
@@ -9,15 +9,47 @@ namespace Kav
public VertexBuffer VertexBuffer { get; }
public Triangle[] Triangles { get; }
public Vector3[] Positions { get; }
- public Effect Effect { get; }
+
+ private Texture2D albedoTexture = null;
+ private Texture2D normalTexture = null;
+ private Texture2D metallicRoughnessTexture = null;
- public MeshPart(VertexBuffer vertexBuffer, IndexBuffer indexBuffer, Vector3[] positions, Triangle[] triangles, Effect effect)
- {
+ public Texture2D AlbedoTexture
+ {
+ get { return DisableAlbedoMap ? null : albedoTexture; }
+ set { albedoTexture = value; }
+ }
+
+ public Texture2D NormalTexture
+ {
+ get { return DisableNormalMap ? null : normalTexture; }
+ set { normalTexture = value; }
+ }
+
+ public Texture2D MetallicRoughnessTexture
+ {
+ get { return DisableMetallicRoughnessMap ? null : metallicRoughnessTexture; }
+ set { metallicRoughnessTexture = value; }
+ }
+
+ public Vector3 Albedo { get; set; } = Vector3.One;
+ public float Metallic { get; set; } = 0.5f;
+ public float Roughness { get; set; } = 0.5f;
+
+ public bool DisableAlbedoMap { get; set; } = false;
+ public bool DisableNormalMap { get; set; } = false;
+ public bool DisableMetallicRoughnessMap { get; set; } = false;
+
+ public MeshPart(
+ VertexBuffer vertexBuffer,
+ IndexBuffer indexBuffer,
+ Vector3[] positions,
+ Triangle[] triangles
+ ) {
VertexBuffer = vertexBuffer;
IndexBuffer = indexBuffer;
Positions = positions;
Triangles = triangles;
- Effect = effect;
}
}
}
diff --git a/Geometry/Model.cs b/Geometry/Model.cs
index 1d83410..9ab1dc0 100644
--- a/Geometry/Model.cs
+++ b/Geometry/Model.cs
@@ -6,9 +6,84 @@ namespace Kav
{
public Mesh[] Meshes { get; }
+ public Color Albedo
+ {
+ set
+ {
+ foreach (var mesh in Meshes)
+ {
+ foreach (var meshPart in mesh.MeshParts)
+ {
+ meshPart.Albedo = value.ToVector3();
+ }
+ }
+ }
+ }
+
+ public float Metallic
+ {
+ set
+ {
+ foreach (var mesh in Meshes)
+ {
+ foreach (var meshPart in mesh.MeshParts)
+ {
+ meshPart.Metallic = value;
+ }
+ }
+ }
+ }
+
+ public float Roughness
+ {
+ set
+ {
+ foreach (var mesh in Meshes)
+ {
+ foreach (var meshPart in mesh.MeshParts)
+ {
+ meshPart.Roughness = value;
+ }
+ }
+ }
+ }
+
public Model(Mesh[] meshes)
{
Meshes = meshes;
}
+
+ public void DisableAlbedoMaps()
+ {
+ foreach (var mesh in Meshes)
+ {
+ foreach (var meshPart in mesh.MeshParts)
+ {
+ meshPart.DisableAlbedoMap = true;
+ }
+ }
+ }
+
+ public void DisableNormalMaps()
+ {
+ foreach (var mesh in Meshes)
+ {
+ foreach (var meshPart in mesh.MeshParts)
+ {
+ meshPart.DisableNormalMap = true;
+ }
+ }
+ }
+
+ public void DisableMetallicRoughnessMaps()
+ {
+ foreach (var mesh in Meshes)
+ {
+ foreach (var meshPart in mesh.MeshParts)
+ {
+ meshPart.DisableMetallicRoughnessMap = true;
+ }
+ }
+ }
}
}
diff --git a/Kav.Core.csproj b/Kav.Core.csproj
index 327d7bb..1a7954e 100644
--- a/Kav.Core.csproj
+++ b/Kav.Core.csproj
@@ -30,6 +30,9 @@
Kav.Resources.ToneMapEffect.fxb
+
+ Kav.Resources.Deferred_ToonEffect.fxb
+
Kav.Resources.DeferredPBREffect.fxb
@@ -39,6 +42,15 @@
Kav.Resources.SimpleDepthEffect.fxb
+
+ Kav.Resources.LinearDepthEffect.fxb
+
+
+ Kav.Resources.SkyboxEffect.fxb
+
+
+ Kav.Resources.UnitCube.glb
+
diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj
index 2b2dd4f..c60f949 100644
--- a/Kav.Framework.csproj
+++ b/Kav.Framework.csproj
@@ -30,6 +30,9 @@
Kav.Resources.ToneMapEffect.fxb
+
+ Kav.Resources.Deferred_ToonEffect.fxb
+
Kav.Resources.DeferredPBREffect.fxb
@@ -39,6 +42,15 @@
Kav.Resources.SimpleDepthEffect.fxb
+
+ Kav.Resources.LinearDepthEffect.fxb
+
+
+ Kav.Resources.SkyboxEffect.fxb
+
+
+ Kav.Resources.UnitCube.glb
+
diff --git a/Lights/AmbientLight.cs b/Lights/AmbientLight.cs
new file mode 100644
index 0000000..94bb42e
--- /dev/null
+++ b/Lights/AmbientLight.cs
@@ -0,0 +1,14 @@
+using Microsoft.Xna.Framework;
+
+namespace Kav
+{
+ public struct AmbientLight
+ {
+ public Color Color { get; set; }
+
+ public AmbientLight(Color color)
+ {
+ Color = color;
+ }
+ }
+}
diff --git a/Lights/DirectionalLight.cs b/Lights/DirectionalLight.cs
index a22ab33..985745f 100644
--- a/Lights/DirectionalLight.cs
+++ b/Lights/DirectionalLight.cs
@@ -4,9 +4,9 @@ namespace Kav
{
public struct DirectionalLight
{
- public Vector3 Direction { get; set; }
- public Color Color { get; set; }
- public float Intensity { get; set; }
+ public Vector3 Direction { get; }
+ public Color Color { get; }
+ public float Intensity { get; }
public Matrix View
{
diff --git a/Loaders/ModelLoader.cs b/Loaders/ModelLoader.cs
index 4e908a0..634ae94 100644
--- a/Loaders/ModelLoader.cs
+++ b/Loaders/ModelLoader.cs
@@ -15,19 +15,6 @@ namespace Kav
foreach (var meshPartData in meshData.MeshParts)
{
- var effect = new Kav.DeferredPBR_GBufferEffect(
- graphicsDevice
- )
- {
- Albedo = meshPartData.Albedo,
- Metallic = meshPartData.Metallic,
- Roughness = meshPartData.Roughness,
-
- AlbedoTexture = meshPartData.AlbedoTexture,
- NormalTexture = meshPartData.NormalTexture,
- MetallicRoughnessTexture = meshPartData.MetallicRoughnessTexture
- };
-
var triangles = new Kav.Triangle[meshPartData.Triangles.Length];
for (int i = 0; i < meshPartData.Triangles.Length; i++)
{
@@ -40,13 +27,22 @@ namespace Kav
);
}
- meshParts.Add(new Kav.MeshPart(
+ var meshPart = new Kav.MeshPart(
meshPartData.VertexBuffer,
meshPartData.IndexBuffer,
meshPartData.Positions,
- triangles,
- effect
- ));
+ triangles
+ );
+
+ meshPart.Albedo = meshPartData.Albedo;
+ meshPart.Metallic = meshPartData.Metallic;
+ meshPart.Roughness = meshPartData.Roughness;
+
+ meshPart.AlbedoTexture = meshPartData.AlbedoTexture;
+ meshPart.NormalTexture = meshPartData.NormalTexture;
+ meshPart.MetallicRoughnessTexture = meshPartData.MetallicRoughnessTexture;
+
+ meshParts.Add(meshPart);
}
meshes.Add(new Kav.Mesh(meshParts.ToArray()));
diff --git a/Models/UnitCube.glb b/Models/UnitCube.glb
new file mode 100644
index 0000000..aa53f03
Binary files /dev/null and b/Models/UnitCube.glb differ
diff --git a/README.md b/README.md
index 8f3190b..29c7615 100644
--- a/README.md
+++ b/README.md
@@ -15,9 +15,9 @@ Essential
- [x] Tone map shader
- [x] Poisson soft shadowing
- [ ] Frustum culling
-- [ ] Shadow-casting point lights
+- [x] Shadow-casting point lights
- [ ] Parabolic lights
-- [ ] Skyboxes
+- [x] Skyboxes
- [ ] Screen-space reflection
Nice-To-Haves
diff --git a/Renderer.cs b/Renderer.cs
index 63b34e5..3eb2440 100644
--- a/Renderer.cs
+++ b/Renderer.cs
@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -21,19 +23,27 @@ namespace Kav
private RenderTarget2D[] ShadowRenderTargets { get; }
private DeferredPBREffect DeferredPBREffect { get; }
+ /* FIXME: these next two dont actually have anything to do with PBR */
+ private DeferredPBR_GBufferEffect Deferred_GBufferEffect { get; }
private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; }
private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; }
private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; }
+ private Deferred_ToonEffect Deferred_ToonEffect { get; }
private SimpleDepthEffect SimpleDepthEffect { get; }
+ private LinearDepthEffect LinearDepthEffect { get; }
private Effect ToneMapEffect { get; }
+ private SkyboxEffect SkyboxEffect { get; }
private RenderTarget2D gPosition { get; }
private RenderTarget2D gNormal { get; }
private RenderTarget2D gAlbedo { get; }
private RenderTarget2D gMetallicRoughness { get; }
+ private RenderTargetCube PointShadowCubeMap { get; }
private RenderTargetBinding[] GBuffer { get; }
+ private Kav.Model UnitCube { get; }
+
private SpriteBatch SpriteBatch { get; }
public Renderer(
@@ -70,7 +80,7 @@ namespace Kav
renderDimensionsY,
false,
SurfaceFormat.Color,
- DepthFormat.None,
+ DepthFormat.Depth24,
0,
RenderTargetUsage.PreserveContents
);
@@ -129,13 +139,26 @@ namespace Kav
new RenderTargetBinding(gMetallicRoughness)
};
+ PointShadowCubeMap = new RenderTargetCube(
+ GraphicsDevice,
+ shadowMapSize,
+ false,
+ SurfaceFormat.Single,
+ DepthFormat.Depth24
+ );
+
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
+ LinearDepthEffect = new LinearDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
+
+ Deferred_GBufferEffect = new DeferredPBR_GBufferEffect(GraphicsDevice);
DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice);
DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize;
ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect);
+ Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice);
+ SkyboxEffect = new SkyboxEffect(GraphicsDevice);
FullscreenTriangle = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 3, BufferUsage.WriteOnly);
FullscreenTriangle.SetData(new VertexPositionTexture[3] {
@@ -144,39 +167,93 @@ namespace Kav
new VertexPositionTexture(new Vector3(3, 1, 0), new Vector2(2, 0))
});
+ UnitCube = Kav.ModelLoader.Load(
+ GraphicsDevice,
+ Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel))
+ );
+
SpriteBatch = new SpriteBatch(graphicsDevice);
}
public void DeferredRender(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
+ AmbientLight ambientLight,
IEnumerable pointLights,
DirectionalLight directionalLight
) {
- // g-buffer pass
+ GBufferRender(camera, modelTransforms);
- GraphicsDevice.SetRenderTargets(GBuffer);
+ GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.Clear(Color.Black);
+
+ AmbientLightRender(ambientLight);
+
+ DeferredPointLightEffect.EyePosition = camera.Position;
+
+ foreach (var pointLight in pointLights)
+ {
+ PointLightRender(camera, modelTransforms, pointLight);
+ }
+
+ DirectionalLightRender(camera, modelTransforms, directionalLight);
+
+ GraphicsDevice.SetRenderTarget(null);
+ GraphicsDevice.Clear(Color.Black);
+ SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect);
+ SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
+ SpriteBatch.End();
+ }
+
+ public void DeferredToonRender(
+ PerspectiveCamera camera,
+ IEnumerable<(Model, Matrix)> modelTransforms,
+ AmbientLight ambientLight,
+ IEnumerable pointLights,
+ DirectionalLight directionalLight,
+ TextureCube skybox
+ ) {
+ GBufferRender(camera, modelTransforms);
+
+ GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
- GraphicsDevice.BlendState = BlendState.Opaque;
+ DepthRender(camera, modelTransforms);
+ GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+
+ AmbientLightRender(ambientLight);
+ foreach (var pointLight in pointLights)
+ {
+ PointLightRender(camera, modelTransforms, pointLight);
+ }
+ DirectionalLightToonRender(camera, modelTransforms, directionalLight);
+ SkyboxRender(camera, skybox);
+
+ GraphicsDevice.SetRenderTarget(null);
+ SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, null);
+ SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
+ SpriteBatch.End();
+ }
+
+ private void DepthRender(
+ PerspectiveCamera camera,
+ IEnumerable<(Model, Matrix)> modelTransforms
+ ) {
foreach (var (model, transform) in modelTransforms)
{
foreach (var modelMesh in model.Meshes)
{
foreach (var meshPart in modelMesh.MeshParts)
{
+ SimpleDepthEffect.Model = transform;
+ SimpleDepthEffect.View = camera.View;
+ SimpleDepthEffect.Projection = camera.Projection;
+
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
GraphicsDevice.Indices = meshPart.IndexBuffer;
- if (meshPart.Effect is TransformEffect transformEffect)
- {
- transformEffect.World = transform;
- transformEffect.View = camera.View;
- transformEffect.Projection = camera.Projection;
- }
-
- foreach (var pass in meshPart.Effect.CurrentTechnique.Passes)
+ foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes)
{
pass.Apply();
@@ -192,14 +269,96 @@ namespace Kav
}
}
}
+ }
+ private void SkyboxRender(
+ PerspectiveCamera camera,
+ TextureCube skybox
+ ) {
+ GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace;
+ SkyboxEffect.Skybox = skybox;
+
+ var view = camera.View;
+ view.Translation = Vector3.Zero;
+ SkyboxEffect.View = view;
+
+ SkyboxEffect.Projection = camera.Projection;
+
+ GraphicsDevice.SetVertexBuffer(UnitCube.Meshes[0].MeshParts[0].VertexBuffer);
+ GraphicsDevice.Indices = UnitCube.Meshes[0].MeshParts[0].IndexBuffer;
+
+ foreach (var pass in SkyboxEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+
+ GraphicsDevice.DrawIndexedPrimitives(
+ PrimitiveType.TriangleList,
+ 0,
+ 0,
+ UnitCube.Meshes[0].MeshParts[0].VertexBuffer.VertexCount,
+ 0,
+ UnitCube.Meshes[0].MeshParts[0].Triangles.Length
+ );
+ }
+ GraphicsDevice.RasterizerState.CullMode = CullMode.CullCounterClockwiseFace;
+ }
+
+ private void GBufferRender(
+ PerspectiveCamera camera,
+ IEnumerable<(Model, Matrix)> modelTransforms
+ ) {
+ GraphicsDevice.SetRenderTargets(GBuffer);
+ GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ foreach (var (model, transform) in modelTransforms)
+ {
+ foreach (var modelMesh in model.Meshes)
+ {
+ foreach (var meshPart in modelMesh.MeshParts)
+ {
+ Deferred_GBufferEffect.World = transform;
+ Deferred_GBufferEffect.View = camera.View;
+ Deferred_GBufferEffect.Projection = camera.Projection;
+
+ Deferred_GBufferEffect.Albedo = meshPart.Albedo;
+ Deferred_GBufferEffect.Metallic = meshPart.Metallic;
+ Deferred_GBufferEffect.Roughness = meshPart.Roughness;
+
+ Deferred_GBufferEffect.AlbedoTexture = meshPart.AlbedoTexture;
+ Deferred_GBufferEffect.NormalTexture = meshPart.NormalTexture;
+ Deferred_GBufferEffect.MetallicRoughnessTexture = meshPart.MetallicRoughnessTexture;
+
+ GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
+ GraphicsDevice.Indices = meshPart.IndexBuffer;
+
+ foreach (var pass in Deferred_GBufferEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+
+ GraphicsDevice.DrawIndexedPrimitives(
+ PrimitiveType.TriangleList,
+ 0,
+ 0,
+ meshPart.VertexBuffer.VertexCount,
+ 0,
+ meshPart.Triangles.Length
+ );
+ }
+ }
+ }
+ }
+ }
+
+ private void AmbientLightRender(AmbientLight ambientLight)
+ {
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
- GraphicsDevice.Clear(Color.Black);
- GraphicsDevice.BlendState = BlendState.Additive;
- GraphicsDevice.DepthStencilState = DepthStencilState.None;
+ GraphicsDevice.BlendState = BlendState.Opaque;
DeferredAmbientLightEffect.GPosition = gPosition;
DeferredAmbientLightEffect.GAlbedo = gAlbedo;
+ DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3();
foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes)
{
@@ -207,39 +366,31 @@ namespace Kav
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
-
- DeferredPointLightEffect.EyePosition = camera.Position;
-
- foreach (var pointLight in pointLights)
- {
- PointLightRender(pointLight);
- }
-
- DirectionalLightRender(camera, modelTransforms, directionalLight);
- // return;
- // GraphicsDevice.SetRenderTarget(null);
- // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
- // SpriteBatch.Draw(DirectionalRenderTarget, Vector2.Zero, Color.White);
- // SpriteBatch.End();
-
- GraphicsDevice.SetRenderTarget(null);
- GraphicsDevice.Clear(Color.Black);
- SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect);
- SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
- SpriteBatch.End();
}
- private void PointLightRender(PointLight pointLight)
- {
+ private void PointLightRender(
+ PerspectiveCamera camera,
+ IEnumerable<(Model, Matrix)> modelTransforms,
+ PointLight pointLight
+ ) {
+ RenderPointShadows(camera, modelTransforms, pointLight);
+
+ GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+ GraphicsDevice.BlendState = BlendState.Additive;
+
DeferredPointLightEffect.GPosition = gPosition;
DeferredPointLightEffect.GAlbedo = gAlbedo;
DeferredPointLightEffect.GNormal = gNormal;
DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
+ DeferredPointLightEffect.ShadowMap = PointShadowCubeMap;
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
DeferredPointLightEffect.PointLightColor =
pointLight.Color.ToVector3() * pointLight.Intensity;
+ DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value
+
foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
@@ -253,28 +404,7 @@ namespace Kav
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight
) {
- // 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, i);
-
- previousFarPlane = farPlane;
- }
+ RenderDirectionalShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect);
DeferredDirectionalLightEffect.GPosition = gPosition;
DeferredDirectionalLightEffect.GAlbedo = gAlbedo;
@@ -300,7 +430,7 @@ namespace Kav
directionalLight.Color.ToVector3() * directionalLight.Intensity;
DeferredDirectionalLightEffect.ViewMatrix = camera.View;
- DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation;
+ DeferredDirectionalLightEffect.EyePosition = camera.Position;
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.BlendState = BlendState.Additive;
@@ -313,10 +443,89 @@ namespace Kav
}
}
- private void RenderShadowMap(
+ private void DirectionalLightToonRender(
+ PerspectiveCamera camera,
+ IEnumerable<(Model, Matrix)> modelTransforms,
+ DirectionalLight directionalLight
+ ) {
+ RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect);
+
+ GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+ GraphicsDevice.BlendState = BlendState.Additive;
+
+ Deferred_ToonEffect.GPosition = gPosition;
+ Deferred_ToonEffect.GAlbedo = gAlbedo;
+ Deferred_ToonEffect.GNormal = gNormal;
+ Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness;
+
+ Deferred_ToonEffect.DitheredShadows = false;
+
+ Deferred_ToonEffect.EyePosition = camera.Position;
+ Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction;
+ Deferred_ToonEffect.DirectionalLightColor =
+ directionalLight.Color.ToVector3() * directionalLight.Intensity;
+
+ 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)
+ {
+ pass.Apply();
+ GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
+ GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
+ }
+ }
+
+ private void RenderDirectionalShadows(
+ 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
+ RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
+
+ effect.CascadeFarPlanes[i] = farPlane;
+ previousFarPlane = farPlane;
+ }
+ }
+
+ private void RenderDirectionalShadowMap(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
+ ShadowCascadeEffect effect,
int shadowCascadeIndex
) {
GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
@@ -357,23 +566,21 @@ namespace Kav
if (shadowCascadeIndex == 0)
{
- DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix;
+ effect.LightSpaceMatrixOne = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 1)
{
- DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix;
+ effect.LightSpaceMatrixTwo = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 2)
{
- DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix;
+ effect.LightSpaceMatrixThree = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 3)
{
- DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix;
+ effect.LightSpaceMatrixFour = lightSpaceMatrix;
}
- DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane;
-
foreach (var (model, transform) in modelTransforms)
{
foreach (var modelMesh in model.Meshes)
@@ -403,62 +610,98 @@ namespace Kav
}
}
- public void Render(
- PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- IEnumerable pointLights,
- IEnumerable directionalLights
- ) {
- Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights);
- }
-
- private void Render(
- Matrix view,
- Matrix projection,
+ private void RenderPointShadows(
+ PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
- IEnumerable pointLights,
- IEnumerable directionalLights
+ PointLight pointLight
) {
- foreach (var (model, transform) in modelTransforms)
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
+ MathHelper.PiOver2,
+ 1,
+ 0.1f,
+ 25f // FIXME: magic value
+ );
+ LinearDepthEffect.FarPlane = 25f;
+ LinearDepthEffect.LightPosition = pointLight.Position;
+
+ foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
{
- foreach (var modelMesh in model.Meshes)
+ GraphicsDevice.SetRenderTarget(PointShadowCubeMap, face);
+
+ Vector3 targetDirection;
+ Vector3 targetUpDirection;
+ switch(face)
{
- foreach (var meshPart in modelMesh.MeshParts)
+ case CubeMapFace.PositiveX:
+ targetDirection = Vector3.Right;
+ targetUpDirection = Vector3.Up;
+ break;
+
+ case CubeMapFace.NegativeX:
+ targetDirection = Vector3.Left;
+ targetUpDirection = Vector3.Up;
+ break;
+
+ case CubeMapFace.PositiveY:
+ targetDirection = Vector3.Up;
+ targetUpDirection = Vector3.Forward;
+ break;
+
+ case CubeMapFace.NegativeY:
+ targetDirection = Vector3.Down;
+ targetUpDirection = Vector3.Backward;
+ break;
+
+ case CubeMapFace.PositiveZ:
+ targetDirection = Vector3.Backward;
+ targetUpDirection = Vector3.Up;
+ break;
+
+ case CubeMapFace.NegativeZ:
+ targetDirection = Vector3.Forward;
+ targetUpDirection = Vector3.Up;
+ break;
+
+ default:
+ targetDirection = Vector3.Right;
+ targetUpDirection = Vector3.Up;
+ break;
+ }
+
+ LinearDepthEffect.View = Matrix.CreateLookAt(
+ pointLight.Position,
+ pointLight.Position + targetDirection,
+ targetUpDirection
+ );
+
+ foreach (var (model, transform) in modelTransforms)
+ {
+ foreach (var modelMesh in model.Meshes)
{
- GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
- GraphicsDevice.Indices = meshPart.IndexBuffer;
-
- if (meshPart.Effect is TransformEffect transformEffect)
+ foreach (var meshPart in modelMesh.MeshParts)
{
- transformEffect.World = transform;
- transformEffect.View = view;
- transformEffect.Projection = projection;
- }
+ GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
+ GraphicsDevice.Indices = meshPart.IndexBuffer;
- if (meshPart.Effect is PointLightEffect pointLightEffect)
- {
- int i = 0;
- foreach (var pointLight in pointLights)
+ LinearDepthEffect.Model = transform;
+
+ foreach (var pass in LinearDepthEffect.CurrentTechnique.Passes)
{
- if (i > pointLightEffect.MaxPointLights) { break; }
- pointLightEffect.PointLights[i] = pointLight;
- i++;
+ pass.Apply();
+
+ GraphicsDevice.DrawIndexedPrimitives(
+ PrimitiveType.TriangleList,
+ 0,
+ 0,
+ meshPart.VertexBuffer.VertexCount,
+ 0,
+ meshPart.Triangles.Length
+ );
}
}
-
- foreach (var pass in meshPart.Effect.CurrentTechnique.Passes)
- {
- pass.Apply();
-
- GraphicsDevice.DrawIndexedPrimitives(
- PrimitiveType.TriangleList,
- 0,
- 0,
- meshPart.VertexBuffer.VertexCount,
- 0,
- meshPart.Triangles.Length
- );
- }
}
}
}
diff --git a/Resources.cs b/Resources.cs
index 7791eaa..fd5daa5 100644
--- a/Resources.cs
+++ b/Resources.cs
@@ -10,7 +10,7 @@ namespace Kav
{
if (ambientLightEffect == null)
{
- ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect");
+ ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect.fxb");
}
return ambientLightEffect;
}
@@ -21,7 +21,7 @@ namespace Kav
{
if (pointLightEffect == null)
{
- pointLightEffect = GetResource("DeferredPBR_PointLightEffect");
+ pointLightEffect = GetResource("DeferredPBR_PointLightEffect.fxb");
}
return pointLightEffect;
}
@@ -33,7 +33,7 @@ namespace Kav
{
if (directionalLightEffect == null)
{
- directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect");
+ directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect.fxb");
}
return directionalLightEffect;
}
@@ -45,7 +45,7 @@ namespace Kav
{
if (gBufferEffect == null)
{
- gBufferEffect = GetResource("DeferredPBR_GBufferEffect");
+ gBufferEffect = GetResource("DeferredPBR_GBufferEffect.fxb");
}
return gBufferEffect;
}
@@ -57,19 +57,31 @@ namespace Kav
{
if (toneMapEffect == null)
{
- toneMapEffect = GetResource("ToneMapEffect");
+ toneMapEffect = GetResource("ToneMapEffect.fxb");
}
return toneMapEffect;
}
}
+ public static byte[] Deferred_ToonEffect
+ {
+ get
+ {
+ if (deferredToonEffect == null)
+ {
+ deferredToonEffect = GetResource("Deferred_ToonEffect.fxb");
+ }
+ return deferredToonEffect;
+ }
+ }
+
public static byte[] DeferredPBREffect
{
get
{
if (deferredPBREffect == null)
{
- deferredPBREffect = GetResource("DeferredPBREffect");
+ deferredPBREffect = GetResource("DeferredPBREffect.fxb");
}
return deferredPBREffect;
}
@@ -81,7 +93,7 @@ namespace Kav
{
if (pbrEffect == null)
{
- pbrEffect = GetResource("PBREffect");
+ pbrEffect = GetResource("PBREffect.fxb");
}
return pbrEffect;
}
@@ -93,25 +105,66 @@ namespace Kav
{
if (simpleDepthEffect == null)
{
- simpleDepthEffect = GetResource("SimpleDepthEffect");
+ simpleDepthEffect = GetResource("SimpleDepthEffect.fxb");
}
return simpleDepthEffect;
}
}
+ public static byte[] LinearDepthEffect
+ {
+ get
+ {
+ if (linearDepthEffect == null)
+ {
+ linearDepthEffect = GetResource("LinearDepthEffect.fxb");
+ }
+ return linearDepthEffect;
+ }
+ }
+
+ public static byte[] SkyboxEffect
+ {
+ get
+ {
+ if (skyboxEffect == null)
+ {
+ skyboxEffect = GetResource("SkyboxEffect.fxb");
+ }
+ return skyboxEffect;
+ }
+ }
+
+ public static byte[] UnitCubeModel
+ {
+ get
+ {
+ if (unitCubeModel == null)
+ {
+ unitCubeModel = GetResource("UnitCube.glb");
+ }
+ return unitCubeModel;
+ }
+ }
+
private static byte[] ambientLightEffect;
private static byte[] pointLightEffect;
private static byte[] directionalLightEffect;
private static byte[] gBufferEffect;
private static byte[] toneMapEffect;
+ private static byte[] deferredToonEffect;
private static byte[] deferredPBREffect;
private static byte[] pbrEffect;
private static byte[] simpleDepthEffect;
+ private static byte[] linearDepthEffect;
+ private static byte[] skyboxEffect;
+
+ private static byte[] unitCubeModel;
private static byte[] GetResource(string name)
{
Stream stream = typeof(Resources).Assembly.GetManifestResourceStream(
- "Kav.Resources." + name + ".fxb"
+ "Kav.Resources." + name
);
using (MemoryStream ms = new MemoryStream())
{