diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..40824ed
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.fxb filter=lfs diff=lfs merge=lfs -text
diff --git a/Cameras/Camera.cs b/Cameras/Camera.cs
index c0729ee..5462985 100644
--- a/Cameras/Camera.cs
+++ b/Cameras/Camera.cs
@@ -2,22 +2,32 @@ using Microsoft.Xna.Framework;
namespace Kav
{
- public struct Camera
+ public struct PerspectiveCamera
{
- public Matrix Transform { get; }
- public Matrix View
- {
- get
- {
- return Matrix.CreateLookAt(Transform.Translation, Transform.Translation + Transform.Forward, Transform.Up);
- }
- }
+ public Matrix View { get; }
public Matrix Projection { get; }
- public Camera(Matrix transform, Matrix projection)
+ public Vector3 Position { get; }
+ public Vector3 Forward { get; }
+ public Vector3 Up { get; }
+
+ public float FieldOfView { get; }
+ public float AspectRatio { get; }
+ public float NearPlane { get; }
+ public float FarPlane { get; }
+
+ public PerspectiveCamera(Vector3 position, Vector3 forward, Vector3 up, float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
{
- Transform = transform;
- Projection = projection;
+ Position = position;
+ Forward = forward;
+ Up = up;
+ View = Matrix.CreateLookAt(Position, Position + Forward, Up);
+
+ FieldOfView = fieldOfView;
+ AspectRatio = aspectRatio;
+ NearPlane = nearPlane;
+ FarPlane = farPlane;
+ Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
}
}
}
diff --git a/EffectInterfaces/DirectionalLightEffect.cs b/EffectInterfaces/DirectionalLightEffect.cs
deleted file mode 100644
index 53ba894..0000000
--- a/EffectInterfaces/DirectionalLightEffect.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Kav
-{
- public interface DirectionalLightEffect
- {
- int MaxDirectionalLights { get; }
- DirectionalLightCollection DirectionalLights { get; }
- }
-}
diff --git a/Effects/DeferredPBREffect.cs b/Effects/DeferredPBREffect.cs
index 97c8615..181d1ec 100644
--- a/Effects/DeferredPBREffect.cs
+++ b/Effects/DeferredPBREffect.cs
@@ -10,15 +10,47 @@ namespace Kav
EffectParameter gNormalParam;
EffectParameter gMetallicRoughnessParam;
+ EffectParameter shadowMapOneParam;
+ EffectParameter shadowMapTwoParam;
+ EffectParameter shadowMapThreeParam;
+ EffectParameter shadowMapFourParam;
+
+ EffectParameter lightSpaceMatrixOneParam;
+ EffectParameter lightSpaceMatrixTwoParam;
+ EffectParameter lightSpaceMatrixThreeParam;
+ EffectParameter lightSpaceMatrixFourParam;
+
+ EffectParameter viewMatrixParam;
+ EffectParameter cascadeFarPlanesParam;
+
+ EffectParameter directionalLightColorParam;
+ EffectParameter directionalLightDirectionParam;
+
EffectParameter eyePositionParam;
+
PointLightCollection pointLightCollection;
- DirectionalLightCollection directionalLightCollection;
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 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 readonly float[] CascadeFarPlanes;
+
+ public Vector3 DirectionalLightColor { get; set; }
+ public Vector3 DirectionalLightDirection { get; set; }
+
public Vector3 EyePosition { get; set; }
public int MaxPointLights { get; } = 64;
@@ -29,16 +61,10 @@ namespace Kav
private set { pointLightCollection = value; }
}
- public int MaxDirectionalLights { get; } = 4;
-
- public DirectionalLightCollection DirectionalLights
- {
- get { return directionalLightCollection; }
- private set { directionalLightCollection = value; }
- }
-
public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect)
{
+ CascadeFarPlanes = new float[4];
+
CacheEffectParameters();
pointLightCollection = new PointLightCollection(
@@ -46,11 +72,6 @@ namespace Kav
Parameters["PointLightColors"],
MaxPointLights
);
-
- DirectionalLights = new DirectionalLightCollection(
- Parameters["DirectionalLightDirections"],
- Parameters["DirectionalLightColors"]
- );
}
protected DeferredPBREffect(DeferredPBREffect cloneSource) : base(cloneSource)
@@ -72,16 +93,6 @@ namespace Kav
{
PointLights[i] = cloneSource.PointLights[i];
}
-
- DirectionalLights = new DirectionalLightCollection(
- Parameters["DirectionalLightDirections"],
- Parameters["DirectionalLightColors"]
- );
-
- for (int i = 0; i < MaxDirectionalLights; i++)
- {
- DirectionalLights[i] = cloneSource.DirectionalLights[i];
- }
}
public override Effect Clone()
@@ -96,17 +107,49 @@ namespace Kav
gNormalParam.SetValue(GNormal);
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
+ shadowMapOneParam.SetValue(ShadowMapOne);
+ shadowMapTwoParam.SetValue(ShadowMapTwo);
+ shadowMapThreeParam.SetValue(ShadowMapThree);
+ shadowMapFourParam.SetValue(ShadowMapFour);
+
+ lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
+ lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
+ lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
+ lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
+
+ viewMatrixParam.SetValue(ViewMatrix);
+ cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
+
+ directionalLightColorParam.SetValue(DirectionalLightColor);
+ directionalLightDirectionParam.SetValue(DirectionalLightDirection);
+
eyePositionParam.SetValue(EyePosition);
}
void CacheEffectParameters()
{
- gPositionParam = Parameters["gPosition"];
- gAlbedoParam = Parameters["gAlbedo"];
- gNormalParam = Parameters["gNormal"];
- gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
+ gPositionParam = Parameters["gPosition"];
+ gAlbedoParam = Parameters["gAlbedo"];
+ gNormalParam = Parameters["gNormal"];
+ gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
- eyePositionParam = Parameters["EyePosition"];
+ shadowMapOneParam = Parameters["shadowMapOne"];
+ shadowMapTwoParam = Parameters["shadowMapTwo"];
+ shadowMapThreeParam = Parameters["shadowMapThree"];
+ shadowMapFourParam = Parameters["shadowMapFour"];
+
+ lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
+ lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
+ lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
+ lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
+
+ viewMatrixParam = Parameters["ViewMatrix"];
+ cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
+
+ directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
+ directionalLightColorParam = Parameters["DirectionalLightColor"];
+
+ eyePositionParam = Parameters["EyePosition"];
}
}
}
diff --git a/Effects/DeferredPBR_AmbientLightEffect.cs b/Effects/DeferredPBR_AmbientLightEffect.cs
new file mode 100644
index 0000000..f1d5e31
--- /dev/null
+++ b/Effects/DeferredPBR_AmbientLightEffect.cs
@@ -0,0 +1,31 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+
+ public class DeferredPBR_AmbientLightEffect : Effect
+ {
+ EffectParameter gPositionParam;
+ EffectParameter gAlbedoParam;
+
+ public Texture2D GPosition { get; set; }
+ public Texture2D GAlbedo { get; set; }
+
+ public DeferredPBR_AmbientLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_AmbientLightEffect)
+ {
+ CacheEffectParameters();
+ }
+
+ protected override void OnApply()
+ {
+ gPositionParam.SetValue(GPosition);
+ gAlbedoParam.SetValue(GAlbedo);
+ }
+
+ void CacheEffectParameters()
+ {
+ gPositionParam = Parameters["gPosition"];
+ gAlbedoParam = Parameters["gAlbedo"];
+ }
+ }
+}
diff --git a/Effects/DeferredPBR_DirectionalLightEffect.cs b/Effects/DeferredPBR_DirectionalLightEffect.cs
new file mode 100644
index 0000000..5e1f919
--- /dev/null
+++ b/Effects/DeferredPBR_DirectionalLightEffect.cs
@@ -0,0 +1,161 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class DeferredPBR_DirectionalLightEffect : Effect
+ {
+ EffectParameter gPositionParam;
+ EffectParameter gAlbedoParam;
+ EffectParameter gNormalParam;
+ EffectParameter gMetallicRoughnessParam;
+
+ EffectParameter shadowMapOneParam;
+ EffectParameter shadowMapTwoParam;
+ EffectParameter shadowMapThreeParam;
+ EffectParameter shadowMapFourParam;
+
+ EffectParameter eyePositionParam;
+
+ EffectParameter directionalLightColorParam;
+ EffectParameter directionalLightDirectionParam;
+
+ EffectParameter cascadeFarPlanesParam;
+
+ EffectParameter shadowMapSizeParam;
+
+ EffectParameter lightSpaceMatrixOneParam;
+ EffectParameter lightSpaceMatrixTwoParam;
+ EffectParameter lightSpaceMatrixThreeParam;
+ EffectParameter lightSpaceMatrixFourParam;
+
+ EffectParameter viewMatrixParam;
+
+ 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 readonly float[] CascadeFarPlanes;
+
+ public int 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 DeferredPBR_DirectionalLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_DirectionalLightEffect)
+ {
+ CascadeFarPlanes = new float[4];
+ CacheEffectParameters();
+ }
+
+ public DeferredPBR_DirectionalLightEffect(DeferredPBR_DirectionalLightEffect cloneSource) : base(cloneSource)
+ {
+ GPosition = cloneSource.GPosition;
+ GAlbedo = cloneSource.GAlbedo;
+ GNormal = cloneSource.GNormal;
+ GMetallicRoughness = cloneSource.GMetallicRoughness;
+
+ ShadowMapOne = cloneSource.ShadowMapOne;
+ ShadowMapTwo = cloneSource.ShadowMapTwo;
+ ShadowMapThree = cloneSource.ShadowMapThree;
+ ShadowMapFour = cloneSource.ShadowMapFour;
+
+ EyePosition = cloneSource.EyePosition;
+
+ DirectionalLightDirection = cloneSource.DirectionalLightDirection;
+ DirectionalLightColor = cloneSource.DirectionalLightColor;
+
+ CascadeFarPlanes = new float[4];
+ for (int i = 0 ; i < 4; i++)
+ {
+ CascadeFarPlanes[i] = cloneSource.CascadeFarPlanes[i];
+ }
+
+ ShadowMapSize = cloneSource.ShadowMapSize;
+
+ LightSpaceMatrixOne = cloneSource.LightSpaceMatrixOne;
+ LightSpaceMatrixTwo = cloneSource.LightSpaceMatrixTwo;
+ LightSpaceMatrixThree = cloneSource.LightSpaceMatrixThree;
+ LightSpaceMatrixFour = cloneSource.LightSpaceMatrixFour;
+
+ ViewMatrix = cloneSource.ViewMatrix;
+ }
+
+ public override Effect Clone()
+ {
+ return new DeferredPBR_DirectionalLightEffect(this);
+ }
+
+ 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);
+ }
+
+ 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"];
+ }
+ }
+}
diff --git a/Effects/DeferredPBR_PointLightEffect.cs b/Effects/DeferredPBR_PointLightEffect.cs
new file mode 100644
index 0000000..6f959ea
--- /dev/null
+++ b/Effects/DeferredPBR_PointLightEffect.cs
@@ -0,0 +1,77 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class DeferredPBR_PointLightEffect : Effect
+ {
+ EffectParameter gPositionParam;
+ EffectParameter gAlbedoParam;
+ EffectParameter gNormalParam;
+ EffectParameter gMetallicRoughnessParam;
+
+ EffectParameter eyePositionParam;
+
+ EffectParameter pointLightColorParam;
+ EffectParameter pointLightPositionParam;
+
+ public Texture2D GPosition { get; set; }
+ public Texture2D GAlbedo { get; set; }
+ public Texture2D GNormal { get; set; }
+ public Texture2D GMetallicRoughness { get; set; }
+
+ public Vector3 EyePosition { get; set; }
+
+ public Vector3 PointLightPosition { get; set; }
+ public Vector3 PointLightColor { get; set; }
+
+ public DeferredPBR_PointLightEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBR_PointLightEffect)
+ {
+ CacheEffectParameters();
+ }
+
+ public DeferredPBR_PointLightEffect(DeferredPBR_PointLightEffect cloneSource) : base(cloneSource)
+ {
+ GPosition = cloneSource.GPosition;
+ GAlbedo = cloneSource.GAlbedo;
+ GNormal = cloneSource.GNormal;
+ GMetallicRoughness = cloneSource.GMetallicRoughness;
+
+ EyePosition = cloneSource.EyePosition;
+
+ PointLightPosition = cloneSource.PointLightPosition;
+ PointLightColor = cloneSource.PointLightColor;
+ }
+
+ public override Effect Clone()
+ {
+ return new DeferredPBR_PointLightEffect(this);
+ }
+
+ protected override void OnApply()
+ {
+ gPositionParam.SetValue(GPosition);
+ gAlbedoParam.SetValue(GAlbedo);
+ gNormalParam.SetValue(GNormal);
+ gMetallicRoughnessParam.SetValue(GMetallicRoughness);
+
+ eyePositionParam.SetValue(EyePosition);
+
+ pointLightPositionParam.SetValue(PointLightPosition);
+ pointLightColorParam.SetValue(PointLightColor);
+ }
+
+ void CacheEffectParameters()
+ {
+ gPositionParam = Parameters["gPosition"];
+ gAlbedoParam = Parameters["gAlbedo"];
+ gNormalParam = Parameters["gNormal"];
+ gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
+
+ eyePositionParam = Parameters["EyePosition"];
+
+ pointLightPositionParam = Parameters["PointLightPosition"];
+ pointLightColorParam = Parameters["PointLightColor"];
+ }
+ }
+}
diff --git a/Effects/DirectionalLightCollection.cs b/Effects/DirectionalLightCollection.cs
deleted file mode 100644
index 10bcbda..0000000
--- a/Effects/DirectionalLightCollection.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-
-namespace Kav
-{
- public class DirectionalLightCollection
- {
- private readonly Vector3[] directions = new Vector3[4];
- private readonly Vector3[] colors = new Vector3[4];
- private readonly float[] intensities = new float[4];
-
- readonly EffectParameter lightDirectionsParam;
- readonly EffectParameter lightColorsParam;
-
- public DirectionalLightCollection(EffectParameter lightDirectionsParam, EffectParameter lightColorsParam)
- {
- this.lightDirectionsParam = lightDirectionsParam;
- this.lightColorsParam = lightColorsParam;
- }
-
- public DirectionalLight this[int i]
- {
- get
- {
- var color = colors[i] / intensities[i];
- return new DirectionalLight(
- directions[i],
- new Color(
- color.X,
- color.Y,
- color.Z,
- 1f
- ),
- intensities[i]
- );
- }
- set
- {
- directions[i] = value.Direction;
- colors[i] = value.Color.ToVector3() * value.Intensity;
- intensities[i] = value.Intensity;
- lightDirectionsParam.SetValue(directions);
- lightColorsParam.SetValue(colors);
- }
- }
- }
-}
diff --git a/Effects/FXB/DeferredPBREffect.fxb b/Effects/FXB/DeferredPBREffect.fxb
index 10298d5..1d4d26c 100644
Binary files a/Effects/FXB/DeferredPBREffect.fxb and b/Effects/FXB/DeferredPBREffect.fxb differ
diff --git a/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb
new file mode 100644
index 0000000..62de278
--- /dev/null
+++ b/Effects/FXB/DeferredPBR_AmbientLightEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0ea0cb071a1e53fec5058fae9919d376e408d33cd3de9485d42e1ebcbee85546
+size 1016
diff --git a/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb
new file mode 100644
index 0000000..9f6cf27
--- /dev/null
+++ b/Effects/FXB/DeferredPBR_DirectionalLightEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:be44f16057328acf2bf9d2e4ff1e2d448df720711a2900daa39f5fc8c8732711
+size 21692
diff --git a/Effects/FXB/DeferredPBR_GBufferEffect.fxb b/Effects/FXB/DeferredPBR_GBufferEffect.fxb
index 9b2d684..3aff2ad 100644
Binary files a/Effects/FXB/DeferredPBR_GBufferEffect.fxb and b/Effects/FXB/DeferredPBR_GBufferEffect.fxb differ
diff --git a/Effects/FXB/DeferredPBR_PointLightEffect.fxb b/Effects/FXB/DeferredPBR_PointLightEffect.fxb
new file mode 100644
index 0000000..398f66b
--- /dev/null
+++ b/Effects/FXB/DeferredPBR_PointLightEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e51395e0f0dd1e1b86272dde1e4fb5aea1fdf9b86399416015ad33cfb7260691
+size 3108
diff --git a/Effects/FXB/GBufferEffect.fxb b/Effects/FXB/GBufferEffect.fxb
index 9b2d684..ad31fdf 100644
Binary files a/Effects/FXB/GBufferEffect.fxb and b/Effects/FXB/GBufferEffect.fxb differ
diff --git a/Effects/FXB/PBREffect.fxb b/Effects/FXB/PBREffect.fxb
index 1747aa4..2214837 100644
Binary files a/Effects/FXB/PBREffect.fxb and b/Effects/FXB/PBREffect.fxb differ
diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb
index e2baafe..c60bf15 100644
Binary files a/Effects/FXB/SimpleDepthEffect.fxb and b/Effects/FXB/SimpleDepthEffect.fxb differ
diff --git a/Effects/FXB/ToneMapEffect.fxb b/Effects/FXB/ToneMapEffect.fxb
new file mode 100644
index 0000000..5085f08
--- /dev/null
+++ b/Effects/FXB/ToneMapEffect.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efddb6cc98515293e65c5033e376ff01f38e3589c329e1b52901437df82ea93e
+size 584
diff --git a/Effects/HLSL/DeferredPBREffect.fx b/Effects/HLSL/DeferredPBREffect.fx
index a49f27a..14a58c2 100644
--- a/Effects/HLSL/DeferredPBREffect.fx
+++ b/Effects/HLSL/DeferredPBREffect.fx
@@ -2,12 +2,16 @@
static const float PI = 3.141592653589793;
static const int MAX_POINT_LIGHTS = 64;
-static const int MAX_DIRECTIONAL_LIGHTS = 4;
+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
@@ -16,19 +20,45 @@ BEGIN_CONSTANTS
float3 PointLightPositions[MAX_POINT_LIGHTS] _ps(c1) _cb(c1);
float3 PointLightColors[MAX_POINT_LIGHTS] _ps(c65) _cb(c65);
- float3 DirectionalLightDirections[MAX_DIRECTIONAL_LIGHTS] _ps(c129) _cb(c129);
- float3 DirectionalLightColors[MAX_DIRECTIONAL_LIGHTS] _ps(c133) _cb(c133);
+ float3 DirectionalLightDirection _ps(c129) _cb(c129);
+ float3 DirectionalLightColor _ps(c130) _cb(c130);
+
+ float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c131) _cb(c131);
MATRIX_CONSTANTS
+ float4x4 LightSpaceMatrixOne _ps(c135) _cb(c135);
+ float4x4 LightSpaceMatrixTwo _ps(c139) _cb(c139);
+ float4x4 LightSpaceMatrixThree _ps(c143) _cb(c143);
+ float4x4 LightSpaceMatrixFour _ps(c147) _cb(c147);
+
+ // used to select shadow cascade
+ float4x4 ViewMatrix _ps(c151) _cb(c151);
+
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;
+}
+
// Pixel Shader
float3 FresnelSchlick(float cosTheta, float3 F0)
@@ -71,17 +101,119 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
return ggx1 * ggx2;
}
+float ComputeShadow(float3 positionWorldSpace, float3 N, float 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
+ 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;
+ }
+
+ 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
+
+ float inc = 1.0 / 1024.0;
+
+ float shadowFactor = 0;
+ for (int row = -1; row <= 1; row++)
+ {
+ for (int col = -1; col <= 1; col++)
+ {
+ float closestDepth;
+ if (shadowCascadeIndex == 0)
+ {
+ closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy + float2(row, col) * inc).r;
+ }
+ else if (shadowCascadeIndex == 1)
+ {
+ closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy + float2(row, col) * inc).r;
+ }
+ else if (shadowCascadeIndex == 2)
+ {
+ closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy + float2(row, col) * inc).r;
+ }
+ else
+ {
+ closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy + float2(row, col) * inc).r;
+ }
+ shadowFactor += projectionCoords.z - bias > closestDepth ? 1.0 : 0.0;
+ }
+ }
+
+ shadowFactor /= 9.0;
+
+ if (projectionCoords.z > 1.0)
+ {
+ shadowFactor = 1.0;
+ }
+
+ return shadowFactor;
+
+
+ // float currentDepth = projectionCoords.z;
+
+ // if (currentDepth > 1.0)
+ // {
+ // return 0.0;
+ // }
+
+ // if (currentDepth - bias > closestDepth)
+ // {
+ // return 1.0;
+ // }
+ // else
+ // {
+ // return 0.0;
+ // }
+}
+
float3 ComputeLight(
- float3 lightDir,
+ float3 L,
float3 radiance,
float3 F0,
float3 V,
float3 N,
float3 albedo,
float metallic,
- float roughness
+ float roughness,
+ float shadow
) {
- float3 L = normalize(lightDir);
float3 H = normalize(V + L);
float NDF = DistributionGGX(N, H, roughness);
@@ -98,7 +230,7 @@ float3 ComputeLight(
kD *= 1.0 - metallic;
float NdotL = max(dot(N, L), 0.0);
- return (kD * albedo / PI + specular) * radiance * NdotL;
+ return (kD * albedo / PI + specular) * radiance * NdotL * shadow;
}
float4 ComputeColor(
@@ -120,21 +252,20 @@ float4 ComputeColor(
for (int i = 0; i < MAX_POINT_LIGHTS; i++)
{
float3 lightDir = PointLightPositions[i] - worldPosition;
+ float3 L = normalize(lightDir);
float distance = length(lightDir);
float attenuation = 1.0 / (distance * distance);
float3 radiance = PointLightColors[i] * attenuation;
- Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness);
+ Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
}
// directional light
- for (int i = 0; i < MAX_DIRECTIONAL_LIGHTS; i++)
- {
- float3 lightDir = DirectionalLightDirections[i];
- float3 radiance = DirectionalLightColors[i];
+ float3 L = normalize(DirectionalLightDirection);
+ float3 radiance = DirectionalLightColor;
- Lo += ComputeLight(lightDir, radiance, F0, V, N, albedo, metallic, roughness);
- }
+ float shadow = ComputeShadow(worldPosition, N, L);
+ Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow));
float3 ambient = float3(0.03, 0.03, 0.03) * albedo; // * AO;
float3 color = ambient + Lo;
@@ -148,13 +279,13 @@ float4 ComputeColor(
float4 main_ps(PixelInput input) : SV_TARGET0
{
- float3 fragPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
+ 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;
return ComputeColor(
- fragPosition,
+ worldPosition,
normal,
albedo,
metallicRoughness.r,
@@ -166,6 +297,7 @@ Technique DeferredPBR
{
Pass
{
+ VertexShader = compile vs_3_0 main_vs();
PixelShader = compile ps_3_0 main_ps();
}
}
diff --git a/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx
new file mode 100644
index 0000000..4144c8d
--- /dev/null
+++ b/Effects/HLSL/DeferredPBR_AmbientLightEffect.fx
@@ -0,0 +1,54 @@
+#include "Macros.fxh" // from FNA
+
+DECLARE_TEXTURE(gPosition, 0);
+DECLARE_TEXTURE(gAlbedo, 1);
+
+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;
+}
+
+float4 ComputeColor(
+ float3 worldPosition,
+ float3 albedo
+) {
+ float3 color = float3(0.03, 0.03, 0.03) * albedo;
+ return float4(color, 1.0);
+}
+
+float4 main_ps(PixelInput input) : SV_TARGET0
+{
+ float3 worldPosition = SAMPLE_TEXTURE(gPosition, input.TexCoord).rgb;
+ float3 albedo = SAMPLE_TEXTURE(gAlbedo, input.TexCoord).rgb;
+
+ return ComputeColor(
+ worldPosition,
+ albedo
+ );
+}
+
+Technique DeferredPBR_Ambient
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 main_vs();
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx
new file mode 100644
index 0000000..0982521
--- /dev/null
+++ b/Effects/HLSL/DeferredPBR_DirectionalLightEffect.fx
@@ -0,0 +1,252 @@
+#include "Macros.fxh" //from FNA
+#include "Lighting.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(c3) _cb(c3);
+
+ float ShadowMapSize _ps(c7) _cb(c7);
+
+MATRIX_CONSTANTS
+
+ 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(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;
+ 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;
+}
+
+// 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
+ 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;
+ }
+
+ 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);
+ }
+ else if (shadowCascadeIndex == 1)
+ {
+ visibility = PoissonCoord(SAMPLER(shadowMapTwo), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ }
+ else if (shadowCascadeIndex == 2)
+ {
+ visibility = PoissonCoord(SAMPLER(shadowMapThree), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ }
+ else
+ {
+ visibility = PoissonCoord(SAMPLER(shadowMapFour), positionWorldSpace, projectionCoords.xy, projectionCoords.z, bias);
+ }
+
+ return visibility;
+}
+
+float4 ComputeColor(
+ float3 worldPosition,
+ float3 worldNormal,
+ float3 albedo,
+ float metallic,
+ float roughness
+) {
+ float3 V = normalize(EyePosition - worldPosition);
+ float3 N = normalize(worldNormal);
+
+ float3 F0 = float3(0.04, 0.04, 0.04);
+ F0 = lerp(F0, albedo, metallic);
+
+ float3 L = normalize(DirectionalLightDirection);
+ float3 radiance = DirectionalLightColor;
+
+ float shadow = ComputeShadow(worldPosition, N, L);
+ float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, shadow);
+
+ return float4(color, 1.0);
+}
+
+float4 main_ps(PixelInput input) : SV_TARGET0
+{
+ 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;
+
+ return ComputeColor(
+ worldPosition,
+ normal,
+ albedo,
+ metallicRoughness.r,
+ metallicRoughness.g
+ );
+}
+
+Technique DeferredPBR_Directional
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 main_vs();
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/HLSL/DeferredPBR_GBufferEffect.fx b/Effects/HLSL/DeferredPBR_GBufferEffect.fx
index 63cb2ce..f24da19 100644
--- a/Effects/HLSL/DeferredPBR_GBufferEffect.fx
+++ b/Effects/HLSL/DeferredPBR_GBufferEffect.fx
@@ -79,10 +79,10 @@ PixelOutput NonePS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(normalize(input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0);
- output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0);
+ output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output;
}
@@ -91,10 +91,10 @@ PixelOutput AlbedoPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(normalize(input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
- output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0);
+ output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output;
}
@@ -103,8 +103,8 @@ PixelOutput MetallicRoughnessPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(normalize(input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@@ -115,10 +115,10 @@ PixelOutput NormalPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0);
- output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0);
+ output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output;
}
@@ -127,8 +127,8 @@ PixelOutput AlbedoMetallicRoughnessPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(normalize(input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(normalize(input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@@ -139,10 +139,10 @@ PixelOutput AlbedoNormalPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
- output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0);
+ output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 1.0);
return output;
}
@@ -151,8 +151,8 @@ PixelOutput MetallicRoughnessNormalPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = float4(AlbedoValue, 1.0);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
@@ -163,8 +163,8 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input)
{
PixelOutput output;
- output.gPosition = float4(input.PositionWorld, 0.0);
- output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0);
+ output.gPosition = float4(input.PositionWorld, 1.0);
+ output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 1.0);
output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord);
output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord);
diff --git a/Effects/HLSL/DeferredPBR_PointLightEffect.fx b/Effects/HLSL/DeferredPBR_PointLightEffect.fx
new file mode 100644
index 0000000..fba3c6e
--- /dev/null
+++ b/Effects/HLSL/DeferredPBR_PointLightEffect.fx
@@ -0,0 +1,91 @@
+#include "Macros.fxh" //from FNA
+#include "Lighting.fxh"
+
+DECLARE_TEXTURE(gPosition, 0);
+DECLARE_TEXTURE(gAlbedo, 1);
+DECLARE_TEXTURE(gNormal, 2);
+DECLARE_TEXTURE(gMetallicRoughness, 3);
+
+BEGIN_CONSTANTS
+
+ float3 EyePosition _ps(c0) _cb(c0);
+
+ float3 PointLightPosition _ps(c1) _cb(c1);
+ float3 PointLightColor _ps(c2) _cb(c2);
+
+MATRIX_CONSTANTS
+
+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;
+}
+
+// Pixel Shader
+
+float4 ComputeColor(
+ float3 worldPosition,
+ float3 worldNormal,
+ float3 albedo,
+ float metallic,
+ float roughness
+) {
+ float3 V = normalize(EyePosition - worldPosition);
+ float3 N = normalize(worldNormal);
+
+ float3 F0 = float3(0.04, 0.04, 0.04);
+ F0 = lerp(F0, albedo, metallic);
+
+ float3 lightDir = PointLightPosition - worldPosition;
+ float3 L = normalize(lightDir);
+ float distance = length(lightDir);
+ float attenuation = 1.0 / (distance * distance);
+ float3 radiance = PointLightColor * attenuation;
+
+ float3 color = ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, 1.0);
+
+ return float4(color, 1.0);
+}
+
+float4 main_ps(PixelInput input) : SV_TARGET0
+{
+ 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;
+
+ return ComputeColor(
+ worldPosition,
+ normal,
+ albedo,
+ metallicRoughness.r,
+ metallicRoughness.g
+ );
+}
+
+Technique DeferredPBR_Point
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 main_vs();
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/HLSL/Lighting.fxh b/Effects/HLSL/Lighting.fxh
new file mode 100644
index 0000000..e57b391
--- /dev/null
+++ b/Effects/HLSL/Lighting.fxh
@@ -0,0 +1,71 @@
+static const float PI = 3.141592653589793;
+
+float3 FresnelSchlick(float cosTheta, float3 F0)
+{
+ return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
+}
+
+float DistributionGGX(float3 N, float3 H, float roughness)
+{
+ float a = roughness * roughness;
+ float a2 = a * a;
+ float NdotH = max(dot(N, H), 0.0);
+ float NdotH2 = NdotH * NdotH;
+
+ float num = a2;
+ float denom = (NdotH2 * (a2 - 1.0) + 1.0);
+ denom = PI * denom * denom;
+
+ return num / denom;
+}
+
+float GeometrySchlickGGX(float NdotV, float roughness)
+{
+ float r = (roughness + 1.0);
+ float k = (r * r) / 8.0;
+
+ float num = NdotV;
+ float denom = NdotV * (1.0 - k) + k;
+
+ return num / denom;
+}
+
+float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
+{
+ float NdotV = max(dot(N, V), 0.0);
+ float NdotL = max(dot(N, L), 0.0);
+ float ggx2 = GeometrySchlickGGX(NdotV, roughness);
+ float ggx1 = GeometrySchlickGGX(NdotL, roughness);
+
+ return ggx1 * ggx2;
+}
+
+float3 ComputeLight(
+ float3 L,
+ float3 radiance,
+ float3 F0,
+ float3 V,
+ float3 N,
+ float3 albedo,
+ float metallic,
+ float roughness,
+ float visibility
+) {
+ float3 H = normalize(V + L);
+
+ float NDF = DistributionGGX(N, H, roughness);
+ float G = GeometrySmith(N, V, L, roughness);
+ float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0);
+
+ float3 numerator = NDF * G * F;
+ float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0);
+ float3 specular = numerator / max(denominator, 0.001);
+
+ float3 kS = F;
+ float3 kD = float3(1.0, 1.0, 1.0) - kS;
+
+ kD *= 1.0 - metallic;
+
+ float NdotL = max(dot(N, L), 0.0);
+ return (kD * albedo / PI + specular) * radiance * NdotL * visibility;
+}
diff --git a/Effects/HLSL/Macros.fxh b/Effects/HLSL/Macros.fxh
index 91d1702..c0aaaac 100644
--- a/Effects/HLSL/Macros.fxh
+++ b/Effects/HLSL/Macros.fxh
@@ -54,4 +54,5 @@
#define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord)
#define SAMPLE_CUBEMAP(Name, texCoord) texCUBE(Name##Sampler, texCoord)
#define SAMPLE_CUBEMAP_LOD(Name, texCoord) texCUBElod(Name##Sampler, texCoord)
+#define SAMPLER(Name) Name##Sampler
#endif
diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx
index 8b19a4e..3598eb5 100644
--- a/Effects/HLSL/SimpleDepthEffect.fx
+++ b/Effects/HLSL/SimpleDepthEffect.fx
@@ -16,21 +16,29 @@ struct VertexShaderInput
struct VertexShaderOutput
{
float4 Position : SV_Position;
+ float Depth : TEXCOORD0;
};
VertexShaderOutput main_vs(VertexShaderInput input)
{
VertexShaderOutput output;
- output.Position = mul(float4(input.Position.xyz, 1.0), ModelViewProjection);
+ output.Position = mul(input.Position, ModelViewProjection);
+ output.Depth = output.Position.z;
return output;
}
+float4 main_ps(VertexShaderOutput input) : SV_TARGET0
+{
+ return float4(input.Depth, 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/ToneMapEffect.fx b/Effects/HLSL/ToneMapEffect.fx
new file mode 100644
index 0000000..281b714
--- /dev/null
+++ b/Effects/HLSL/ToneMapEffect.fx
@@ -0,0 +1,20 @@
+sampler TextureSampler : register(s0);
+
+float4 main_ps(float2 texCoord : TEXCOORD0) : COLOR0
+{
+ float3 color = tex2D(TextureSampler, texCoord).xyz;
+
+ color = color / (color + float3(1.0, 1.0, 1.0));
+ float exposureConstant = 1.0 / 2.2;
+ color = pow(color, float3(exposureConstant, exposureConstant, exposureConstant));
+
+ return float4(color, 1.0);
+}
+
+Technique DeferredPBR
+{
+ Pass
+ {
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs
index db04a25..2df74cd 100644
--- a/Effects/PBREffect.cs
+++ b/Effects/PBREffect.cs
@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
- public class PBREffect : Effect, TransformEffect, PointLightEffect, DirectionalLightEffect
+ public class PBREffect : Effect, TransformEffect, PointLightEffect
{
EffectParameter worldParam;
EffectParameter worldViewProjectionParam;
@@ -31,7 +31,6 @@ namespace Kav
Matrix view = Matrix.Identity;
Matrix projection = Matrix.Identity;
PointLightCollection pointLightCollection;
- DirectionalLightCollection directionalLightCollection;
Vector3 albedo;
float metallic;
@@ -84,14 +83,6 @@ namespace Kav
private set { pointLightCollection = value; }
}
- public int MaxDirectionalLights { get; } = 4;
-
- public DirectionalLightCollection DirectionalLights
- {
- get { return directionalLightCollection; }
- private set { directionalLightCollection = value; }
- }
-
public Vector3 Albedo
{
get { return albedo; }
@@ -204,11 +195,6 @@ namespace Kav
Parameters["PositionLightColors"],
MaxPointLights
);
-
- directionalLightCollection = new DirectionalLightCollection(
- Parameters["LightDirections"],
- Parameters["DirectionLightColors"]
- );
}
protected PBREffect(PBREffect cloneSource) : base(cloneSource)
@@ -230,16 +216,6 @@ namespace Kav
PointLights[i] = cloneSource.PointLights[i];
}
- DirectionalLights = new DirectionalLightCollection(
- Parameters["LightDirections"],
- Parameters["DirectionLightColors"]
- );
-
- for (int i = 0; i < MaxDirectionalLights; i++)
- {
- DirectionalLights[i] = cloneSource.DirectionalLights[i];
- }
-
AlbedoTexture = cloneSource.AlbedoTexture;
NormalTexture = cloneSource.NormalTexture;
EmissionTexture = cloneSource.EmissionTexture;
diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs
index f68f513..1854e3c 100644
--- a/Effects/SimpleDepthEffect.cs
+++ b/Effects/SimpleDepthEffect.cs
@@ -42,6 +42,7 @@ namespace Kav
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
}
+
public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect)
{
CacheEffectParameters();
@@ -51,8 +52,8 @@ namespace Kav
{
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
- Matrix.Multiply(ref model, ref view, out Matrix modelView);
- Matrix.Multiply(ref modelView, ref projection, out Matrix worldViewProj);
+ Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
+ Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj);
modelViewProjectionParam.SetValue(worldViewProj);
diff --git a/Kav.Core.csproj b/Kav.Core.csproj
index 7957739..327d7bb 100644
--- a/Kav.Core.csproj
+++ b/Kav.Core.csproj
@@ -15,9 +15,21 @@
+
+ Kav.Resources.DeferredPBR_AmbientLightEffect.fxb
+
+
+ Kav.Resources.DeferredPBR_PointLightEffect.fxb
+
+
+ Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb
+
Kav.Resources.DeferredPBR_GBufferEffect.fxb
+
+ Kav.Resources.ToneMapEffect.fxb
+
Kav.Resources.DeferredPBREffect.fxb
diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj
index 0557675..2b2dd4f 100644
--- a/Kav.Framework.csproj
+++ b/Kav.Framework.csproj
@@ -15,9 +15,21 @@
+
+ Kav.Resources.DeferredPBR_AmbientLightEffect.fxb
+
+
+ Kav.Resources.DeferredPBR_PointLightEffect.fxb
+
+
+ Kav.Resources.DeferredPBR_DirectionalLightEffect.fxb
+
Kav.Resources.DeferredPBR_GBufferEffect.fxb
+
+ Kav.Resources.ToneMapEffect.fxb
+
Kav.Resources.DeferredPBREffect.fxb
diff --git a/Lights/DirectionalLight.cs b/Lights/DirectionalLight.cs
index 31ca4fd..a22ab33 100644
--- a/Lights/DirectionalLight.cs
+++ b/Lights/DirectionalLight.cs
@@ -12,7 +12,7 @@ namespace Kav
{
get
{
- return Matrix.CreateLookAt(-Direction * 100f, Vector3.Zero, Vector3.Up);
+ return Matrix.CreateLookAt(Direction * 100f, Vector3.Zero, Vector3.Up);
}
}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8f3190b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+# Kav
+
+A 3D renderer built on top of FNA.
+
+## Roadmap
+
+Essential
+
+- [x] PBR shading
+- [x] Deferred rendering
+- [x] Point lighting
+- [x] Directional lighting
+- [x] Directional shadow maps
+- [x] Cascading shadow maps
+- [x] Tone map shader
+- [x] Poisson soft shadowing
+- [ ] Frustum culling
+- [ ] Shadow-casting point lights
+- [ ] Parabolic lights
+- [ ] Skyboxes
+- [ ] Screen-space reflection
+
+Nice-To-Haves
+
+- [ ] Anti-aliasing
+- [ ] Image-based lighting
+- [ ] Volumetric lighting
+- [ ] Volumetric smoke
+- [ ]
diff --git a/Renderer.cs b/Renderer.cs
index c53f855..63b34e5 100644
--- a/Renderer.cs
+++ b/Renderer.cs
@@ -6,38 +6,84 @@ namespace Kav
{
public class Renderer
{
+ private const int MAX_SHADOW_CASCADES = 4;
+ private int ShadowMapSize { get; }
+
private GraphicsDevice GraphicsDevice { get; }
private int RenderDimensionsX { get; }
private int RenderDimensionsY { get; }
- private RenderTarget2D DepthRenderTarget { get; }
+ private VertexBuffer FullscreenTriangle { get; }
+ private int NumShadowCascades { get; }
+
+ private RenderTarget2D ColorRenderTarget { get; }
+ private RenderTarget2D DirectionalRenderTarget { get; }
+ private RenderTarget2D[] ShadowRenderTargets { get; }
+
+ private DeferredPBREffect DeferredPBREffect { get; }
+ private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; }
+ private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; }
+ private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; }
private SimpleDepthEffect SimpleDepthEffect { get; }
+ private Effect ToneMapEffect { get; }
private RenderTarget2D gPosition { get; }
private RenderTarget2D gNormal { get; }
private RenderTarget2D gAlbedo { get; }
private RenderTarget2D gMetallicRoughness { get; }
- private RenderTarget2D deferredRenderTarget { get; }
private RenderTargetBinding[] GBuffer { get; }
- private DeferredPBREffect DeferredPBREffect { get; }
-
private SpriteBatch SpriteBatch { get; }
- public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY)
- {
+ public Renderer(
+ GraphicsDevice graphicsDevice,
+ int renderDimensionsX,
+ int renderDimensionsY,
+ int numShadowCascades,
+ int shadowMapSize
+ ) {
GraphicsDevice = graphicsDevice;
RenderDimensionsX = renderDimensionsX;
RenderDimensionsY = renderDimensionsY;
- DepthRenderTarget = new RenderTarget2D(
- GraphicsDevice,
+ ShadowMapSize = shadowMapSize;
+
+ NumShadowCascades = (int)MathHelper.Clamp(numShadowCascades, 1, MAX_SHADOW_CASCADES);
+ ShadowRenderTargets = new RenderTarget2D[numShadowCascades];
+
+ for (var i = 0; i < numShadowCascades; i++)
+ {
+ ShadowRenderTargets[i] = new RenderTarget2D(
+ GraphicsDevice,
+ ShadowMapSize,
+ ShadowMapSize,
+ false,
+ SurfaceFormat.Single,
+ DepthFormat.Depth24
+ );
+ }
+
+ ColorRenderTarget = new RenderTarget2D(
+ graphicsDevice,
renderDimensionsX,
renderDimensionsY,
false,
- SurfaceFormat.HalfSingle, // unused
- DepthFormat.Depth24
+ SurfaceFormat.Color,
+ DepthFormat.None,
+ 0,
+ RenderTargetUsage.PreserveContents
+ );
+
+ DirectionalRenderTarget = new RenderTarget2D(
+ graphicsDevice,
+ renderDimensionsX,
+ renderDimensionsY,
+ false,
+ SurfaceFormat.Color,
+ DepthFormat.None,
+ 0,
+ RenderTargetUsage.PreserveContents
);
gPosition = new RenderTarget2D(
@@ -82,29 +128,33 @@ namespace Kav
new RenderTargetBinding(gAlbedo),
new RenderTargetBinding(gMetallicRoughness)
};
-
- deferredRenderTarget = new RenderTarget2D(
- GraphicsDevice,
- renderDimensionsX,
- renderDimensionsY
- );
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(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);
- SpriteBatch = new SpriteBatch(GraphicsDevice);
+ 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)),
+ new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)),
+ new VertexPositionTexture(new Vector3(3, 1, 0), new Vector2(2, 0))
+ });
- GraphicsDevice.SetRenderTarget(deferredRenderTarget);
- graphicsDevice.Clear(Color.White);
- GraphicsDevice.SetRenderTarget(null);
+ SpriteBatch = new SpriteBatch(graphicsDevice);
}
public void DeferredRender(
- Camera camera,
+ PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable pointLights,
- IEnumerable directionalLights
+ DirectionalLight directionalLight
) {
+ // g-buffer pass
+
GraphicsDevice.SetRenderTargets(GBuffer);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
@@ -143,53 +193,186 @@ namespace Kav
}
}
- GraphicsDevice.SetRenderTarget(null);
- GraphicsDevice.Clear(Color.CornflowerBlue);
+ GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.Clear(Color.Black);
+ GraphicsDevice.BlendState = BlendState.Additive;
+ GraphicsDevice.DepthStencilState = DepthStencilState.None;
- DeferredPBREffect.GPosition = gPosition;
- DeferredPBREffect.GAlbedo = gAlbedo;
- DeferredPBREffect.GNormal = gNormal;
- DeferredPBREffect.GMetallicRoughness = gMetallicRoughness;
- DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation;
+ DeferredAmbientLightEffect.GPosition = gPosition;
+ DeferredAmbientLightEffect.GAlbedo = gAlbedo;
+
+ foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+ GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
+ GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
+ }
+
+ DeferredPointLightEffect.EyePosition = camera.Position;
- int i = 0;
foreach (var pointLight in pointLights)
{
- if (i > DeferredPBREffect.MaxPointLights) { break; }
- DeferredPBREffect.PointLights[i] = pointLight;
- i++;
+ PointLightRender(pointLight);
}
- i = 0;
- foreach (var directionalLight in directionalLights)
- {
- if (i > DeferredPBREffect.MaxDirectionalLights) { break; }
- DeferredPBREffect.DirectionalLights[i] = directionalLight;
- i++;
- }
+ DirectionalLightRender(camera, modelTransforms, directionalLight);
+ // return;
+ // GraphicsDevice.SetRenderTarget(null);
+ // SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied);
+ // SpriteBatch.Draw(DirectionalRenderTarget, Vector2.Zero, Color.White);
+ // SpriteBatch.End();
- SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect);
- SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White);
+ 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 Render(
- Camera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- IEnumerable pointLights,
- IEnumerable directionalLights
- ) {
- Render(camera.View, camera.Projection, modelTransforms, pointLights, directionalLights);
+ private void PointLightRender(PointLight pointLight)
+ {
+ DeferredPointLightEffect.GPosition = gPosition;
+ DeferredPointLightEffect.GAlbedo = gAlbedo;
+ DeferredPointLightEffect.GNormal = gNormal;
+ DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
+
+ DeferredPointLightEffect.PointLightPosition = pointLight.Position;
+ DeferredPointLightEffect.PointLightColor =
+ pointLight.Color.ToVector3() * pointLight.Intensity;
+
+ foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+ GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
+ GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
+ }
}
- // for shadow mapping
- public void DepthRender(IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight)
- {
- GraphicsDevice.SetRenderTarget(DepthRenderTarget);
- GraphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1, 0);
+ private void DirectionalLightRender(
+ PerspectiveCamera camera,
+ 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));
- SimpleDepthEffect.View = directionalLight.View;
- SimpleDepthEffect.Projection = directionalLight.Projection;
+ // 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.GAlbedo = gAlbedo;
+ DeferredDirectionalLightEffect.GNormal = gNormal;
+ DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness;
+
+ DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0];
+ if (NumShadowCascades > 1)
+ {
+ DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1];
+ }
+ if (NumShadowCascades > 2)
+ {
+ DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2];
+ }
+ if (NumShadowCascades > 3)
+ {
+ DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3];
+ }
+
+ DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction;
+ DeferredDirectionalLightEffect.DirectionalLightColor =
+ directionalLight.Color.ToVector3() * directionalLight.Intensity;
+
+ DeferredDirectionalLightEffect.ViewMatrix = camera.View;
+ DeferredDirectionalLightEffect.EyePosition = Matrix.Invert(camera.View).Translation;
+
+ GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.BlendState = BlendState.Additive;
+
+ foreach (EffectPass pass in DeferredDirectionalLightEffect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+ GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
+ GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
+ }
+ }
+
+ private void RenderShadowMap(
+ PerspectiveCamera camera,
+ IEnumerable<(Model, Matrix)> modelTransforms,
+ DirectionalLight directionalLight,
+ int shadowCascadeIndex
+ ) {
+ GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
+ GraphicsDevice.Clear(Color.White);
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
+ Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners();
+
+ Vector3 frustumCenter = Vector3.Zero;
+ for (var i = 0; i < frustumCorners.Length; i++)
+ {
+ frustumCenter += frustumCorners[i];
+ }
+ frustumCenter /= 8f;
+
+ var lightView = Matrix.CreateLookAt(frustumCenter + directionalLight.Direction, frustumCenter, Vector3.Backward);
+
+ for (var i = 0; i < frustumCorners.Length; i++)
+ {
+ frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView);
+ }
+
+ BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);
+
+ SimpleDepthEffect.View = lightView;
+ SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(
+ lightBox.Min.X,
+ lightBox.Max.X,
+ lightBox.Min.Y,
+ lightBox.Max.Y,
+ -lightBox.Max.Z - 10f, // TODO: near clip plane needs scene AABB info to get rid of this magic value
+ -lightBox.Min.Z
+ );
+
+ var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection;
+
+ if (shadowCascadeIndex == 0)
+ {
+ DeferredDirectionalLightEffect.LightSpaceMatrixOne = lightSpaceMatrix;
+ }
+ else if (shadowCascadeIndex == 1)
+ {
+ DeferredDirectionalLightEffect.LightSpaceMatrixTwo = lightSpaceMatrix;
+ }
+ else if (shadowCascadeIndex == 2)
+ {
+ DeferredDirectionalLightEffect.LightSpaceMatrixThree = lightSpaceMatrix;
+ }
+ else if (shadowCascadeIndex == 3)
+ {
+ DeferredDirectionalLightEffect.LightSpaceMatrixFour = lightSpaceMatrix;
+ }
+
+ DeferredDirectionalLightEffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane;
foreach (var (model, transform) in modelTransforms)
{
@@ -220,6 +403,15 @@ 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,
@@ -254,17 +446,6 @@ namespace Kav
}
}
- if (meshPart.Effect is DirectionalLightEffect directionalLightEffect)
- {
- int i = 0;
- foreach (var directionalLight in directionalLights)
- {
- if (i > directionalLightEffect.MaxDirectionalLights) { break; }
- directionalLightEffect.DirectionalLights[i] = directionalLight;
- i++;
- }
- }
-
foreach (var pass in meshPart.Effect.CurrentTechnique.Passes)
{
pass.Apply();
diff --git a/Resources.cs b/Resources.cs
index 83d4b04..7791eaa 100644
--- a/Resources.cs
+++ b/Resources.cs
@@ -4,6 +4,41 @@ namespace Kav
{
internal class Resources
{
+ public static byte[] DeferredPBR_AmbientLightEffect
+ {
+ get
+ {
+ if (ambientLightEffect == null)
+ {
+ ambientLightEffect = GetResource("DeferredPBR_AmbientLightEffect");
+ }
+ return ambientLightEffect;
+ }
+ }
+ public static byte[] DeferredPBR_PointLightEffect
+ {
+ get
+ {
+ if (pointLightEffect == null)
+ {
+ pointLightEffect = GetResource("DeferredPBR_PointLightEffect");
+ }
+ return pointLightEffect;
+ }
+ }
+
+ public static byte[] DeferredPBR_DirectionalLightEffect
+ {
+ get
+ {
+ if (directionalLightEffect == null)
+ {
+ directionalLightEffect = GetResource("DeferredPBR_DirectionalLightEffect");
+ }
+ return directionalLightEffect;
+ }
+ }
+
public static byte[] DeferredPBR_GBufferEffect
{
get
@@ -16,6 +51,18 @@ namespace Kav
}
}
+ public static byte[] ToneMapEffect
+ {
+ get
+ {
+ if (toneMapEffect == null)
+ {
+ toneMapEffect = GetResource("ToneMapEffect");
+ }
+ return toneMapEffect;
+ }
+ }
+
public static byte[] DeferredPBREffect
{
get
@@ -52,7 +99,11 @@ namespace Kav
}
}
+ private static byte[] ambientLightEffect;
+ private static byte[] pointLightEffect;
+ private static byte[] directionalLightEffect;
private static byte[] gBufferEffect;
+ private static byte[] toneMapEffect;
private static byte[] deferredPBREffect;
private static byte[] pbrEffect;
private static byte[] simpleDepthEffect;