diff --git a/DirectionalShadowMapData.cs b/DirectionalShadowMapData.cs
new file mode 100644
index 0000000..455d7d1
--- /dev/null
+++ b/DirectionalShadowMapData.cs
@@ -0,0 +1,59 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class DirectionalShadowMapData
+ {
+ public static readonly int MAX_SHADOW_CASCADES = 4;
+
+ public RenderTarget2D[] ShadowMaps { get; }
+
+ public Matrix[] LightSpaceViews { get; }
+ public Matrix[] LightSpaceProjections { get; }
+
+ public float[] CascadeFarPlanes { get; }
+
+ public int ShadowMapSize { get; }
+ public int NumShadowCascades { get; }
+
+ internal DirectionalShadowMapData(
+ GraphicsDevice graphicsDevice,
+ int shadowMapSize,
+ int numCascades
+ ) {
+ ShadowMapSize = shadowMapSize;
+ NumShadowCascades = (int)MathHelper.Clamp(numCascades, 1, MAX_SHADOW_CASCADES);
+
+ LightSpaceViews = new Matrix[4];
+ LightSpaceProjections = new Matrix[4];
+
+ ShadowMaps = new RenderTarget2D[NumShadowCascades];
+
+ for (var i = 0; i < NumShadowCascades; i++)
+ {
+ ShadowMaps[i] = new RenderTarget2D(
+ graphicsDevice,
+ shadowMapSize,
+ shadowMapSize,
+ false,
+ SurfaceFormat.Single,
+ DepthFormat.Depth24,
+ 0,
+ RenderTargetUsage.PreserveContents
+ );
+ }
+
+ CascadeFarPlanes = new float[MAX_SHADOW_CASCADES];
+ }
+
+ public void Clear(GraphicsDevice graphicsDevice)
+ {
+ foreach (var shadowMap in ShadowMaps)
+ {
+ graphicsDevice.SetRenderTarget(shadowMap);
+ graphicsDevice.Clear(Color.White);
+ }
+ }
+ }
+}
diff --git a/EffectInterfaces/IHasTranslation.cs b/EffectInterfaces/IHasTranslation.cs
new file mode 100644
index 0000000..969cfdb
--- /dev/null
+++ b/EffectInterfaces/IHasTranslation.cs
@@ -0,0 +1,9 @@
+using Microsoft.Xna.Framework;
+
+namespace Kav
+{
+ public interface IHasTranslation
+ {
+ Vector3 Translation { get; set; }
+ }
+}
diff --git a/EffectInterfaces/IHasWorldMatrix.cs b/EffectInterfaces/IHasWorldMatrix.cs
new file mode 100644
index 0000000..e7d2bae
--- /dev/null
+++ b/EffectInterfaces/IHasWorldMatrix.cs
@@ -0,0 +1,9 @@
+using Microsoft.Xna.Framework;
+
+namespace Kav
+{
+ public interface IHasWorldMatrix
+ {
+ Matrix World { get; set; }
+ }
+}
diff --git a/Effects/DeferredPBR_GBufferEffect.cs b/Effects/DeferredPBR_GBufferEffect.cs
index 26a9c8f..335a592 100644
--- a/Effects/DeferredPBR_GBufferEffect.cs
+++ b/Effects/DeferredPBR_GBufferEffect.cs
@@ -6,8 +6,7 @@ namespace Kav
public class DeferredPBR_GBufferEffect : Effect, TransformEffect
{
EffectParameter worldParam;
- EffectParameter worldViewProjectionParam;
- EffectParameter worldInverseTransposeParam;
+ EffectParameter viewProjectionParam;
EffectParameter albedoTextureParam;
EffectParameter normalTextureParam;
@@ -17,6 +16,7 @@ namespace Kav
EffectParameter metallicParam;
EffectParameter roughnessParam;
+ EffectParameter vertexShaderIndexParam;
EffectParameter shaderIndexParam;
Matrix world = Matrix.Identity;
@@ -30,6 +30,7 @@ namespace Kav
bool albedoTextureEnabled = false;
bool metallicRoughnessMapEnabled = false;
bool normalMapEnabled = false;
+ bool hardwareInstancingEnabled = false;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
@@ -39,7 +40,7 @@ namespace Kav
set
{
world = value;
- dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj;
+ dirtyFlags |= EffectDirtyFlags.World;
}
}
@@ -49,7 +50,7 @@ namespace Kav
set
{
view = value;
- dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition;
+ dirtyFlags |= EffectDirtyFlags.ViewProj | EffectDirtyFlags.EyePosition;
}
}
@@ -59,7 +60,7 @@ namespace Kav
set
{
projection = value;
- dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ dirtyFlags |= EffectDirtyFlags.ViewProj;
}
}
@@ -100,7 +101,7 @@ namespace Kav
{
albedoTextureParam.SetValue(value);
albedoTextureEnabled = value != null;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
@@ -111,7 +112,7 @@ namespace Kav
{
normalTextureParam.SetValue(value);
normalMapEnabled = value != null;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
@@ -122,7 +123,20 @@ namespace Kav
{
metallicRoughnessTextureParam.SetValue(value);
metallicRoughnessMapEnabled = value != null;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
+ }
+ }
+
+ public bool HardwareInstancingEnabled
+ {
+ get { return hardwareInstancingEnabled; }
+ set
+ {
+ if (value != hardwareInstancingEnabled)
+ {
+ hardwareInstancingEnabled = value;
+ dirtyFlags |= EffectDirtyFlags.VertexShaderIndex;
+ }
}
}
@@ -159,30 +173,30 @@ namespace Kav
{
worldParam.SetValue(world);
- Matrix.Invert(ref world, out Matrix worldInverse);
- Matrix.Transpose(ref worldInverse, out Matrix worldInverseTranspose);
- worldInverseTransposeParam.SetValue(worldInverseTranspose);
-
dirtyFlags &= ~EffectDirtyFlags.World;
}
- if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
+ if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0)
{
- Matrix.Multiply(ref world, ref view, out Matrix worldView);
- Matrix.Multiply(ref worldView, ref projection, out Matrix worldViewProj);
- worldViewProjectionParam.SetValue(worldViewProj);
+ Matrix.Multiply(ref view, ref projection, out Matrix viewProj);
+ viewProjectionParam.SetValue(viewProj);
- dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
+ dirtyFlags &= ~EffectDirtyFlags.ViewProj;
}
- if ((dirtyFlags & EffectDirtyFlags.EyePosition) != 0)
+ if ((dirtyFlags & EffectDirtyFlags.VertexShaderIndex) != 0)
{
- Matrix.Invert(ref view, out Matrix inverseView);
+ int vertexShaderIndex = 0;
- dirtyFlags &= ~EffectDirtyFlags.EyePosition;
+ if (hardwareInstancingEnabled)
+ {
+ vertexShaderIndex = 1;
+ }
+
+ vertexShaderIndexParam.SetValue(vertexShaderIndex);
}
- if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
+ if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{
int shaderIndex = 0;
@@ -217,15 +231,14 @@ namespace Kav
shaderIndexParam.SetValue(shaderIndex);
- dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
+ dirtyFlags &= ~EffectDirtyFlags.PixelShaderIndex;
}
}
void CacheEffectParameters()
{
worldParam = Parameters["World"];
- worldViewProjectionParam = Parameters["WorldViewProjection"];
- worldInverseTransposeParam = Parameters["WorldInverseTranspose"];
+ viewProjectionParam = Parameters["ViewProjection"];
albedoTextureParam = Parameters["AlbedoTexture"];
normalTextureParam = Parameters["NormalTexture"];
@@ -235,7 +248,8 @@ namespace Kav
metallicParam = Parameters["MetallicValue"];
roughnessParam = Parameters["RoughnessValue"];
- shaderIndexParam = Parameters["ShaderIndex"];
+ shaderIndexParam = Parameters["PixelShaderIndex"];
+ vertexShaderIndexParam = Parameters["VertexShaderIndex"];
}
}
}
diff --git a/Effects/DiffuseLitSpriteEffect.cs b/Effects/DiffuseLitSpriteEffect.cs
index faf05b3..ae8618c 100644
--- a/Effects/DiffuseLitSpriteEffect.cs
+++ b/Effects/DiffuseLitSpriteEffect.cs
@@ -40,7 +40,7 @@ namespace Kav
if (normalMapEnabled != value)
{
normalMapEnabled = value;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
}
@@ -142,7 +142,7 @@ namespace Kav
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
- if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
+ if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{
int shaderIndex = 0;
diff --git a/Effects/EffectHelpers.cs b/Effects/EffectHelpers.cs
index 5a23251..56a0cec 100644
--- a/Effects/EffectHelpers.cs
+++ b/Effects/EffectHelpers.cs
@@ -8,7 +8,9 @@ namespace Kav
WorldViewProj = 1,
World = 2,
EyePosition = 4,
- ShaderIndex = 8,
+ VertexShaderIndex = 8,
+ PixelShaderIndex = 16,
+ ViewProj = 32,
All = -1
}
}
diff --git a/Effects/FXB/DeferredPBR_GBufferEffect.fxb b/Effects/FXB/DeferredPBR_GBufferEffect.fxb
index 3aff2ad..2012ecb 100644
--- a/Effects/FXB/DeferredPBR_GBufferEffect.fxb
+++ b/Effects/FXB/DeferredPBR_GBufferEffect.fxb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1208a4d715117867d4bd2b877526b526eadd5b47bf99c6e8dfbf2d8d3487478c
-size 7476
+oid sha256:240a1d0911f6bba0da34d5a158c5868e52f6612dc055cdc42d8cef2a5f3febaa
+size 8908
diff --git a/Effects/FXB/SimpleDepthEffect.fxb b/Effects/FXB/SimpleDepthEffect.fxb
index 8281157..fee70bc 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:e0984ae92245afe3e2bda5fab0135293b5121c3ee7b5f146e7d4ddd1684ee74b
-size 848
+oid sha256:41083a1789c08363517ab1279e80e985e8fcfc3439411d13df4a5ea4667491e9
+size 1340
diff --git a/Effects/FXB/SimpleDepthEffectInstanced.fxb b/Effects/FXB/SimpleDepthEffectInstanced.fxb
new file mode 100644
index 0000000..9c19b29
--- /dev/null
+++ b/Effects/FXB/SimpleDepthEffectInstanced.fxb
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f5eb937af7db2fd9a149ae06981af2a33043abaa5fbb72c8382b4b72097f98f
+size 1008
diff --git a/Effects/HLSL/DeferredPBR_GBufferEffect.fx b/Effects/HLSL/DeferredPBR_GBufferEffect.fx
index f24da19..52f911c 100644
--- a/Effects/HLSL/DeferredPBR_GBufferEffect.fx
+++ b/Effects/HLSL/DeferredPBR_GBufferEffect.fx
@@ -13,8 +13,7 @@ BEGIN_CONSTANTS
MATRIX_CONSTANTS
float4x4 World _vs(c0) _cb(c7);
- float4x4 WorldInverseTranspose _vs(c4) _cb(c11);
- float4x4 WorldViewProjection _vs(c8) _cb(c15);
+ float4x4 ViewProjection _vs(c4) _cb(c11);
END_CONSTANTS
@@ -22,7 +21,7 @@ struct VertexInput
{
float4 Position : POSITION;
float3 Normal : NORMAL;
- float2 TexCoord : TEXCOORD0;
+ float2 TexCoord : TEXCOORD;
};
struct PixelInput
@@ -48,9 +47,33 @@ PixelInput main_vs(VertexInput input)
PixelInput output;
output.PositionWorld = mul(input.Position, World).xyz;
- output.NormalWorld = mul(input.Normal, (float3x3)WorldInverseTranspose).xyz;
+ output.NormalWorld = normalize(mul(input.Normal, World));
output.TexCoord = input.TexCoord;
- output.Position = mul(input.Position, WorldViewProjection);
+
+ float4x4 worldViewProjection = mul(World, ViewProjection);
+ output.Position = mul(input.Position, worldViewProjection);
+
+ return output;
+}
+
+PixelInput instanced_vs(VertexInput input, float3 Translation : TEXCOORD2)
+{
+ PixelInput output;
+
+ float4x4 world = float4x4(
+ float4(1, 0, 0, 0),
+ float4(0, 1, 0, 0),
+ float4(0, 0, 1, 0),
+ float4(Translation.x, Translation.y, Translation.z, 1)
+ );
+
+ float4x4 worldViewProjection = mul(world, ViewProjection);
+
+ output.PositionWorld = mul(input.Position, world);
+ output.NormalWorld = mul(input.Normal, world);
+ output.TexCoord = input.TexCoord;
+
+ output.Position = mul(input.Position, worldViewProjection);
return output;
}
@@ -171,6 +194,19 @@ PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input)
return output;
}
+VertexShader VSArray[2] =
+{
+ compile vs_3_0 main_vs(),
+ compile vs_3_0 instanced_vs()
+};
+
+int VSIndices[2] =
+{
+ 0, 1
+};
+
+int VertexShaderIndex = 0;
+
PixelShader PSArray[8] =
{
compile ps_3_0 NonePS(),
@@ -191,13 +227,13 @@ int PSIndices[8] =
0, 1, 2, 3, 4, 5, 6, 7
};
-int ShaderIndex = 0;
+int PixelShaderIndex = 0;
Technique GBuffer
{
Pass
{
- VertexShader = compile vs_3_0 main_vs();
- PixelShader = (PSArray[PSIndices[ShaderIndex]]);
+ VertexShader = (VSArray[VSIndices[VertexShaderIndex]]);
+ PixelShader = (PSArray[PSIndices[PixelShaderIndex]]);
}
}
diff --git a/Effects/HLSL/SimpleDepthEffect.fx b/Effects/HLSL/SimpleDepthEffect.fx
index f813bd7..a4c3bbc 100644
--- a/Effects/HLSL/SimpleDepthEffect.fx
+++ b/Effects/HLSL/SimpleDepthEffect.fx
@@ -2,10 +2,11 @@
BEGIN_CONSTANTS
- float4x4 ModelViewProjection _vs(c0) _cb(c0);
-
MATRIX_CONSTANTS
+ float4x4 World _vs(c0) _cb(c0);
+ float4x4 ViewProjection _vs(c4) _cb(c4);
+
END_CONSTANTS
struct VertexShaderInput
@@ -15,15 +16,16 @@ struct VertexShaderInput
struct VertexShaderOutput
{
- float4 Position : SV_Position;
- float Depth : TEXCOORD0;
+ float4 Position : SV_POSITION;
+ float Depth : DEPTH;
};
VertexShaderOutput main_vs(VertexShaderInput input)
{
VertexShaderOutput output;
- output.Position = mul(input.Position, ModelViewProjection);
+ float4x4 worldViewProjection = mul(World, ViewProjection);
+ output.Position = mul(input.Position, worldViewProjection);
output.Depth = output.Position.z / output.Position.w;
return output;
diff --git a/Effects/HLSL/SimpleDepthEffectInstanced.fx b/Effects/HLSL/SimpleDepthEffectInstanced.fx
new file mode 100644
index 0000000..f35471e
--- /dev/null
+++ b/Effects/HLSL/SimpleDepthEffectInstanced.fx
@@ -0,0 +1,45 @@
+float4x4 ViewProjection;
+
+struct VertexShaderInput
+{
+ float4 Position : POSITION;
+};
+
+struct VertexShaderOutput
+{
+ float4 Position : POSITION0;
+ float4 ProjectedPosition : TEXCOORD0;
+};
+
+VertexShaderOutput instanced_vs(VertexShaderInput input, float3 Translation : TEXCOORD2)
+{
+ VertexShaderOutput output;
+
+ float4x4 world = float4x4(
+ float4(1, 0, 0, 0),
+ float4(0, 1, 0, 0),
+ float4(0, 0, 1, 0),
+ float4(Translation.x, Translation.y, Translation.z, 1)
+ );
+
+ float4x4 worldViewProjection = mul(world, ViewProjection);
+
+ output.Position = mul(input.Position, worldViewProjection);
+ output.ProjectedPosition = output.Position;
+
+ return output;
+}
+
+float4 main_ps(VertexShaderOutput input) : COLOR0
+{
+ return float4(input.ProjectedPosition.z / input.ProjectedPosition.w, 0.0, 0.0, 0.0);
+}
+
+Technique SimpleDepth
+{
+ Pass
+ {
+ VertexShader = compile vs_3_0 instanced_vs();
+ PixelShader = compile ps_3_0 main_ps();
+ }
+}
diff --git a/Effects/LinearDepthEffect.cs b/Effects/LinearDepthEffect.cs
index 2a6a76a..5b62977 100644
--- a/Effects/LinearDepthEffect.cs
+++ b/Effects/LinearDepthEffect.cs
@@ -3,28 +3,28 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
- public class LinearDepthEffect : Effect
+ public class LinearDepthEffect : Effect, IHasWorldMatrix
{
- EffectParameter modelParam;
- EffectParameter modelViewProjectionParam;
+ EffectParameter worldParam;
+ EffectParameter worldViewProjectionParam;
EffectParameter lightPositionParam;
EffectParameter farPlaneParam;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
- Matrix model;
+ Matrix world;
Matrix view;
Matrix projection;
public Vector3 LightPosition { get; set; }
public float FarPlane { get; set; }
- public Matrix Model
+ public Matrix World
{
- get { return model; }
+ get { return world; }
set
{
- model = value;
+ world = value;
dirtyFlags |= EffectDirtyFlags.World;
dirtyFlags |= EffectDirtyFlags.WorldViewProj;
}
@@ -60,16 +60,16 @@ namespace Kav
if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
{
Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
- Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj);
+ Matrix.Multiply(ref world, ref viewProjection, out Matrix worldViewProj);
- modelViewProjectionParam.SetValue(worldViewProj);
+ worldViewProjectionParam.SetValue(worldViewProj);
dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.World) != 0)
{
- modelParam.SetValue(model);
+ worldParam.SetValue(world);
dirtyFlags &= ~EffectDirtyFlags.World;
}
@@ -80,8 +80,8 @@ namespace Kav
private void CacheEffectParameters()
{
- modelParam = Parameters["Model"];
- modelViewProjectionParam = Parameters["ModelViewProjection"];
+ worldParam = Parameters["Model"];
+ worldViewProjectionParam = Parameters["ModelViewProjection"];
lightPositionParam = Parameters["LightPosition"];
farPlaneParam = Parameters["FarPlane"];
diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs
index 2df74cd..4b80ecb 100644
--- a/Effects/PBREffect.cs
+++ b/Effects/PBREffect.cs
@@ -130,7 +130,7 @@ namespace Kav
{
albedoTextureParam.SetValue(value);
albedoTextureEnabled = value != null;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
@@ -141,7 +141,7 @@ namespace Kav
{
normalTextureParam.SetValue(value);
normalMapEnabled = value != null;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
@@ -164,7 +164,7 @@ namespace Kav
{
metallicRoughnessTextureParam.SetValue(value);
metallicRoughnessMapEnabled = value != null;
- dirtyFlags |= EffectDirtyFlags.ShaderIndex;
+ dirtyFlags |= EffectDirtyFlags.PixelShaderIndex;
}
}
@@ -266,7 +266,7 @@ namespace Kav
dirtyFlags &= ~EffectDirtyFlags.EyePosition;
}
- if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0)
+ if ((dirtyFlags & EffectDirtyFlags.PixelShaderIndex) != 0)
{
int shaderIndex = 0;
@@ -301,7 +301,7 @@ namespace Kav
shaderIndexParam.SetValue(shaderIndex);
- dirtyFlags &= ~EffectDirtyFlags.ShaderIndex;
+ dirtyFlags &= ~EffectDirtyFlags.PixelShaderIndex;
}
}
diff --git a/Effects/SimpleDepthEffect.cs b/Effects/SimpleDepthEffect.cs
index 1854e3c..2020c11 100644
--- a/Effects/SimpleDepthEffect.cs
+++ b/Effects/SimpleDepthEffect.cs
@@ -3,23 +3,24 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
- public class SimpleDepthEffect : Effect
+ public class SimpleDepthEffect : Effect, IHasWorldMatrix
{
- EffectParameter modelViewProjectionParam;
+ EffectParameter worldParam;
+ EffectParameter viewProjectionParam;
- Matrix model;
+ Matrix world;
Matrix view;
Matrix projection;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
- public Matrix Model
+ public Matrix World
{
- get { return model; }
+ get { return world; }
set
{
- model = value;
- dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ world = value;
+ dirtyFlags |= EffectDirtyFlags.World;
}
}
@@ -29,7 +30,7 @@ namespace Kav
set
{
view = value;
- dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ dirtyFlags |= EffectDirtyFlags.ViewProj;
}
}
@@ -39,7 +40,7 @@ namespace Kav
set
{
projection = value;
- dirtyFlags |= EffectDirtyFlags.WorldViewProj;
+ dirtyFlags |= EffectDirtyFlags.ViewProj;
}
}
@@ -50,20 +51,26 @@ namespace Kav
protected override void OnApply()
{
- if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0)
+ if ((dirtyFlags & EffectDirtyFlags.World) != 0)
+ {
+ worldParam.SetValue(world);
+
+ dirtyFlags &= ~EffectDirtyFlags.World;
+ }
+
+ if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0)
{
Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
- Matrix.Multiply(ref model, ref viewProjection, out Matrix worldViewProj);
+ viewProjectionParam.SetValue(viewProjection);
- modelViewProjectionParam.SetValue(worldViewProj);
-
- dirtyFlags &= ~EffectDirtyFlags.WorldViewProj;
+ dirtyFlags &= ~EffectDirtyFlags.ViewProj;
}
}
private void CacheEffectParameters()
{
- modelViewProjectionParam = Parameters["ModelViewProjection"];
+ worldParam = Parameters["World"];
+ viewProjectionParam = Parameters["ViewProjection"];
}
}
}
diff --git a/Effects/SimpleDepthEffectInstanced.cs b/Effects/SimpleDepthEffectInstanced.cs
new file mode 100644
index 0000000..52fc597
--- /dev/null
+++ b/Effects/SimpleDepthEffectInstanced.cs
@@ -0,0 +1,56 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public class SimpleDepthEffectInstanced : Effect
+ {
+ EffectParameter viewProjectionParam;
+
+ Matrix view;
+ Matrix projection;
+
+ EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
+
+ public Matrix View
+ {
+ get { return view; }
+ set
+ {
+ view = value;
+ dirtyFlags |= EffectDirtyFlags.ViewProj;
+ }
+ }
+
+ public Matrix Projection
+ {
+ get { return projection; }
+ set
+ {
+ projection = value;
+ dirtyFlags |= EffectDirtyFlags.ViewProj;
+ }
+ }
+
+ public SimpleDepthEffectInstanced(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffectInstanced)
+ {
+ CacheEffectParameters();
+ }
+
+ protected override void OnApply()
+ {
+ if ((dirtyFlags & EffectDirtyFlags.ViewProj) != 0)
+ {
+ Matrix.Multiply(ref view, ref projection, out Matrix viewProjection);
+ viewProjectionParam.SetValue(viewProjection);
+
+ dirtyFlags &= ~EffectDirtyFlags.ViewProj;
+ }
+ }
+
+ private void CacheEffectParameters()
+ {
+ viewProjectionParam = Parameters["ViewProjection"];
+ }
+ }
+}
diff --git a/Geometry/Interfaces/IGBufferDrawable.cs b/Geometry/Interfaces/IGBufferDrawable.cs
new file mode 100644
index 0000000..bf84905
--- /dev/null
+++ b/Geometry/Interfaces/IGBufferDrawable.cs
@@ -0,0 +1,16 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public interface IGBufferDrawable
+ {
+ Vector3 Albedo { get; }
+ float Metallic { get; }
+ float Roughness { get; }
+
+ Texture2D AlbedoTexture { get; }
+ Texture2D NormalTexture { get; }
+ Texture2D MetallicRoughnessTexture { get; }
+ }
+}
diff --git a/Geometry/Interfaces/IHasVertexPositions.cs b/Geometry/Interfaces/IHasVertexPositions.cs
new file mode 100644
index 0000000..e39483b
--- /dev/null
+++ b/Geometry/Interfaces/IHasVertexPositions.cs
@@ -0,0 +1,9 @@
+using Microsoft.Xna.Framework;
+
+namespace Kav
+{
+ public interface IHasVertexPositions
+ {
+ Vector3[] Positions { get; }
+ }
+}
diff --git a/Geometry/Interfaces/IIndexDrawable.cs b/Geometry/Interfaces/IIndexDrawable.cs
new file mode 100644
index 0000000..f59f8d4
--- /dev/null
+++ b/Geometry/Interfaces/IIndexDrawable.cs
@@ -0,0 +1,10 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public interface IIndexDrawable
+ {
+ VertexBuffer VertexBuffer { get; }
+ IndexBuffer IndexBuffer { get; }
+ }
+}
diff --git a/Geometry/MeshPart.cs b/Geometry/MeshPart.cs
index 25cb93c..80f52de 100644
--- a/Geometry/MeshPart.cs
+++ b/Geometry/MeshPart.cs
@@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
- public class MeshPart
+ public class MeshPart : IIndexDrawable, IGBufferDrawable, ICullable, IHasVertexPositions
{
public IndexBuffer IndexBuffer { get; }
public VertexBuffer VertexBuffer { get; }
@@ -11,27 +11,27 @@ namespace Kav
public Vector3[] Positions { get; }
public BoundingBox BoundingBox { get; }
-
+
private Texture2D albedoTexture = null;
private Texture2D normalTexture = null;
private Texture2D metallicRoughnessTexture = null;
- public Texture2D AlbedoTexture
- {
+ public Texture2D AlbedoTexture
+ {
get { return DisableAlbedoMap ? null : albedoTexture; }
set { albedoTexture = value; }
}
- public Texture2D NormalTexture
- {
- get { return DisableNormalMap ? null : normalTexture; }
+ public Texture2D NormalTexture
+ {
+ get { return DisableNormalMap ? null : normalTexture; }
set { normalTexture = value; }
}
- public Texture2D MetallicRoughnessTexture
- {
+ public Texture2D MetallicRoughnessTexture
+ {
get { return DisableMetallicRoughnessMap ? null : metallicRoughnessTexture; }
- set { metallicRoughnessTexture = value; }
+ set { metallicRoughnessTexture = value; }
}
public Vector3 Albedo { get; set; } = Vector3.One;
diff --git a/Geometry/MeshSprite.cs b/Geometry/MeshSprite.cs
index ac50ab7..dce9752 100644
--- a/Geometry/MeshSprite.cs
+++ b/Geometry/MeshSprite.cs
@@ -4,7 +4,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
- public class MeshSprite : ICullable
+ public class MeshSprite : ICullable, IIndexDrawable
{
private static readonly int PixelScale = 40;
private static readonly short[] Indices = new short[]
diff --git a/Kav.Core.csproj b/Kav.Core.csproj
index 9cb9564..6077186 100644
--- a/Kav.Core.csproj
+++ b/Kav.Core.csproj
@@ -43,6 +43,9 @@
Kav.Resources.SimpleDepthEffect.fxb
+
+ Kav.Resources.SimpleDepthEffectInstanced.fxb
+
Kav.Resources.LinearDepthEffect.fxb
diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj
index d5c6c57..60a7849 100644
--- a/Kav.Framework.csproj
+++ b/Kav.Framework.csproj
@@ -43,6 +43,9 @@
Kav.Resources.SimpleDepthEffect.fxb
+
+ Kav.Resources.SimpleDepthEffectInstanced.fxb
+
Kav.Resources.LinearDepthEffect.fxb
diff --git a/Renderer.cs b/Renderer.cs
index 08c673e..5eabb74 100644
--- a/Renderer.cs
+++ b/Renderer.cs
@@ -8,19 +8,11 @@ namespace Kav
{
public class Renderer
{
- private const int MAX_SHADOW_CASCADES = 4;
- private int ShadowMapSize { get; }
+ private const int MAX_INSTANCE_VERTEX_COUNT = 1000000;
private GraphicsDevice GraphicsDevice { get; }
- private int RenderDimensionsX { get; }
- private int RenderDimensionsY { 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; }
/* FIXME: these next two dont actually have anything to do with PBR */
@@ -30,125 +22,24 @@ namespace Kav
private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; }
private Deferred_ToonEffect Deferred_ToonEffect { get; }
private SimpleDepthEffect SimpleDepthEffect { get; }
+ private SimpleDepthEffectInstanced SimpleDepthEffectInstanced { get; }
private LinearDepthEffect LinearDepthEffect { get; }
private Effect ToneMapEffect { get; }
private SkyboxEffect SkyboxEffect { get; }
private DiffuseLitSpriteEffect DiffuseLitSpriteEffect { 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; }
+ private DynamicVertexBuffer PositionInstanceVertexBuffer { get; }
+ private readonly PositionInstanceVertex[] PositionInstanceVertices = new PositionInstanceVertex[MAX_INSTANCE_VERTEX_COUNT];
public Renderer(
- GraphicsDevice graphicsDevice,
- int renderDimensionsX,
- int renderDimensionsY,
- int numShadowCascades,
- int shadowMapSize
+ GraphicsDevice graphicsDevice
) {
GraphicsDevice = graphicsDevice;
- RenderDimensionsX = renderDimensionsX;
- RenderDimensionsY = renderDimensionsY;
-
- 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.Color,
- DepthFormat.Depth24,
- 0,
- RenderTargetUsage.PreserveContents
- );
-
- DirectionalRenderTarget = new RenderTarget2D(
- graphicsDevice,
- renderDimensionsX,
- renderDimensionsY,
- false,
- SurfaceFormat.Color,
- DepthFormat.None,
- 0,
- RenderTargetUsage.PreserveContents
- );
-
- gPosition = new RenderTarget2D(
- GraphicsDevice,
- renderDimensionsX,
- renderDimensionsY,
- false,
- SurfaceFormat.Vector4,
- DepthFormat.Depth24
- );
-
- gNormal = new RenderTarget2D(
- GraphicsDevice,
- renderDimensionsX,
- renderDimensionsY,
- false,
- SurfaceFormat.Vector4,
- DepthFormat.None
- );
-
- gAlbedo = new RenderTarget2D(
- GraphicsDevice,
- renderDimensionsX,
- renderDimensionsY,
- false,
- SurfaceFormat.Color,
- DepthFormat.None
- );
-
- gMetallicRoughness = new RenderTarget2D(
- GraphicsDevice,
- renderDimensionsX,
- renderDimensionsY,
- false,
- SurfaceFormat.HalfVector2,
- DepthFormat.None
- );
-
- GBuffer = new RenderTargetBinding[4] {
- new RenderTargetBinding(gPosition),
- new RenderTargetBinding(gNormal),
- new RenderTargetBinding(gAlbedo),
- new RenderTargetBinding(gMetallicRoughness)
- };
-
- PointShadowCubeMap = new RenderTargetCube(
- GraphicsDevice,
- shadowMapSize,
- false,
- SurfaceFormat.Single,
- DepthFormat.Depth24
- );
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
+ SimpleDepthEffectInstanced = new SimpleDepthEffectInstanced(GraphicsDevice);
LinearDepthEffect = new LinearDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
@@ -156,7 +47,6 @@ namespace Kav
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);
@@ -174,77 +64,39 @@ namespace Kav
Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel))
);
- SpriteBatch = new SpriteBatch(graphicsDevice);
+ PositionInstanceVertexBuffer = new DynamicVertexBuffer(
+ GraphicsDevice,
+ VertexDeclarations.PositionInstanceDeclaration,
+ MAX_INSTANCE_VERTEX_COUNT,
+ BufferUsage.WriteOnly
+ );
}
- public void DeferredRender(
- RenderTarget2D renderTarget,
- PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- AmbientLight ambientLight,
- IEnumerable pointLights,
- DirectionalLight? directionalLight
+ public static RenderTargetCube CreateShadowCubeMap(
+ GraphicsDevice graphicsDevice,
+ int shadowMapSize
) {
- GBufferRender(camera, modelTransforms);
-
- GraphicsDevice.SetRenderTarget(ColorRenderTarget);
- GraphicsDevice.Clear(Color.Black);
-
- AmbientLightRender(ambientLight);
-
- DeferredPointLightEffect.EyePosition = camera.Position;
-
- foreach (var pointLight in pointLights)
- {
- PointLightRender(camera, modelTransforms, pointLight);
- }
-
- if (directionalLight.HasValue)
- {
- DirectionalLightRender(camera, modelTransforms, directionalLight.Value);
- }
-
- GraphicsDevice.SetRenderTarget(renderTarget);
- SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, ToneMapEffect);
- SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
- SpriteBatch.End();
+ return new RenderTargetCube(
+ graphicsDevice,
+ shadowMapSize,
+ false,
+ SurfaceFormat.Single,
+ DepthFormat.Depth24,
+ 0,
+ RenderTargetUsage.PreserveContents
+ );
}
- public void DeferredToonRender(
- RenderTarget2D renderTarget,
- PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- AmbientLight ambientLight,
- IEnumerable pointLights,
- DirectionalLight? directionalLight,
- TextureCube skybox
+ public static DirectionalShadowMapData CreateDirectionalShadowMaps(
+ GraphicsDevice graphicsDevice,
+ int shadowMapSize,
+ int numCascades
) {
- GBufferRender(camera, modelTransforms);
-
- GraphicsDevice.SetRenderTarget(ColorRenderTarget);
- GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
- GraphicsDevice.DepthStencilState = DepthStencilState.Default;
-
- DepthRender(camera, modelTransforms);
- GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
-
- AmbientLightRender(ambientLight);
- foreach (var pointLight in pointLights)
- {
- PointLightRender(camera, modelTransforms, pointLight);
- }
-
- if (directionalLight.HasValue)
- {
- DirectionalLightToonRender(camera, modelTransforms, directionalLight.Value);
- }
-
- SkyboxRender(camera, skybox);
-
- GraphicsDevice.SetRenderTarget(renderTarget);
- SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, null, null, null, null);
- SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
- SpriteBatch.End();
+ return new DirectionalShadowMapData(
+ graphicsDevice,
+ shadowMapSize,
+ numCascades
+ );
}
// TODO: we could make this a lot more efficient probably
@@ -252,18 +104,12 @@ namespace Kav
public void MeshSpriteRender(
RenderTarget2D renderTarget,
PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<(MeshSprite, Matrix)> meshSpriteTransforms,
AmbientLight ambientLight,
IEnumerable pointLights,
DirectionalLight? directionalLight
) {
- GraphicsDevice.SetRenderTarget(ColorRenderTarget);
- GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
- GraphicsDevice.DepthStencilState = DepthStencilState.Default;
-
- DepthRender(camera, modelTransforms);
- GraphicsDevice.Clear(ClearOptions.Target, new Color(0, 0, 0, 0), 1f, 0);
+ GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
@@ -348,143 +194,115 @@ namespace Kav
);
}
}
-
- GraphicsDevice.SetRenderTarget(renderTarget);
- GraphicsDevice.Clear(new Color(0, 0, 0, 0));
- SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null);
- SpriteBatch.Draw(ColorRenderTarget, Vector2.Zero, Color.White);
- SpriteBatch.End();
}
- private void DepthRender(
- PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms
- ) {
- var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
-
- foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
+ // Renders a series of drawable-transform pairs using an effect that has a World matrix.
+ // Effect must be pre-configured!!
+ public static void CullAndRenderIndexed(
+ GraphicsDevice graphicsDevice,
+ BoundingFrustum boundingFrustum,
+ IEnumerable<(T, Matrix)> drawableTransformPairs,
+ U effect
+ ) where T : IIndexDrawable, ICullable where U : Effect, IHasWorldMatrix
+ {
+ foreach (var (drawable, transform) in FrustumCull(boundingFrustum, drawableTransformPairs))
{
- foreach (var modelMesh in model.Meshes)
- {
- foreach (var meshPart in modelMesh.MeshParts)
- {
- SimpleDepthEffect.Model = transform;
- SimpleDepthEffect.View = camera.View;
- SimpleDepthEffect.Projection = camera.Projection;
+ effect.World = transform;
- GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
- GraphicsDevice.Indices = meshPart.IndexBuffer;
-
- foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes)
- {
- pass.Apply();
-
- GraphicsDevice.DrawIndexedPrimitives(
- PrimitiveType.TriangleList,
- 0,
- 0,
- meshPart.VertexBuffer.VertexCount,
- 0,
- meshPart.Triangles.Length
- );
- }
- }
- }
+ RenderIndexed(
+ graphicsDevice,
+ drawable,
+ effect
+ );
}
}
- private void SkyboxRender(
- PerspectiveCamera camera,
- TextureCube skybox
- ) {
- GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace;
- SkyboxEffect.Skybox = skybox;
+ public static void RenderIndexed(
+ GraphicsDevice graphicsDevice,
+ T drawable,
+ U effect
+ ) where T : IIndexDrawable where U : Effect
+ {
+ graphicsDevice.SetVertexBuffer(drawable.VertexBuffer);
+ graphicsDevice.Indices = drawable.IndexBuffer;
- 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)
+ foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
- GraphicsDevice.DrawIndexedPrimitives(
+ graphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
- UnitCube.Meshes[0].MeshParts[0].VertexBuffer.VertexCount,
+ drawable.VertexBuffer.VertexCount,
0,
- UnitCube.Meshes[0].MeshParts[0].Triangles.Length
+ drawable.IndexBuffer.IndexCount / 3
);
}
- 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;
-
- var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
-
- foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
+ public static int FillAndSetBuffersForInstancing(
+ GraphicsDevice graphicsDevice,
+ BoundingFrustum boundingFrustum,
+ T drawable,
+ IEnumerable transforms,
+ V[] vertexData,
+ DynamicVertexBuffer dynamicVertexBuffer
+ ) where T : ICullable, IIndexDrawable where V : struct, IVertexType, IHasTranslation
+ {
+ int numInstances = 0;
+ foreach (var transform in FrustumCull(boundingFrustum, drawable, transforms))
{
- 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;
+ vertexData[numInstances].Translation = transform.Translation;
+ numInstances += 1;
+ }
- Deferred_GBufferEffect.Albedo = meshPart.Albedo;
- Deferred_GBufferEffect.Metallic = meshPart.Metallic;
- Deferred_GBufferEffect.Roughness = meshPart.Roughness;
+ if (numInstances == 0) { return 0; }
- Deferred_GBufferEffect.AlbedoTexture = meshPart.AlbedoTexture;
- Deferred_GBufferEffect.NormalTexture = meshPart.NormalTexture;
- Deferred_GBufferEffect.MetallicRoughnessTexture = meshPart.MetallicRoughnessTexture;
+ dynamicVertexBuffer.SetData(
+ vertexData,
+ 0,
+ numInstances,
+ SetDataOptions.Discard
+ );
- GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
- GraphicsDevice.Indices = meshPart.IndexBuffer;
+ graphicsDevice.SetVertexBuffers(
+ drawable.VertexBuffer,
+ new VertexBufferBinding(dynamicVertexBuffer, 0, 1)
+ );
+ graphicsDevice.Indices = drawable.IndexBuffer;
- foreach (var pass in Deferred_GBufferEffect.CurrentTechnique.Passes)
- {
- pass.Apply();
+ return numInstances;
+ }
- GraphicsDevice.DrawIndexedPrimitives(
- PrimitiveType.TriangleList,
- 0,
- 0,
- meshPart.VertexBuffer.VertexCount,
- 0,
- meshPart.Triangles.Length
- );
- }
- }
- }
+ public static void RenderInstanced(
+ GraphicsDevice graphicsDevice,
+ T drawable,
+ U effect,
+ int numInstances
+ ) where T : ICullable, IIndexDrawable where U : Effect
+ {
+ foreach (var pass in effect.CurrentTechnique.Passes)
+ {
+ pass.Apply();
+
+ graphicsDevice.DrawInstancedPrimitives(
+ PrimitiveType.TriangleList,
+ 0,
+ 0,
+ drawable.VertexBuffer.VertexCount,
+ 0,
+ drawable.IndexBuffer.IndexCount / 3,
+ numInstances
+ );
}
}
- private void AmbientLightRender(AmbientLight ambientLight)
- {
- GraphicsDevice.SetRenderTarget(ColorRenderTarget);
- GraphicsDevice.BlendState = BlendState.Opaque;
-
- DeferredAmbientLightEffect.GPosition = gPosition;
- DeferredAmbientLightEffect.GAlbedo = gAlbedo;
- DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3();
-
- foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes)
+ // TODO: can probably make this static somehow
+ public void RenderFullscreenEffect(
+ Effect effect
+ ) {
+ foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
@@ -492,14 +310,170 @@ namespace Kav
}
}
- private void PointLightRender(
+ public void RenderDepthIndexed(
+ RenderTarget2D renderTarget,
+ PerspectiveCamera camera,
+ IEnumerable<(T, Matrix)> drawableTransforms
+ ) where T : ICullable, IIndexDrawable
+ {
+ GraphicsDevice.SetRenderTarget(renderTarget);
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+
+ SimpleDepthEffect.View = camera.View;
+ SimpleDepthEffect.Projection = camera.Projection;
+
+ CullAndRenderIndexed(
+ GraphicsDevice,
+ new BoundingFrustum(camera.View * camera.Projection),
+ drawableTransforms,
+ SimpleDepthEffect
+ );
+ }
+
+ public void RenderSkybox(
+ RenderTarget2D renderTarget,
+ PerspectiveCamera camera,
+ TextureCube skybox
+ ) {
+ GraphicsDevice.SetRenderTarget(renderTarget);
+ GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace;
+ GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+
+ SkyboxEffect.Skybox = skybox;
+
+ var view = camera.View;
+ view.Translation = Vector3.Zero;
+
+ SkyboxEffect.View = view;
+ SkyboxEffect.Projection = camera.Projection;
+
+ RenderIndexed(
+ GraphicsDevice,
+ UnitCube.Meshes[0].MeshParts[0],
+ SkyboxEffect
+ );
+
+ GraphicsDevice.RasterizerState.CullMode = CullMode.CullCounterClockwiseFace;
+ }
+
+ ///
+ /// GBuffer binding must have 4 render targets.
+ ///
+ public void RenderGBufferInstanced(
+ RenderTargetBinding[] gBuffer,
+ RenderTarget2D depthBuffer,
+ PerspectiveCamera camera,
+ T drawable,
+ IEnumerable transforms
+ ) where T : ICullable, IIndexDrawable, IGBufferDrawable {
+ GraphicsDevice.SetRenderTargets(gBuffer);
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ Deferred_GBufferEffect.HardwareInstancingEnabled = true;
+
+ Deferred_GBufferEffect.Albedo = drawable.Albedo;
+ Deferred_GBufferEffect.Metallic = drawable.Metallic;
+ Deferred_GBufferEffect.Roughness = drawable.Roughness;
+
+ Deferred_GBufferEffect.AlbedoTexture = drawable.AlbedoTexture;
+ Deferred_GBufferEffect.NormalTexture = drawable.NormalTexture;
+ Deferred_GBufferEffect.MetallicRoughnessTexture = drawable.MetallicRoughnessTexture;
+
+ Deferred_GBufferEffect.View = camera.View;
+ Deferred_GBufferEffect.Projection = camera.Projection;
+
+ var numInstances = FillAndSetBuffersForInstancing(
+ GraphicsDevice,
+ new BoundingFrustum(camera.View * camera.Projection),
+ drawable,
+ transforms,
+ PositionInstanceVertices,
+ PositionInstanceVertexBuffer
+ );
+
+ RenderInstanced(
+ GraphicsDevice,
+ drawable,
+ Deferred_GBufferEffect,
+ numInstances
+ );
+
+ // re-render to get depth
+ GraphicsDevice.SetRenderTargets(depthBuffer);
+
+ SimpleDepthEffectInstanced.View = camera.View;
+ SimpleDepthEffectInstanced.Projection = camera.Projection;
+
+ RenderInstanced(
+ GraphicsDevice,
+ drawable,
+ SimpleDepthEffectInstanced,
+ numInstances
+ );
+ }
+
+ public void RenderGBufferIndexed(
+ RenderTargetBinding[] gBuffer,
+ PerspectiveCamera camera,
+ IEnumerable<(T, Matrix)> drawableTransforms
+ ) where T : ICullable, IIndexDrawable, IGBufferDrawable {
+ GraphicsDevice.SetRenderTargets(gBuffer);
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ Deferred_GBufferEffect.HardwareInstancingEnabled = false;
+ Deferred_GBufferEffect.View = camera.View;
+ Deferred_GBufferEffect.Projection = camera.Projection;
+
+ var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
+
+ foreach (var (drawable, transform) in FrustumCull(boundingFrustum, drawableTransforms))
+ {
+ Deferred_GBufferEffect.World = transform;
+
+ Deferred_GBufferEffect.HardwareInstancingEnabled = false;
+
+ Deferred_GBufferEffect.Albedo = drawable.Albedo;
+ Deferred_GBufferEffect.Metallic = drawable.Metallic;
+ Deferred_GBufferEffect.Roughness = drawable.Roughness;
+
+ Deferred_GBufferEffect.AlbedoTexture = drawable.AlbedoTexture;
+ Deferred_GBufferEffect.NormalTexture = drawable.NormalTexture;
+ Deferred_GBufferEffect.MetallicRoughnessTexture = drawable.MetallicRoughnessTexture;
+
+ RenderIndexed(GraphicsDevice, drawable, Deferred_GBufferEffect);
+ }
+ }
+
+ public void RenderAmbientLight(
+ RenderTarget2D renderTarget,
+ Texture2D gPosition,
+ Texture2D gAlbedo,
+ AmbientLight ambientLight
+ ) {
+ GraphicsDevice.SetRenderTarget(renderTarget);
+ GraphicsDevice.BlendState = BlendState.Opaque;
+ GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+
+ DeferredAmbientLightEffect.GPosition = gPosition;
+ DeferredAmbientLightEffect.GAlbedo = gAlbedo;
+ DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3();
+
+ RenderFullscreenEffect(DeferredAmbientLightEffect);
+ }
+
+ public void RenderPointLight(
+ RenderTarget2D renderTarget,
+ Texture2D gPosition,
+ Texture2D gAlbedo,
+ Texture2D gNormal,
+ Texture2D gMetallicRoughness,
+ TextureCube shadowMap,
PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
PointLight pointLight
) {
- RenderPointShadows(camera, modelTransforms, pointLight);
-
- GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
@@ -507,7 +481,9 @@ namespace Kav
DeferredPointLightEffect.GAlbedo = gAlbedo;
DeferredPointLightEffect.GNormal = gNormal;
DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
- DeferredPointLightEffect.ShadowMap = PointShadowCubeMap;
+ DeferredPointLightEffect.ShadowMap = shadowMap;
+
+ DeferredPointLightEffect.EyePosition = camera.Position;
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
DeferredPointLightEffect.PointLightColor =
@@ -515,38 +491,51 @@ namespace Kav
DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value
- foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes)
- {
- pass.Apply();
- GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
- GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
- }
+ RenderFullscreenEffect(DeferredPointLightEffect);
}
- private void DirectionalLightRender(
+ public void RenderDirectionalLight(
+ RenderTarget2D renderTarget,
+ Texture2D gPosition,
+ Texture2D gAlbedo,
+ Texture2D gNormal,
+ Texture2D gMetallicRoughness,
+ DirectionalShadowMapData shadowMapData,
PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight
) {
- RenderDirectionalShadows(camera, modelTransforms, directionalLight, DeferredDirectionalLightEffect);
+ GraphicsDevice.SetRenderTarget(renderTarget);
+ GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+ GraphicsDevice.BlendState = BlendState.Additive;
DeferredDirectionalLightEffect.GPosition = gPosition;
DeferredDirectionalLightEffect.GAlbedo = gAlbedo;
DeferredDirectionalLightEffect.GNormal = gNormal;
DeferredDirectionalLightEffect.GMetallicRoughness = gMetallicRoughness;
- DeferredDirectionalLightEffect.ShadowMapOne = ShadowRenderTargets[0];
- if (NumShadowCascades > 1)
+ DeferredDirectionalLightEffect.ShadowMapSize = shadowMapData.ShadowMapSize;
+
+ DeferredDirectionalLightEffect.ShadowMapOne = shadowMapData.ShadowMaps[0];
+ DeferredDirectionalLightEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceViews[0] * shadowMapData.LightSpaceProjections[0];
+ DeferredDirectionalLightEffect.CascadeFarPlanes[0] = shadowMapData.CascadeFarPlanes[0];
+
+ if (shadowMapData.NumShadowCascades > 1)
{
- DeferredDirectionalLightEffect.ShadowMapTwo = ShadowRenderTargets[1];
+ DeferredDirectionalLightEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1];
+ DeferredDirectionalLightEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceViews[1] * shadowMapData.LightSpaceProjections[1];
+ DeferredDirectionalLightEffect.CascadeFarPlanes[1] = shadowMapData.CascadeFarPlanes[1];
}
- if (NumShadowCascades > 2)
+ if (shadowMapData.NumShadowCascades > 2)
{
- DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2];
+ DeferredDirectionalLightEffect.ShadowMapThree = shadowMapData.ShadowMaps[2];
+ DeferredDirectionalLightEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceViews[2] * shadowMapData.LightSpaceProjections[2];
+ DeferredDirectionalLightEffect.CascadeFarPlanes[2] = shadowMapData.CascadeFarPlanes[2];
}
- if (NumShadowCascades > 3)
+ if (shadowMapData.NumShadowCascades > 3)
{
- DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3];
+ DeferredDirectionalLightEffect.ShadowMapFour = shadowMapData.ShadowMaps[3];
+ DeferredDirectionalLightEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceViews[3] * shadowMapData.LightSpaceProjections[3];
+ DeferredDirectionalLightEffect.CascadeFarPlanes[3] = shadowMapData.CascadeFarPlanes[3];
}
DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction;
@@ -556,25 +545,21 @@ namespace Kav
DeferredDirectionalLightEffect.ViewMatrix = camera.View;
DeferredDirectionalLightEffect.EyePosition = camera.Position;
- 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);
- }
+ RenderFullscreenEffect(DeferredDirectionalLightEffect);
}
- private void DirectionalLightToonRender(
+ public void RenderDirectionalLightToon(
+ RenderTarget2D renderTarget,
+ Texture2D gPosition,
+ Texture2D gAlbedo,
+ Texture2D gNormal,
+ Texture2D gMetallicRoughness,
+ DirectionalShadowMapData shadowMapData,
PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- DirectionalLight directionalLight
+ DirectionalLight directionalLight,
+ bool ditheredShadows
) {
- RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect);
-
- GraphicsDevice.SetRenderTarget(ColorRenderTarget);
+ GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
@@ -583,7 +568,7 @@ namespace Kav
Deferred_ToonEffect.GNormal = gNormal;
Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness;
- Deferred_ToonEffect.DitheredShadows = false;
+ Deferred_ToonEffect.DitheredShadows = ditheredShadows;
Deferred_ToonEffect.EyePosition = camera.Position;
@@ -591,41 +576,39 @@ namespace Kav
Deferred_ToonEffect.DirectionalLightColor =
directionalLight.Color.ToVector3() * directionalLight.Intensity;
- Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0];
- if (NumShadowCascades > 1)
+ Deferred_ToonEffect.ShadowMapOne = shadowMapData.ShadowMaps[0];
+ Deferred_ToonEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceViews[0] * shadowMapData.LightSpaceProjections[0];
+
+ if (shadowMapData.NumShadowCascades > 1)
{
- Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1];
+ Deferred_ToonEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1];
+ Deferred_ToonEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceViews[1] * shadowMapData.LightSpaceProjections[1];
}
- if (NumShadowCascades > 2)
+ if (shadowMapData.NumShadowCascades > 2)
{
- Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2];
+ Deferred_ToonEffect.ShadowMapThree = shadowMapData.ShadowMaps[2];
+ Deferred_ToonEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceViews[1] * shadowMapData.LightSpaceProjections[2];
}
- if (NumShadowCascades > 3)
+ if (shadowMapData.NumShadowCascades > 3)
{
- Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3];
+ Deferred_ToonEffect.ShadowMapFour = shadowMapData.ShadowMaps[3];
+ Deferred_ToonEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceViews[2] * shadowMapData.LightSpaceProjections[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);
- }
+ RenderFullscreenEffect(Deferred_ToonEffect);
}
- private void RenderDirectionalShadows(
+ public void PrepareDirectionalShadowData(
+ DirectionalShadowMapData shadowMapData,
PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- DirectionalLight directionalLight,
- ShadowCascadeEffect effect
+ DirectionalLight directionalLight
) {
- // render the individual shadow cascades
var previousFarPlane = camera.NearPlane;
- for (var i = 0; i < NumShadowCascades; i++)
+ for (var i = 0; i < shadowMapData.NumShadowCascades; i++)
{
- var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f));
+ var farPlane = camera.FarPlane / (MathHelper.Max((shadowMapData.NumShadowCascades - i - 1) * 2f, 1f));
// divide the view frustum
var shadowCamera = new PerspectiveCamera(
@@ -638,27 +621,25 @@ namespace Kav
farPlane
);
- // TODO: This is tightly coupled to the effect and it sucks
- RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
+ PrepareDirectionalShadowCascade(
+ shadowMapData,
+ i,
+ shadowCamera,
+ directionalLight
+ );
- effect.CascadeFarPlanes[i] = farPlane;
+ shadowMapData.CascadeFarPlanes[i] = farPlane;
previousFarPlane = farPlane;
}
}
- private void RenderDirectionalShadowMap(
- PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- DirectionalLight directionalLight,
- ShadowCascadeEffect effect,
- int shadowCascadeIndex
+ private void PrepareDirectionalShadowCascade(
+ DirectionalShadowMapData shadowMapData,
+ int shadowCascadeIndex,
+ PerspectiveCamera shadowCamera,
+ DirectionalLight directionalLight
) {
- GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
- GraphicsDevice.Clear(Color.White);
- GraphicsDevice.DepthStencilState = DepthStencilState.Default;
- GraphicsDevice.BlendState = BlendState.Opaque;
-
- var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
+ var cameraBoundingFrustum = new BoundingFrustum(shadowCamera.View * shadowCamera.Projection);
Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners();
Vector3 frustumCenter = Vector3.Zero;
@@ -677,8 +658,8 @@ namespace Kav
BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);
- SimpleDepthEffect.View = lightView;
- SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(
+ shadowMapData.LightSpaceViews[shadowCascadeIndex] = lightView;
+ shadowMapData.LightSpaceProjections[shadowCascadeIndex] = Matrix.CreateOrthographicOffCenter(
lightBox.Min.X,
lightBox.Max.X,
lightBox.Min.Y,
@@ -686,77 +667,104 @@ namespace Kav
-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)
+ public void RenderDirectionalShadowsIndexed(
+ DirectionalShadowMapData shadowMapData,
+ IEnumerable<(T, Matrix)> drawableTransforms
+ ) where T : ICullable, IIndexDrawable {
+ // render the individual shadow cascades
+ for (var i = 0; i < shadowMapData.NumShadowCascades; i++)
{
- effect.LightSpaceMatrixOne = lightSpaceMatrix;
- }
- else if (shadowCascadeIndex == 1)
- {
- effect.LightSpaceMatrixTwo = lightSpaceMatrix;
- }
- else if (shadowCascadeIndex == 2)
- {
- effect.LightSpaceMatrixThree = lightSpaceMatrix;
- }
- else if (shadowCascadeIndex == 3)
- {
- effect.LightSpaceMatrixFour = lightSpaceMatrix;
- }
-
- var boundingFrustum = new BoundingFrustum(lightSpaceMatrix);
-
- foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
- {
- foreach (var modelMesh in model.Meshes)
- {
- foreach (var meshPart in modelMesh.MeshParts)
- {
- GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
- GraphicsDevice.Indices = meshPart.IndexBuffer;
-
- SimpleDepthEffect.Model = transform;
-
- foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes)
- {
- pass.Apply();
-
- GraphicsDevice.DrawIndexedPrimitives(
- PrimitiveType.TriangleList,
- 0,
- 0,
- meshPart.VertexBuffer.VertexCount,
- 0,
- meshPart.Triangles.Length
- );
- }
- }
- }
+ RenderDirectionalShadowMapIndexed(
+ shadowMapData,
+ i,
+ drawableTransforms
+ );
}
}
- private void RenderPointShadows(
- PerspectiveCamera camera,
- IEnumerable<(Model, Matrix)> modelTransforms,
- PointLight pointLight
- ) {
+ private void RenderDirectionalShadowMapIndexed(
+ DirectionalShadowMapData shadowMapData,
+ int shadowCascadeIndex,
+ IEnumerable<(T, Matrix)> drawableTransforms
+ ) where T : ICullable, IIndexDrawable {
+ GraphicsDevice.SetRenderTarget(shadowMapData.ShadowMaps[shadowCascadeIndex]);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
- LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
- MathHelper.PiOver2,
- 1,
- 0.1f,
- 25f // FIXME: magic value
+ SimpleDepthEffect.View = shadowMapData.LightSpaceViews[shadowCascadeIndex];
+ SimpleDepthEffect.Projection = shadowMapData.LightSpaceProjections[shadowCascadeIndex];
+
+ CullAndRenderIndexed(
+ GraphicsDevice,
+ new BoundingFrustum(SimpleDepthEffect.View * SimpleDepthEffect.Projection),
+ drawableTransforms,
+ SimpleDepthEffect
);
- LinearDepthEffect.FarPlane = 25f;
- LinearDepthEffect.LightPosition = pointLight.Position;
+ }
+
+ public void RenderDirectionalShadowsInstanced(
+ DirectionalShadowMapData shadowMapData,
+ T drawable,
+ IEnumerable transforms
+ ) where T : ICullable, IIndexDrawable
+ {
+ // render the individual shadow cascades
+ for (var i = 0; i < shadowMapData.NumShadowCascades; i++)
+ {
+ RenderDirectionalShadowMapInstanced(
+ shadowMapData,
+ i,
+ drawable,
+ transforms
+ );
+ }
+ }
+
+ private void RenderDirectionalShadowMapInstanced(
+ DirectionalShadowMapData shadowMapData,
+ int shadowCascadeIndex,
+ T drawable,
+ IEnumerable transforms
+ ) where T : ICullable, IIndexDrawable
+ {
+ GraphicsDevice.SetRenderTarget(shadowMapData.ShadowMaps[shadowCascadeIndex]);
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ SimpleDepthEffectInstanced.View = shadowMapData.LightSpaceViews[shadowCascadeIndex];
+ SimpleDepthEffectInstanced.Projection = shadowMapData.LightSpaceProjections[shadowCascadeIndex];
+
+ var numInstances = FillAndSetBuffersForInstancing(
+ GraphicsDevice,
+ new BoundingFrustum(SimpleDepthEffectInstanced.View * SimpleDepthEffectInstanced.Projection),
+ drawable,
+ transforms,
+ PositionInstanceVertices,
+ PositionInstanceVertexBuffer
+ );
+
+ RenderInstanced(
+ GraphicsDevice,
+ drawable,
+ SimpleDepthEffectInstanced,
+ numInstances
+ );
+ }
+
+ public void RenderPointShadowMapIndexed(
+ RenderTargetCube pointShadowCubeMap,
+ PerspectiveCamera camera,
+ IEnumerable<(T, Matrix)> modelTransforms,
+ PointLight pointLight
+ ) where T : ICullable, IIndexDrawable {
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
{
- GraphicsDevice.SetRenderTarget(PointShadowCubeMap, face);
+ GraphicsDevice.SetRenderTarget(pointShadowCubeMap, face);
Vector3 targetDirection;
Vector3 targetUpDirection;
@@ -803,36 +811,110 @@ namespace Kav
pointLight.Position + targetDirection,
targetUpDirection
);
+ LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
+ MathHelper.PiOver2,
+ 1,
+ 0.1f,
+ 25f // FIXME: magic value
+ );
+ LinearDepthEffect.FarPlane = 25f;
- var boundingFrustum = new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection);
+ LinearDepthEffect.LightPosition = pointLight.Position;
- foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
+ CullAndRenderIndexed(
+ GraphicsDevice,
+ new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection),
+ modelTransforms,
+ LinearDepthEffect
+ );
+ }
+ }
+
+ public void RenderPointShadowMapInstanced(
+ RenderTargetCube pointShadowCubeMap,
+ PerspectiveCamera camera,
+ T drawable,
+ IEnumerable modelTransforms,
+ PointLight pointLight
+ ) where T : ICullable, IIndexDrawable
+ {
+ GraphicsDevice.DepthStencilState = DepthStencilState.Default;
+ GraphicsDevice.BlendState = BlendState.Opaque;
+
+ foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
+ {
+ GraphicsDevice.SetRenderTarget(pointShadowCubeMap, face);
+
+ Vector3 targetDirection;
+ Vector3 targetUpDirection;
+ switch(face)
{
- foreach (var modelMesh in model.Meshes)
- {
- foreach (var meshPart in modelMesh.MeshParts)
- {
- GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
- GraphicsDevice.Indices = meshPart.IndexBuffer;
+ case CubeMapFace.PositiveX:
+ targetDirection = Vector3.Right;
+ targetUpDirection = Vector3.Up;
+ break;
- LinearDepthEffect.Model = transform;
+ case CubeMapFace.NegativeX:
+ targetDirection = Vector3.Left;
+ targetUpDirection = Vector3.Up;
+ break;
- foreach (var pass in LinearDepthEffect.CurrentTechnique.Passes)
- {
- pass.Apply();
+ case CubeMapFace.PositiveY:
+ targetDirection = Vector3.Up;
+ targetUpDirection = Vector3.Forward;
+ break;
- GraphicsDevice.DrawIndexedPrimitives(
- PrimitiveType.TriangleList,
- 0,
- 0,
- meshPart.VertexBuffer.VertexCount,
- 0,
- meshPart.Triangles.Length
- );
- }
- }
- }
+ 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
+ );
+ LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
+ MathHelper.PiOver2,
+ 1,
+ 0.1f,
+ 25f // FIXME: magic value
+ );
+ LinearDepthEffect.FarPlane = 25f;
+
+ LinearDepthEffect.LightPosition = pointLight.Position;
+
+ // TODO: set up instancing
+ // var numInstances = FillAndSetBuffersForInstancing(
+ // GraphicsDevice,
+ // camera,
+ // drawable,
+ // transforms,
+ // VertexPos
+ // );
+
+ // RenderInstanced(
+ // GraphicsDevice,
+ // camera,
+ // modelTransforms,
+ // LinearDepthEffect
+ // );
}
}
@@ -851,6 +933,23 @@ namespace Kav
}
}
+ private static IEnumerable FrustumCull(
+ BoundingFrustum boundingFrustum,
+ T cullable,
+ IEnumerable transforms
+ ) where T : ICullable
+ {
+ foreach (var transform in transforms)
+ {
+ var boundingBox = TransformedBoundingBox(cullable.BoundingBox, transform);
+ var containment = boundingFrustum.Contains(boundingBox);
+ if (containment != ContainmentType.Disjoint)
+ {
+ yield return transform;
+ }
+ }
+ }
+
private static BoundingBox TransformedBoundingBox(BoundingBox boundingBox, Matrix matrix)
{
var center = (boundingBox.Min + boundingBox.Max) / 2f;
diff --git a/Resources.cs b/Resources.cs
index 44c28f7..2924998 100644
--- a/Resources.cs
+++ b/Resources.cs
@@ -111,6 +111,18 @@ namespace Kav
}
}
+ public static byte[] SimpleDepthEffectInstanced
+ {
+ get
+ {
+ if (simpleDepthEffectInstanced == null)
+ {
+ simpleDepthEffectInstanced = GetResource("SimpleDepthEffectInstanced.fxb");
+ }
+ return simpleDepthEffectInstanced;
+ }
+ }
+
public static byte[] LinearDepthEffect
{
get
@@ -168,6 +180,7 @@ namespace Kav
private static byte[] deferredPBREffect;
private static byte[] pbrEffect;
private static byte[] simpleDepthEffect;
+ private static byte[] simpleDepthEffectInstanced;
private static byte[] linearDepthEffect;
private static byte[] skyboxEffect;
private static byte[] diffuseLitSpriteEffect;
diff --git a/VertexDeclarations.cs b/VertexDeclarations.cs
new file mode 100644
index 0000000..f8b073f
--- /dev/null
+++ b/VertexDeclarations.cs
@@ -0,0 +1,12 @@
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ public static class VertexDeclarations
+ {
+ public static VertexDeclaration PositionInstanceDeclaration = new VertexDeclaration
+ (
+ new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2)
+ );
+ }
+}
diff --git a/Vertices/PositionInstanceVertex.cs b/Vertices/PositionInstanceVertex.cs
new file mode 100644
index 0000000..f173cef
--- /dev/null
+++ b/Vertices/PositionInstanceVertex.cs
@@ -0,0 +1,28 @@
+using System.Runtime.InteropServices;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+
+namespace Kav
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct PositionInstanceVertex : IVertexType, IHasTranslation
+ {
+ VertexDeclaration IVertexType.VertexDeclaration
+ {
+ get
+ {
+ return VertexDeclarations.PositionInstanceDeclaration;
+ }
+ }
+
+ public Vector3 Translation { get; set; }
+
+ public static readonly VertexDeclaration VertexDeclaration;
+
+ public PositionInstanceVertex(
+ Vector3 translation
+ ) {
+ Translation = translation;
+ }
+ }
+}