Kav/Renderer.cs

840 lines
34 KiB
C#
Raw Normal View History

2020-10-19 10:01:37 +00:00
using System;
using System.Collections.Generic;
2020-10-17 20:53:26 +00:00
using System.IO;
2020-08-07 00:58:50 +00:00
using Microsoft.Xna.Framework;
2020-08-04 09:32:02 +00:00
using Microsoft.Xna.Framework.Graphics;
namespace Kav
{
2020-08-07 00:58:50 +00:00
public class Renderer
2020-08-04 09:32:02 +00:00
{
private const int MAX_INSTANCE_VERTEX_COUNT = 1000000;
private const int MAX_SHADOW_CASCADES = 4;
private int ShadowMapSize { get; }
2020-08-07 00:58:50 +00:00
private GraphicsDevice GraphicsDevice { get; }
private VertexBuffer FullscreenTriangle { get; }
private int NumShadowCascades { get; }
private RenderTarget2D ColorRenderTarget { get; }
private RenderTarget2D[] ShadowRenderTargets { get; }
private DeferredPBREffect DeferredPBREffect { get; }
/* FIXME: these next two dont actually have anything to do with PBR */
private DeferredPBR_GBufferEffect Deferred_GBufferEffect { get; }
private DeferredPBR_AmbientLightEffect DeferredAmbientLightEffect { get; }
private DeferredPBR_PointLightEffect DeferredPointLightEffect { get; }
private DeferredPBR_DirectionalLightEffect DeferredDirectionalLightEffect { get; }
private Deferred_ToonEffect Deferred_ToonEffect { get; }
2020-08-07 00:58:50 +00:00
private SimpleDepthEffect SimpleDepthEffect { get; }
2020-10-19 10:01:37 +00:00
private LinearDepthEffect LinearDepthEffect { get; }
private Effect ToneMapEffect { get; }
2020-10-17 20:53:26 +00:00
private SkyboxEffect SkyboxEffect { get; }
2020-12-06 03:47:01 +00:00
private DiffuseLitSpriteEffect DiffuseLitSpriteEffect { get; }
2020-08-07 00:58:50 +00:00
2020-10-19 10:01:37 +00:00
private RenderTargetCube PointShadowCubeMap { get; }
2020-10-17 20:53:26 +00:00
private Kav.Model UnitCube { get; }
private SpriteBatch SpriteBatch { get; }
private DynamicVertexBuffer GBufferInstanceVertexBuffer { get; }
private readonly GBufferInstanceVertex[] GBufferInstanceVertices = new GBufferInstanceVertex[MAX_INSTANCE_VERTEX_COUNT];
public Renderer(
GraphicsDevice graphicsDevice,
int renderDimensionsX,
int renderDimensionsY,
int numShadowCascades,
int shadowMapSize
) {
2020-08-07 00:58:50 +00:00
GraphicsDevice = 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,
2020-08-07 00:58:50 +00:00
renderDimensionsX,
renderDimensionsY,
false,
SurfaceFormat.Color,
2020-10-17 20:53:26 +00:00
DepthFormat.Depth24,
0,
RenderTargetUsage.PreserveContents
);
2020-10-19 10:01:37 +00:00
PointShadowCubeMap = new RenderTargetCube(
GraphicsDevice,
shadowMapSize,
false,
SurfaceFormat.Single,
DepthFormat.Depth24
);
2020-08-07 00:58:50 +00:00
SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice);
2020-10-19 10:01:37 +00:00
LinearDepthEffect = new LinearDepthEffect(GraphicsDevice);
DeferredPBREffect = new DeferredPBREffect(GraphicsDevice);
Deferred_GBufferEffect = new DeferredPBR_GBufferEffect(GraphicsDevice);
DeferredAmbientLightEffect = new DeferredPBR_AmbientLightEffect(GraphicsDevice);
DeferredPointLightEffect = new DeferredPBR_PointLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect = new DeferredPBR_DirectionalLightEffect(GraphicsDevice);
DeferredDirectionalLightEffect.ShadowMapSize = ShadowMapSize;
ToneMapEffect = new Effect(graphicsDevice, Resources.ToneMapEffect);
Deferred_ToonEffect = new Deferred_ToonEffect(GraphicsDevice);
2020-10-17 20:53:26 +00:00
SkyboxEffect = new SkyboxEffect(GraphicsDevice);
2020-12-06 03:47:01 +00:00
DiffuseLitSpriteEffect = new DiffuseLitSpriteEffect(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))
});
2020-10-17 20:53:26 +00:00
UnitCube = Kav.ModelLoader.Load(
GraphicsDevice,
Smuggler.Importer.ImportGLB(GraphicsDevice, new MemoryStream(Resources.UnitCubeModel))
);
SpriteBatch = new SpriteBatch(graphicsDevice);
GBufferInstanceVertexBuffer = new DynamicVertexBuffer(
GraphicsDevice,
VertexDeclarations.GBufferInstanceDeclaration,
MAX_INSTANCE_VERTEX_COUNT,
BufferUsage.WriteOnly
);
}
// TODO: we could make this a lot more efficient probably
// draws mesh sprites with a forward rendered diffuse lighting technique
public void MeshSpriteRender(
2020-12-04 23:39:29 +00:00
RenderTarget2D renderTarget,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
IEnumerable<(MeshSprite, Matrix)> meshSpriteTransforms,
2020-12-06 03:47:01 +00:00
AmbientLight ambientLight,
IEnumerable<PointLight> pointLights,
DirectionalLight? directionalLight
2020-12-04 23:39:29 +00:00
) {
GraphicsDevice.SetRenderTarget(ColorRenderTarget);
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
DepthRender(ColorRenderTarget, camera, modelTransforms);
2020-12-04 23:39:29 +00:00
GraphicsDevice.Clear(ClearOptions.Target, new Color(0, 0, 0, 0), 1f, 0);
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.RasterizerState = RasterizerState.CullNone;
GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp;
GraphicsDevice.BlendState = BlendState.AlphaBlend;
2020-12-06 03:47:01 +00:00
DiffuseLitSpriteEffect.View = camera.View;
DiffuseLitSpriteEffect.Projection = camera.Projection;
2020-12-06 03:58:29 +00:00
DiffuseLitSpriteEffect.AmbientColor =
ambientLight.Color.ToVector3();
if (directionalLight.HasValue)
{
DiffuseLitSpriteEffect.DirectionalLightDirection =
directionalLight.Value.Direction;
DiffuseLitSpriteEffect.DirectionalLightColor =
directionalLight.Value.Color.ToVector3() * directionalLight.Value.Intensity;
}
else
{
DiffuseLitSpriteEffect.DirectionalLightColor = Vector3.Zero;
}
2020-12-06 03:47:01 +00:00
var i = 0;
foreach (var pointLight in pointLights)
{
if (i > DiffuseLitSpriteEffect.MaxPointLights) { break; }
DiffuseLitSpriteEffect.PointLights[i] = pointLight;
i += 1;
}
2020-12-04 23:39:29 +00:00
2020-12-07 03:27:46 +00:00
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
foreach (var (sprite, transform) in FrustumCull(boundingFrustum, meshSpriteTransforms))
2020-12-04 23:39:29 +00:00
{
DiffuseLitSpriteEffect.NormalMapEnabled = sprite.Normal != null;
if (sprite.BillboardConstraint == SpriteBillboardConstraint.None)
{
DiffuseLitSpriteEffect.World = transform;
}
else if (sprite.BillboardConstraint == SpriteBillboardConstraint.Horizontal)
{
2020-12-06 03:47:01 +00:00
DiffuseLitSpriteEffect.World = Matrix.CreateConstrainedBillboard(
transform.Translation,
camera.Position,
Vector3.Up,
camera.Forward,
camera.Position - transform.Translation
);
}
else
{
2020-12-06 03:47:01 +00:00
DiffuseLitSpriteEffect.World = Matrix.CreateConstrainedBillboard(
transform.Translation,
camera.Position,
Vector3.Up,
null,
null
);
}
GraphicsDevice.Textures[0] = sprite.Texture;
GraphicsDevice.Textures[1] = sprite.Normal;
2020-12-04 23:39:29 +00:00
GraphicsDevice.SetVertexBuffer(sprite.VertexBuffer);
GraphicsDevice.Indices = sprite.IndexBuffer;
foreach (var pass in DiffuseLitSpriteEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
sprite.VertexBuffer.VertexCount,
0,
2
);
}
}
2020-12-04 23:39:29 +00:00
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();
}
public void DepthRender(
RenderTarget2D renderTarget,
2020-10-17 20:53:26 +00:00
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms
) {
GraphicsDevice.SetRenderTarget(renderTarget);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
2020-10-20 01:22:54 +00:00
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
2020-10-17 20:53:26 +00:00
{
foreach (var modelMesh in model.Meshes)
{
foreach (var meshPart in modelMesh.MeshParts)
{
SimpleDepthEffect.Model = transform;
SimpleDepthEffect.View = camera.View;
SimpleDepthEffect.Projection = camera.Projection;
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
GraphicsDevice.Indices = meshPart.IndexBuffer;
foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
meshPart.VertexBuffer.VertexCount,
0,
meshPart.Triangles.Length
);
}
}
}
}
}
public void SkyboxRender(
RenderTarget2D renderTarget,
2020-10-17 20:53:26 +00:00
PerspectiveCamera camera,
TextureCube skybox
) {
GraphicsDevice.SetRenderTarget(renderTarget);
2020-10-17 20:53:26 +00:00
GraphicsDevice.RasterizerState.CullMode = CullMode.CullClockwiseFace;
2020-10-19 21:00:11 +00:00
SkyboxEffect.Skybox = skybox;
2020-10-17 20:53:26 +00:00
var view = camera.View;
view.Translation = Vector3.Zero;
SkyboxEffect.View = view;
SkyboxEffect.Projection = camera.Projection;
GraphicsDevice.SetVertexBuffer(UnitCube.Meshes[0].MeshParts[0].VertexBuffer);
GraphicsDevice.Indices = UnitCube.Meshes[0].MeshParts[0].IndexBuffer;
foreach (var pass in SkyboxEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
UnitCube.Meshes[0].MeshParts[0].VertexBuffer.VertexCount,
0,
UnitCube.Meshes[0].MeshParts[0].Triangles.Length
);
}
GraphicsDevice.RasterizerState.CullMode = CullMode.CullCounterClockwiseFace;
}
/// <summary>
/// GBuffer binding must have 4 render targets.
/// </summary>
public void InstancedGBufferRender<T>(
RenderTargetBinding[] gBuffer,
PerspectiveCamera camera,
T drawable,
int numInstances,
IEnumerable<Matrix> transforms
) where T : IIndexDrawable, IGBufferDrawable {
GraphicsDevice.SetRenderTargets(gBuffer);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
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;
int i = 0;
foreach (var transform in transforms)
{
if (i >= numInstances) { break; }
GBufferInstanceVertices[i].World = transform;
GBufferInstanceVertices[i].WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(transform));
GBufferInstanceVertices[i].WorldViewProjection = transform * camera.View * camera.Projection;
i += 1;
}
GBufferInstanceVertexBuffer.SetData(
GBufferInstanceVertices,
0,
numInstances,
SetDataOptions.Discard
);
GraphicsDevice.SetVertexBuffers(
drawable.VertexBuffer,
new VertexBufferBinding(GBufferInstanceVertexBuffer, 0, 1)
);
GraphicsDevice.Indices = drawable.IndexBuffer;
GraphicsDevice.DrawInstancedPrimitives(
PrimitiveType.TriangleList,
0,
0,
drawable.VertexBuffer.VertexCount,
0,
drawable.IndexBuffer.IndexCount / 3,
numInstances
);
}
public void GBufferRender(
RenderTargetBinding[] gBuffer,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms
) {
GraphicsDevice.SetRenderTargets(gBuffer);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
2020-10-20 01:22:54 +00:00
var boundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
{
foreach (var modelMesh in model.Meshes)
{
foreach (var meshPart in modelMesh.MeshParts)
{
Deferred_GBufferEffect.World = transform;
Deferred_GBufferEffect.View = camera.View;
Deferred_GBufferEffect.Projection = camera.Projection;
Deferred_GBufferEffect.Albedo = meshPart.Albedo;
Deferred_GBufferEffect.Metallic = meshPart.Metallic;
Deferred_GBufferEffect.Roughness = meshPart.Roughness;
Deferred_GBufferEffect.AlbedoTexture = meshPart.AlbedoTexture;
Deferred_GBufferEffect.NormalTexture = meshPart.NormalTexture;
Deferred_GBufferEffect.MetallicRoughnessTexture = meshPart.MetallicRoughnessTexture;
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
GraphicsDevice.Indices = meshPart.IndexBuffer;
foreach (var pass in Deferred_GBufferEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
meshPart.VertexBuffer.VertexCount,
0,
meshPart.Triangles.Length
);
}
}
}
}
}
public void AmbientLightRender(
RenderTarget2D renderTarget,
Texture2D gPosition,
Texture2D gAlbedo,
AmbientLight ambientLight
) {
GraphicsDevice.SetRenderTarget(renderTarget);
2020-10-17 20:53:26 +00:00
GraphicsDevice.BlendState = BlendState.Opaque;
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
DeferredAmbientLightEffect.GPosition = gPosition;
DeferredAmbientLightEffect.GAlbedo = gAlbedo;
DeferredAmbientLightEffect.AmbientColor = ambientLight.Color.ToVector3();
foreach (var pass in DeferredAmbientLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
2020-08-07 00:58:50 +00:00
}
public void PointLightRender(
RenderTarget2D renderTarget,
Texture2D gPosition,
Texture2D gAlbedo,
Texture2D gNormal,
Texture2D gMetallicRoughness,
2020-10-19 10:01:37 +00:00
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
PointLight pointLight
) {
RenderPointShadows(camera, modelTransforms, pointLight);
GraphicsDevice.SetRenderTarget(renderTarget);
2020-10-19 10:01:37 +00:00
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
DeferredPointLightEffect.GPosition = gPosition;
DeferredPointLightEffect.GAlbedo = gAlbedo;
DeferredPointLightEffect.GNormal = gNormal;
2020-12-04 23:39:29 +00:00
DeferredPointLightEffect.GMetallicRoughness = gMetallicRoughness;
2020-10-19 10:01:37 +00:00
DeferredPointLightEffect.ShadowMap = PointShadowCubeMap;
DeferredPointLightEffect.EyePosition = camera.Position;
DeferredPointLightEffect.PointLightPosition = pointLight.Position;
2020-12-04 23:39:29 +00:00
DeferredPointLightEffect.PointLightColor =
pointLight.Color.ToVector3() * pointLight.Intensity;
2020-10-19 10:01:37 +00:00
DeferredPointLightEffect.FarPlane = 25f; // FIXME: magic value
foreach (var pass in DeferredPointLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
}
public void DirectionalLightRender(
RenderTarget2D renderTarget,
Texture2D gPosition,
Texture2D gAlbedo,
Texture2D gNormal,
Texture2D gMetallicRoughness,
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
int numShadowCascades
2020-08-07 08:12:46 +00:00
) {
2020-10-19 10:01:37 +00:00
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.ShadowMapTwo = ShadowRenderTargets[1];
}
if (numShadowCascades > 2)
{
DeferredDirectionalLightEffect.ShadowMapThree = ShadowRenderTargets[2];
}
if (numShadowCascades > 3)
{
DeferredDirectionalLightEffect.ShadowMapFour = ShadowRenderTargets[3];
}
2020-12-04 23:39:29 +00:00
DeferredDirectionalLightEffect.DirectionalLightDirection = directionalLight.Direction;
2020-12-04 23:39:29 +00:00
DeferredDirectionalLightEffect.DirectionalLightColor =
directionalLight.Color.ToVector3() * directionalLight.Intensity;
2020-12-04 23:39:29 +00:00
DeferredDirectionalLightEffect.ViewMatrix = camera.View;
2020-10-02 19:28:28 +00:00
DeferredDirectionalLightEffect.EyePosition = camera.Position;
foreach (EffectPass pass in DeferredDirectionalLightEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
2020-08-07 00:58:50 +00:00
}
public void DirectionalLightToonRender(
RenderTarget2D renderTarget,
Texture2D gPosition,
Texture2D gAlbedo,
Texture2D gNormal,
Texture2D gMetallicRoughness,
PerspectiveCamera camera,
2020-10-02 19:28:28 +00:00
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
int numShadowCascades,
bool ditheredShadows
) {
2020-10-19 10:01:37 +00:00
RenderDirectionalShadows(camera, modelTransforms, directionalLight, Deferred_ToonEffect);
2020-10-02 19:28:28 +00:00
GraphicsDevice.SetRenderTarget(renderTarget);
2020-10-17 20:53:26 +00:00
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
GraphicsDevice.BlendState = BlendState.Additive;
2020-10-02 19:28:28 +00:00
Deferred_ToonEffect.GPosition = gPosition;
Deferred_ToonEffect.GAlbedo = gAlbedo;
Deferred_ToonEffect.GNormal = gNormal;
Deferred_ToonEffect.GMetallicRoughness = gMetallicRoughness;
2020-10-02 19:28:28 +00:00
Deferred_ToonEffect.DitheredShadows = ditheredShadows;
2020-10-16 01:19:43 +00:00
Deferred_ToonEffect.EyePosition = camera.Position;
Deferred_ToonEffect.DirectionalLightDirection = directionalLight.Direction;
2020-12-04 23:39:29 +00:00
Deferred_ToonEffect.DirectionalLightColor =
directionalLight.Color.ToVector3() * directionalLight.Intensity;
2020-10-02 19:28:28 +00:00
Deferred_ToonEffect.ShadowMapOne = ShadowRenderTargets[0];
if (numShadowCascades > 1)
2020-10-02 19:28:28 +00:00
{
Deferred_ToonEffect.ShadowMapTwo = ShadowRenderTargets[1];
}
if (numShadowCascades > 2)
2020-10-02 19:28:28 +00:00
{
Deferred_ToonEffect.ShadowMapThree = ShadowRenderTargets[2];
}
if (numShadowCascades > 3)
2020-10-02 19:28:28 +00:00
{
Deferred_ToonEffect.ShadowMapFour = ShadowRenderTargets[3];
}
2020-12-04 23:39:29 +00:00
2020-10-02 19:28:28 +00:00
Deferred_ToonEffect.ViewMatrix = camera.View;
foreach (EffectPass pass in Deferred_ToonEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
}
}
2020-10-19 10:01:37 +00:00
private void RenderDirectionalShadows(
2020-10-02 19:28:28 +00:00
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
ShadowCascadeEffect effect
) {
// render the individual shadow cascades
var previousFarPlane = camera.NearPlane;
for (var i = 0; i < NumShadowCascades; i++)
{
var farPlane = camera.FarPlane / (MathHelper.Max((NumShadowCascades - i - 1) * 2f, 1f));
2020-12-04 23:39:29 +00:00
// divide the view frustum
2020-10-02 19:28:28 +00:00
var shadowCamera = new PerspectiveCamera(
camera.Position,
camera.Forward,
camera.Up,
camera.FieldOfView,
camera.AspectRatio,
previousFarPlane,
farPlane
);
2020-12-04 23:39:29 +00:00
2020-10-02 19:28:28 +00:00
// TODO: This is tightly coupled to the effect and it sucks
2020-10-19 10:01:37 +00:00
RenderDirectionalShadowMap(shadowCamera, modelTransforms, directionalLight, effect, i);
2020-10-02 19:28:28 +00:00
effect.CascadeFarPlanes[i] = farPlane;
previousFarPlane = farPlane;
}
}
2020-10-19 10:01:37 +00:00
private void RenderDirectionalShadowMap(
2020-12-04 23:39:29 +00:00
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
DirectionalLight directionalLight,
2020-10-02 19:28:28 +00:00
ShadowCascadeEffect effect,
int shadowCascadeIndex
) {
GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
GraphicsDevice.Clear(Color.White);
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
2020-12-04 23:39:29 +00:00
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;
2020-08-07 00:58:50 +00:00
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);
2020-12-04 23:39:29 +00:00
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)
{
2020-10-02 19:28:28 +00:00
effect.LightSpaceMatrixOne = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 1)
{
2020-10-02 19:28:28 +00:00
effect.LightSpaceMatrixTwo = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 2)
{
2020-10-02 19:28:28 +00:00
effect.LightSpaceMatrixThree = lightSpaceMatrix;
}
else if (shadowCascadeIndex == 3)
{
2020-10-02 19:28:28 +00:00
effect.LightSpaceMatrixFour = lightSpaceMatrix;
}
2020-10-20 01:22:54 +00:00
var boundingFrustum = new BoundingFrustum(lightSpaceMatrix);
2020-12-04 23:39:29 +00:00
2020-10-20 01:22:54 +00:00
foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
2020-08-07 00:58:50 +00:00
{
foreach (var modelMesh in model.Meshes)
{
foreach (var meshPart in modelMesh.MeshParts)
{
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
GraphicsDevice.Indices = meshPart.IndexBuffer;
SimpleDepthEffect.Model = transform;
2020-08-04 09:32:02 +00:00
2020-08-07 00:58:50 +00:00
foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
meshPart.VertexBuffer.VertexCount,
0,
meshPart.Triangles.Length
);
}
}
}
}
}
2020-10-19 10:01:37 +00:00
private void RenderPointShadows(
PerspectiveCamera camera,
IEnumerable<(Model, Matrix)> modelTransforms,
PointLight pointLight
) {
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;
LinearDepthEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
MathHelper.PiOver2,
1,
0.1f,
25f // FIXME: magic value
);
LinearDepthEffect.FarPlane = 25f;
LinearDepthEffect.LightPosition = pointLight.Position;
foreach (CubeMapFace face in Enum.GetValues(typeof(CubeMapFace)))
{
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
);
2020-10-20 01:22:54 +00:00
var boundingFrustum = new BoundingFrustum(LinearDepthEffect.View * LinearDepthEffect.Projection);
foreach (var (model, transform) in FrustumCull(boundingFrustum, modelTransforms))
2020-10-19 10:01:37 +00:00
{
foreach (var modelMesh in model.Meshes)
{
foreach (var meshPart in modelMesh.MeshParts)
{
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
GraphicsDevice.Indices = meshPart.IndexBuffer;
LinearDepthEffect.Model = transform;
foreach (var pass in LinearDepthEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(
PrimitiveType.TriangleList,
0,
0,
meshPart.VertexBuffer.VertexCount,
0,
meshPart.Triangles.Length
);
}
}
}
}
}
}
2020-10-20 01:22:54 +00:00
2020-12-07 03:36:00 +00:00
private static IEnumerable<(T, Matrix)> FrustumCull<T>(
2020-10-20 01:22:54 +00:00
BoundingFrustum boundingFrustum,
2020-12-07 03:36:00 +00:00
IEnumerable<(T, Matrix)> cullableTransforms
) where T : ICullable {
foreach (var (cullable, transform) in cullableTransforms)
2020-12-07 03:27:46 +00:00
{
2020-12-07 03:36:00 +00:00
var boundingBox = TransformedBoundingBox(cullable.BoundingBox, transform);
2020-12-07 03:27:46 +00:00
var containment = boundingFrustum.Contains(boundingBox);
if (containment != ContainmentType.Disjoint)
{
2020-12-07 03:36:00 +00:00
yield return (cullable, transform);
2020-12-07 03:27:46 +00:00
}
}
}
2020-10-20 01:22:54 +00:00
private static BoundingBox TransformedBoundingBox(BoundingBox boundingBox, Matrix matrix)
{
var center = (boundingBox.Min + boundingBox.Max) / 2f;
var extent = (boundingBox.Max - boundingBox.Min) / 2f;
var newCenter = Vector3.Transform(center, matrix);
var newExtent = Vector3.TransformNormal(extent, AbsoluteMatrix(matrix));
return new BoundingBox(newCenter - newExtent, newCenter + newExtent);
}
private static Matrix AbsoluteMatrix(Matrix matrix)
{
return new Matrix(
Math.Abs(matrix.M11), Math.Abs(matrix.M12), Math.Abs(matrix.M13), Math.Abs(matrix.M14),
Math.Abs(matrix.M21), Math.Abs(matrix.M22), Math.Abs(matrix.M23), Math.Abs(matrix.M24),
Math.Abs(matrix.M31), Math.Abs(matrix.M32), Math.Abs(matrix.M33), Math.Abs(matrix.M34),
Math.Abs(matrix.M41), Math.Abs(matrix.M42), Math.Abs(matrix.M43), Math.Abs(matrix.M44)
);
}
2020-08-04 09:32:02 +00:00
}
}