more shadow decoupling + change world instance to position instance

instancing
cosmonaut 2020-12-08 02:49:18 -08:00
parent 96f6d22896
commit 4d3c5fc316
18 changed files with 296 additions and 88 deletions

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface IHasTranslation
{
Vector3 Translation { get; set; }
}
}

View File

@ -1,4 +1,4 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
namespace Kav
{

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

Binary file not shown.

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

Binary file not shown.

BIN
Effects/FXB/SimpleDepthEffectInstanced.fxb (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -24,11 +24,6 @@ struct VertexInput
float2 TexCoord : TEXCOORD;
};
struct InstanceInput
{
float4x4 World : TEXCOORD2;
};
struct PixelInput
{
float4 Position : SV_POSITION;
@ -61,15 +56,23 @@ PixelInput main_vs(VertexInput input)
return output;
}
PixelInput instanced_vs(VertexInput input, InstanceInput instanceInput)
PixelInput instanced_vs(VertexInput input, float3 Translation : TEXCOORD2)
{
PixelInput output;
output.PositionWorld = mul(input.Position, instanceInput.World).xyz;
output.NormalWorld = normalize(mul(input.Normal, instanceInput.World));
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;
float4x4 worldViewProjection = mul(instanceInput.World, ViewProjection);
output.Position = mul(input.Position, worldViewProjection);
return output;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -7,12 +7,10 @@ namespace Kav
{
EffectParameter worldParam;
EffectParameter viewProjectionParam;
EffectParameter vertexShaderIndexParam;
Matrix world;
Matrix view;
Matrix projection;
bool hardwareInstancingEnabled = false;
EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All;
@ -46,19 +44,6 @@ namespace Kav
}
}
public bool HardwareInstancingEnabled
{
get { return hardwareInstancingEnabled; }
set
{
if (value != hardwareInstancingEnabled)
{
hardwareInstancingEnabled = value;
dirtyFlags |= EffectDirtyFlags.VertexShaderIndex;
}
}
}
public SimpleDepthEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.SimpleDepthEffect)
{
CacheEffectParameters();
@ -80,25 +65,12 @@ namespace Kav
dirtyFlags &= ~EffectDirtyFlags.ViewProj;
}
if ((dirtyFlags & EffectDirtyFlags.VertexShaderIndex) != 0)
{
int vertexShaderIndex = 0;
if (hardwareInstancingEnabled)
{
vertexShaderIndex = 1;
}
vertexShaderIndexParam.SetValue(vertexShaderIndex);
}
}
private void CacheEffectParameters()
{
worldParam = Parameters["World"];
viewProjectionParam = Parameters["ViewProjection"];
vertexShaderIndexParam = Parameters["VertexShaderIndex"];
}
}
}

View File

@ -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"];
}
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.Xna.Framework;
namespace Kav
{
public interface IHasVertexPositions
{
Vector3[] Positions { get; }
}
}

View File

@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
public class MeshPart : IIndexDrawable, IGBufferDrawable, ICullable
public class MeshPart : IIndexDrawable, IGBufferDrawable, ICullable, IHasVertexPositions
{
public IndexBuffer IndexBuffer { get; }
public VertexBuffer VertexBuffer { get; }

View File

@ -43,6 +43,9 @@
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>

View File

@ -43,6 +43,9 @@
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffect.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffect.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\SimpleDepthEffectInstanced.fxb">
<LogicalName>Kav.Resources.SimpleDepthEffectInstanced.fxb</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Effects\FXB\LinearDepthEffect.fxb">
<LogicalName>Kav.Resources.LinearDepthEffect.fxb</LogicalName>
</EmbeddedResource>

View File

@ -9,7 +9,6 @@ namespace Kav
public class Renderer
{
private const int MAX_INSTANCE_VERTEX_COUNT = 1000000;
private const int MAX_SHADOW_CASCADES = 4;
private GraphicsDevice GraphicsDevice { get; }
@ -23,6 +22,7 @@ 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; }
@ -30,10 +30,8 @@ namespace Kav
private Kav.Model UnitCube { get; }
private SpriteBatch SpriteBatch { get; }
private DynamicVertexBuffer GBufferInstanceVertexBuffer { get; }
private readonly GBufferInstanceVertex[] GBufferInstanceVertices = new GBufferInstanceVertex[MAX_INSTANCE_VERTEX_COUNT];
private DynamicVertexBuffer PositionInstanceVertexBuffer { get; }
private readonly PositionInstanceVertex[] GBufferInstanceVertices = new PositionInstanceVertex[MAX_INSTANCE_VERTEX_COUNT];
public Renderer(
GraphicsDevice graphicsDevice
@ -41,6 +39,7 @@ namespace Kav
GraphicsDevice = graphicsDevice;
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
SimpleDepthEffectInstanced = new SimpleDepthEffectInstanced(GraphicsDevice);
LinearDepthEffect = new LinearDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
@ -65,11 +64,9 @@ namespace Kav
Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel))
);
SpriteBatch = new SpriteBatch(graphicsDevice);
GBufferInstanceVertexBuffer = new DynamicVertexBuffer(
PositionInstanceVertexBuffer = new DynamicVertexBuffer(
GraphicsDevice,
VertexDeclarations.GBufferInstanceDeclaration,
VertexDeclarations.PositionInstanceDeclaration,
MAX_INSTANCE_VERTEX_COUNT,
BufferUsage.WriteOnly
);
@ -203,13 +200,11 @@ namespace Kav
// Effect must be pre-configured!!
public static void CullAndRenderIndexed<T, U>(
GraphicsDevice graphicsDevice,
PerspectiveCamera camera,
BoundingFrustum boundingFrustum,
IEnumerable<(T, Matrix)> drawableTransformPairs,
U effect
) where T : IIndexDrawable, ICullable where U : Effect, IHasWorldMatrix
{
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
foreach (var (drawable, transform) in FrustumCull(boundingFrustum, drawableTransformPairs))
{
effect.World = transform;
@ -253,14 +248,14 @@ namespace Kav
IEnumerable<Matrix> transforms,
V[] vertexData,
DynamicVertexBuffer dynamicVertexBuffer
) where T : ICullable, IIndexDrawable where V : struct, IVertexType, IHasWorldMatrix
) where T : ICullable, IIndexDrawable where V : struct, IVertexType, IHasTranslation
{
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
int numInstances = 0;
foreach (var transform in FrustumCull(boundingFrustum, drawable, transforms))
{
vertexData[numInstances].World = transform;
vertexData[numInstances].Translation = transform.Translation;
numInstances += 1;
}
@ -331,7 +326,7 @@ namespace Kav
CullAndRenderIndexed(
GraphicsDevice,
camera,
new BoundingFrustum(camera.View * camera.Projection),
drawableTransforms,
SimpleDepthEffect
);
@ -396,7 +391,7 @@ namespace Kav
drawable,
transforms,
GBufferInstanceVertices,
GBufferInstanceVertexBuffer
PositionInstanceVertexBuffer
);
RenderInstanced(
@ -407,12 +402,15 @@ namespace Kav
);
// re-render to get depth
GraphicsDevice.SetRenderTarget(depthBuffer);
GraphicsDevice.SetRenderTargets(depthBuffer);
SimpleDepthEffectInstanced.View = camera.View;
SimpleDepthEffectInstanced.Projection = camera.Projection;
RenderInstanced(
GraphicsDevice,
drawable,
Deferred_GBufferEffect,
SimpleDepthEffectInstanced,
numInstances
);
}
@ -498,17 +496,16 @@ namespace Kav
RenderFullscreenEffect(DeferredPointLightEffect);
}
public void RenderDirectionalLight<T>(
public void RenderDirectionalLight(
RenderTarget2D renderTarget,
Texture2D gPosition,
Texture2D gAlbedo,
Texture2D gNormal,
Texture2D gMetallicRoughness,
DirectionalShadowMapData shadowMapData,
PerspectiveCamera camera,
IEnumerable<(T, Matrix)> modelTransforms,
DirectionalLight directionalLight,
DirectionalShadowMapData shadowMapData
) where T : ICullable, IIndexDrawable {
DirectionalLight directionalLight
) {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
@ -522,21 +519,25 @@ namespace Kav
DeferredDirectionalLightEffect.ShadowMapOne = shadowMapData.ShadowMaps[0];
DeferredDirectionalLightEffect.LightSpaceMatrixOne = shadowMapData.LightSpaceMatrixOne;
DeferredDirectionalLightEffect.CascadeFarPlanes[0] = shadowMapData.CascadeFarPlanes[0];
if (shadowMapData.NumShadowCascades > 1)
{
DeferredDirectionalLightEffect.ShadowMapTwo = shadowMapData.ShadowMaps[1];
DeferredDirectionalLightEffect.LightSpaceMatrixTwo = shadowMapData.LightSpaceMatrixTwo;
DeferredDirectionalLightEffect.CascadeFarPlanes[1] = shadowMapData.CascadeFarPlanes[1];
}
if (shadowMapData.NumShadowCascades > 2)
{
DeferredDirectionalLightEffect.ShadowMapThree = shadowMapData.ShadowMaps[2];
DeferredDirectionalLightEffect.LightSpaceMatrixThree = shadowMapData.LightSpaceMatrixThree;
DeferredDirectionalLightEffect.CascadeFarPlanes[2] = shadowMapData.CascadeFarPlanes[2];
}
if (shadowMapData.NumShadowCascades > 3)
{
DeferredDirectionalLightEffect.ShadowMapFour = shadowMapData.ShadowMaps[3];
DeferredDirectionalLightEffect.LightSpaceMatrixFour = shadowMapData.LightSpaceMatrixFour;
DeferredDirectionalLightEffect.CascadeFarPlanes[3] = shadowMapData.CascadeFarPlanes[3];
}
DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction;
@ -549,7 +550,7 @@ namespace Kav
RenderFullscreenEffect(DeferredDirectionalLightEffect);
}
public void RenderDirectionalLightToon<T>(
public void RenderDirectionalLightToon(
RenderTarget2D renderTarget,
Texture2D gPosition,
Texture2D gAlbedo,
@ -557,10 +558,9 @@ namespace Kav
Texture2D gMetallicRoughness,
DirectionalShadowMapData shadowMapData,
PerspectiveCamera camera,
IEnumerable<(T, Matrix)> modelTransforms,
DirectionalLight directionalLight,
bool ditheredShadows
) where T : ICullable, IIndexDrawable {
) {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
@ -697,10 +697,15 @@ namespace Kav
shadowMapData.LightSpaceMatrixFour = lightSpaceMatrix;
}
CullAndRenderIndexed(GraphicsDevice, camera, drawableTransforms, SimpleDepthEffect);
CullAndRenderIndexed(
GraphicsDevice,
new BoundingFrustum(lightSpaceMatrix),
drawableTransforms,
SimpleDepthEffect
);
}
public void RenderPointShadowsIndexed<T>(
public void RenderPointShadowMapIndexed<T>(
RenderTargetCube pointShadowCubeMap,
PerspectiveCamera camera,
IEnumerable<(T, Matrix)> modelTransforms,
@ -770,13 +775,101 @@ namespace Kav
CullAndRenderIndexed(
GraphicsDevice,
camera,
new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection),
modelTransforms,
LinearDepthEffect
);
}
}
public void RenderPointShadowMapInstanced<T>(
RenderTargetCube pointShadowCubeMap,
PerspectiveCamera camera,
T drawable,
IEnumerable<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);
Vector3 targetDirection;
Vector3 targetUpDirection;
switch(face)
{
case CubeMapFace.PositiveX:
targetDirection = Vector3.Right;
targetUpDirection = Vector3.Up;
break;
case CubeMapFace.NegativeX:
targetDirection = Vector3.Left;
targetUpDirection = Vector3.Up;
break;
case CubeMapFace.PositiveY:
targetDirection = Vector3.Up;
targetUpDirection = Vector3.Forward;
break;
case CubeMapFace.NegativeY:
targetDirection = Vector3.Down;
targetUpDirection = Vector3.Backward;
break;
case CubeMapFace.PositiveZ:
targetDirection = Vector3.Backward;
targetUpDirection = Vector3.Up;
break;
case CubeMapFace.NegativeZ:
targetDirection = Vector3.Forward;
targetUpDirection = Vector3.Up;
break;
default:
targetDirection = Vector3.Right;
targetUpDirection = Vector3.Up;
break;
}
LinearDepthEffect.View = Matrix.CreateLookAt(
pointLight.Position,
pointLight.Position + targetDirection,
targetUpDirection
);
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
// );
}
}
private static IEnumerable<(T, Matrix)> FrustumCull<T>(
BoundingFrustum boundingFrustum,
IEnumerable<(T, Matrix)> cullableTransforms

View File

@ -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;

View File

@ -4,12 +4,9 @@ namespace Kav
{
public static class VertexDeclarations
{
public static VertexDeclaration GBufferInstanceDeclaration = new VertexDeclaration
public static VertexDeclaration PositionInstanceDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 2),
new VertexElement(16, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 3),
new VertexElement(32, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 4),
new VertexElement(48, VertexElementFormat.Vector4, VertexElementUsage.TextureCoordinate, 5)
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 2)
);
}
}

View File

@ -5,24 +5,24 @@ using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GBufferInstanceVertex : IVertexType, IHasWorldMatrix
public struct PositionInstanceVertex : IVertexType, IHasTranslation
{
VertexDeclaration IVertexType.VertexDeclaration
{
get
{
return VertexDeclarations.GBufferInstanceDeclaration;
return VertexDeclarations.PositionInstanceDeclaration;
}
}
public Matrix World { get; set; }
public Vector3 Translation { get; set; }
public static readonly VertexDeclaration VertexDeclaration;
public GBufferInstanceVertex(
Matrix world
public PositionInstanceVertex(
Vector3 translation
) {
World = world;
Translation = translation;
}
}
}