cascaded shadow maps
parent
10f3d9cd98
commit
a55e33f9a9
|
@ -2,22 +2,32 @@ using Microsoft.Xna.Framework;
|
|||
|
||||
namespace Kav
|
||||
{
|
||||
public struct Camera
|
||||
public struct PerspectiveCamera
|
||||
{
|
||||
public Matrix Transform { get; }
|
||||
public Matrix View
|
||||
{
|
||||
get
|
||||
{
|
||||
return Matrix.CreateLookAt(Transform.Translation, Transform.Translation + Transform.Forward, Transform.Up);
|
||||
}
|
||||
}
|
||||
public Matrix View { get; }
|
||||
public Matrix Projection { get; }
|
||||
|
||||
public Camera(Matrix transform, Matrix projection)
|
||||
public Vector3 Position { get; }
|
||||
public Vector3 Forward { get; }
|
||||
public Vector3 Up { get; }
|
||||
|
||||
public float FieldOfView { get; }
|
||||
public float AspectRatio { get; }
|
||||
public float NearPlane { get; }
|
||||
public float FarPlane { get; }
|
||||
|
||||
public PerspectiveCamera(Vector3 position, Vector3 forward, Vector3 up, float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
|
||||
{
|
||||
Transform = transform;
|
||||
Projection = projection;
|
||||
Position = position;
|
||||
Forward = forward;
|
||||
Up = up;
|
||||
View = Matrix.CreateLookAt(Position, Position + Forward, Up);
|
||||
|
||||
FieldOfView = fieldOfView;
|
||||
AspectRatio = aspectRatio;
|
||||
NearPlane = nearPlane;
|
||||
FarPlane = farPlane;
|
||||
Projection = Matrix.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,24 @@ namespace Kav
|
|||
EffectParameter gAlbedoParam;
|
||||
EffectParameter gNormalParam;
|
||||
EffectParameter gMetallicRoughnessParam;
|
||||
EffectParameter shadowMapParam;
|
||||
|
||||
EffectParameter shadowMapOneParam;
|
||||
EffectParameter shadowMapTwoParam;
|
||||
EffectParameter shadowMapThreeParam;
|
||||
EffectParameter shadowMapFourParam;
|
||||
|
||||
EffectParameter lightSpaceMatrixOneParam;
|
||||
EffectParameter lightSpaceMatrixTwoParam;
|
||||
EffectParameter lightSpaceMatrixThreeParam;
|
||||
EffectParameter lightSpaceMatrixFourParam;
|
||||
|
||||
EffectParameter viewMatrixParam;
|
||||
EffectParameter cascadeFarPlanesParam;
|
||||
|
||||
EffectParameter directionalLightColorParam;
|
||||
EffectParameter directionalLightDirectionParam;
|
||||
|
||||
EffectParameter eyePositionParam;
|
||||
EffectParameter lightSpaceMatrixParam;
|
||||
|
||||
PointLightCollection pointLightCollection;
|
||||
|
||||
|
@ -23,11 +34,22 @@ namespace Kav
|
|||
public Texture2D GAlbedo { get; set; }
|
||||
public Texture2D GNormal { get; set; }
|
||||
public Texture2D GMetallicRoughness { get; set; }
|
||||
public Texture2D ShadowMap { get; set; }
|
||||
|
||||
public Texture2D ShadowMapOne { get; set; }
|
||||
public Texture2D ShadowMapTwo { get; set; }
|
||||
public Texture2D ShadowMapThree { get; set; }
|
||||
public Texture2D ShadowMapFour { get; set; }
|
||||
|
||||
public Matrix LightSpaceMatrixOne { get; set; }
|
||||
public Matrix LightSpaceMatrixTwo { get; set; }
|
||||
public Matrix LightSpaceMatrixThree { get; set; }
|
||||
public Matrix LightSpaceMatrixFour { get; set; }
|
||||
|
||||
public Matrix ViewMatrix { get; set; }
|
||||
public float[] CascadeFarPlanes;
|
||||
|
||||
public Vector3 DirectionalLightColor { get; set; }
|
||||
public Vector3 DirectionalLightDirection { get; set; }
|
||||
public Matrix LightSpaceMatrix { get; set; }
|
||||
|
||||
public Vector3 EyePosition { get; set; }
|
||||
|
||||
|
@ -41,6 +63,8 @@ namespace Kav
|
|||
|
||||
public DeferredPBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.DeferredPBREffect)
|
||||
{
|
||||
CascadeFarPlanes = new float[4];
|
||||
|
||||
CacheEffectParameters();
|
||||
|
||||
pointLightCollection = new PointLightCollection(
|
||||
|
@ -82,13 +106,24 @@ namespace Kav
|
|||
gAlbedoParam.SetValue(GAlbedo);
|
||||
gNormalParam.SetValue(GNormal);
|
||||
gMetallicRoughnessParam.SetValue(GMetallicRoughness);
|
||||
shadowMapParam.SetValue(ShadowMap);
|
||||
|
||||
shadowMapOneParam.SetValue(ShadowMapOne);
|
||||
shadowMapTwoParam.SetValue(ShadowMapTwo);
|
||||
shadowMapThreeParam.SetValue(ShadowMapThree);
|
||||
shadowMapFourParam.SetValue(ShadowMapFour);
|
||||
|
||||
lightSpaceMatrixOneParam.SetValue(LightSpaceMatrixOne);
|
||||
lightSpaceMatrixTwoParam.SetValue(LightSpaceMatrixTwo);
|
||||
lightSpaceMatrixThreeParam.SetValue(LightSpaceMatrixThree);
|
||||
lightSpaceMatrixFourParam.SetValue(LightSpaceMatrixFour);
|
||||
|
||||
viewMatrixParam.SetValue(ViewMatrix);
|
||||
cascadeFarPlanesParam.SetValue(CascadeFarPlanes);
|
||||
|
||||
directionalLightColorParam.SetValue(DirectionalLightColor);
|
||||
directionalLightDirectionParam.SetValue(DirectionalLightDirection);
|
||||
|
||||
eyePositionParam.SetValue(EyePosition);
|
||||
lightSpaceMatrixParam.SetValue(LightSpaceMatrix);
|
||||
}
|
||||
|
||||
void CacheEffectParameters()
|
||||
|
@ -97,11 +132,22 @@ namespace Kav
|
|||
gAlbedoParam = Parameters["gAlbedo"];
|
||||
gNormalParam = Parameters["gNormal"];
|
||||
gMetallicRoughnessParam = Parameters["gMetallicRoughness"];
|
||||
shadowMapParam = Parameters["shadowMap"];
|
||||
|
||||
shadowMapOneParam = Parameters["shadowMapOne"];
|
||||
shadowMapTwoParam = Parameters["shadowMapTwo"];
|
||||
shadowMapThreeParam = Parameters["shadowMapThree"];
|
||||
shadowMapFourParam = Parameters["shadowMapFour"];
|
||||
|
||||
lightSpaceMatrixOneParam = Parameters["LightSpaceMatrixOne"];
|
||||
lightSpaceMatrixTwoParam = Parameters["LightSpaceMatrixTwo"];
|
||||
lightSpaceMatrixThreeParam = Parameters["LightSpaceMatrixThree"];
|
||||
lightSpaceMatrixFourParam = Parameters["LightSpaceMatrixFour"];
|
||||
|
||||
viewMatrixParam = Parameters["ViewMatrix"];
|
||||
cascadeFarPlanesParam = Parameters["CascadeFarPlanes"];
|
||||
|
||||
directionalLightDirectionParam = Parameters["DirectionalLightDirection"];
|
||||
directionalLightColorParam = Parameters["DirectionalLightColor"];
|
||||
lightSpaceMatrixParam = Parameters["LightSpaceMatrix"];
|
||||
|
||||
eyePositionParam = Parameters["EyePosition"];
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -2,12 +2,16 @@
|
|||
|
||||
static const float PI = 3.141592653589793;
|
||||
static const int MAX_POINT_LIGHTS = 64;
|
||||
static const int NUM_SHADOW_CASCADES = 4;
|
||||
|
||||
DECLARE_TEXTURE(gPosition, 0);
|
||||
DECLARE_TEXTURE(gAlbedo, 1);
|
||||
DECLARE_TEXTURE(gNormal, 2);
|
||||
DECLARE_TEXTURE(gMetallicRoughness, 3);
|
||||
DECLARE_TEXTURE(shadowMap, 4);
|
||||
DECLARE_TEXTURE(shadowMapOne, 4);
|
||||
DECLARE_TEXTURE(shadowMapTwo, 5);
|
||||
DECLARE_TEXTURE(shadowMapThree, 6);
|
||||
DECLARE_TEXTURE(shadowMapFour, 7);
|
||||
|
||||
BEGIN_CONSTANTS
|
||||
|
||||
|
@ -19,9 +23,17 @@ BEGIN_CONSTANTS
|
|||
float3 DirectionalLightDirection _ps(c129) _cb(c129);
|
||||
float3 DirectionalLightColor _ps(c130) _cb(c130);
|
||||
|
||||
float CascadeFarPlanes[NUM_SHADOW_CASCADES] _ps(c131) _cb(c131);
|
||||
|
||||
MATRIX_CONSTANTS
|
||||
|
||||
float4x4 LightSpaceMatrix _ps(c131) _cb(c131);
|
||||
float4x4 LightSpaceMatrixOne _ps(c135) _cb(c135);
|
||||
float4x4 LightSpaceMatrixTwo _ps(c139) _cb(c139);
|
||||
float4x4 LightSpaceMatrixThree _ps(c143) _cb(c143);
|
||||
float4x4 LightSpaceMatrixFour _ps(c147) _cb(c147);
|
||||
|
||||
// used to select shadow cascade
|
||||
float4x4 ViewMatrix _ps(c151) _cb(c151);
|
||||
|
||||
END_CONSTANTS
|
||||
|
||||
|
@ -89,11 +101,44 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness)
|
|||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
float ComputeShadow(float4 positionLightSpace, float3 N, float L)
|
||||
float ComputeShadow(float3 positionWorldSpace, float3 N, float L)
|
||||
{
|
||||
float bias = 0.005 * tan(acos(dot(N, L)));
|
||||
bias = clamp(bias, 0, 0.01);
|
||||
|
||||
float4 positionCameraSpace = mul(float4(positionWorldSpace, 1.0), ViewMatrix);
|
||||
|
||||
int shadowCascadeIndex = 0; // 0 is closest
|
||||
for (int i = 0; i < NUM_SHADOW_CASCADES; i++)
|
||||
{
|
||||
if (positionCameraSpace.z < CascadeFarPlanes[i])
|
||||
{
|
||||
shadowCascadeIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float4x4 lightSpaceMatrix;
|
||||
|
||||
if (shadowCascadeIndex == 0)
|
||||
{
|
||||
lightSpaceMatrix = LightSpaceMatrixOne;
|
||||
}
|
||||
else if (shadowCascadeIndex == 1)
|
||||
{
|
||||
lightSpaceMatrix = LightSpaceMatrixTwo;
|
||||
}
|
||||
else if (shadowCascadeIndex == 2)
|
||||
{
|
||||
lightSpaceMatrix = LightSpaceMatrixThree;
|
||||
}
|
||||
else
|
||||
{
|
||||
lightSpaceMatrix = LightSpaceMatrixFour;
|
||||
}
|
||||
|
||||
float4 positionLightSpace = mul(float4(positionWorldSpace, 1.0), lightSpaceMatrix);
|
||||
|
||||
// maps to [-1, 1]
|
||||
float3 projectionCoords = positionLightSpace.xyz / positionLightSpace.w;
|
||||
|
||||
|
@ -102,7 +147,24 @@ float ComputeShadow(float4 positionLightSpace, float3 N, float L)
|
|||
projectionCoords.y = (projectionCoords.y * 0.5) + 0.5;
|
||||
projectionCoords.y *= -1;
|
||||
|
||||
float closestDepth = SAMPLE_TEXTURE(shadowMap, projectionCoords.xy).r;
|
||||
float closestDepth;
|
||||
if (shadowCascadeIndex == 0)
|
||||
{
|
||||
closestDepth = SAMPLE_TEXTURE(shadowMapOne, projectionCoords.xy).r;
|
||||
}
|
||||
else if (shadowCascadeIndex == 1)
|
||||
{
|
||||
closestDepth = SAMPLE_TEXTURE(shadowMapTwo, projectionCoords.xy).r;
|
||||
}
|
||||
else if (shadowCascadeIndex == 2)
|
||||
{
|
||||
closestDepth = SAMPLE_TEXTURE(shadowMapThree, projectionCoords.xy).r;
|
||||
}
|
||||
else
|
||||
{
|
||||
closestDepth = SAMPLE_TEXTURE(shadowMapFour, projectionCoords.xy).r;
|
||||
}
|
||||
|
||||
float currentDepth = projectionCoords.z;
|
||||
|
||||
if (currentDepth - bias > closestDepth)
|
||||
|
@ -176,8 +238,7 @@ float4 ComputeColor(
|
|||
float3 L = normalize(DirectionalLightDirection);
|
||||
float3 radiance = DirectionalLightColor;
|
||||
|
||||
float4 positionLightSpace = mul(float4(worldPosition, 1.0), LightSpaceMatrix);
|
||||
float shadow = ComputeShadow(positionLightSpace, N, L);
|
||||
float shadow = ComputeShadow(worldPosition, N, L);
|
||||
|
||||
Lo += ComputeLight(L, radiance, F0, V, N, albedo, metallic, roughness, (1.0 - shadow));
|
||||
|
||||
|
|
176
Renderer.cs
176
Renderer.cs
|
@ -6,12 +6,15 @@ namespace Kav
|
|||
{
|
||||
public class Renderer
|
||||
{
|
||||
private const int MAX_SHADOW_CASCADES = 4;
|
||||
|
||||
private GraphicsDevice GraphicsDevice { get; }
|
||||
private int RenderDimensionsX { get; }
|
||||
private int RenderDimensionsY { get; }
|
||||
|
||||
private VertexBuffer FullscreenTriangle { get; }
|
||||
private RenderTarget2D ShadowRenderTarget { get; }
|
||||
private int NumShadowCascades { get; }
|
||||
private RenderTarget2D[] ShadowRenderTargets { get; }
|
||||
|
||||
private DeferredPBREffect DeferredPBREffect { get; }
|
||||
private SimpleDepthEffect SimpleDepthEffect { get; }
|
||||
|
@ -24,15 +27,18 @@ namespace Kav
|
|||
|
||||
private RenderTargetBinding[] GBuffer { get; }
|
||||
|
||||
private SpriteBatch SpriteBatch { get; }
|
||||
|
||||
public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY)
|
||||
public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY, int numShadowCascades)
|
||||
{
|
||||
GraphicsDevice = graphicsDevice;
|
||||
RenderDimensionsX = renderDimensionsX;
|
||||
RenderDimensionsY = renderDimensionsY;
|
||||
|
||||
ShadowRenderTarget = new RenderTarget2D(
|
||||
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,
|
||||
1024,
|
||||
1024,
|
||||
|
@ -40,6 +46,7 @@ namespace Kav
|
|||
SurfaceFormat.Single,
|
||||
DepthFormat.Depth24
|
||||
);
|
||||
}
|
||||
|
||||
gPosition = new RenderTarget2D(
|
||||
GraphicsDevice,
|
||||
|
@ -100,21 +107,18 @@ namespace Kav
|
|||
new VertexPositionTexture(new Vector3(3, -1, 0), new Vector2(2, 0))
|
||||
});
|
||||
|
||||
SpriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
|
||||
GraphicsDevice.SetRenderTarget(deferredRenderTarget);
|
||||
graphicsDevice.Clear(Color.White);
|
||||
GraphicsDevice.SetRenderTarget(null);
|
||||
}
|
||||
|
||||
public void DeferredRender(
|
||||
Camera camera,
|
||||
PerspectiveCamera camera,
|
||||
IEnumerable<(Model, Matrix)> modelTransforms,
|
||||
IEnumerable<PointLight> pointLights,
|
||||
DirectionalLight directionalLight,
|
||||
int numCascades
|
||||
DirectionalLight directionalLight
|
||||
) {
|
||||
ShadowMapRender(camera, modelTransforms, directionalLight, numCascades);
|
||||
ShadowMapRender(camera, modelTransforms, directionalLight);
|
||||
|
||||
GraphicsDevice.SetRenderTargets(GBuffer);
|
||||
GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1f, 0);
|
||||
|
@ -161,7 +165,23 @@ namespace Kav
|
|||
DeferredPBREffect.GAlbedo = gAlbedo;
|
||||
DeferredPBREffect.GNormal = gNormal;
|
||||
DeferredPBREffect.GMetallicRoughness = gMetallicRoughness;
|
||||
DeferredPBREffect.ShadowMap = ShadowRenderTarget;
|
||||
|
||||
DeferredPBREffect.ShadowMapOne = ShadowRenderTargets[0];
|
||||
if (NumShadowCascades > 1)
|
||||
{
|
||||
DeferredPBREffect.ShadowMapTwo = ShadowRenderTargets[1];
|
||||
}
|
||||
if (NumShadowCascades > 2)
|
||||
{
|
||||
DeferredPBREffect.ShadowMapThree = ShadowRenderTargets[2];
|
||||
}
|
||||
if (NumShadowCascades > 3)
|
||||
{
|
||||
DeferredPBREffect.ShadowMapFour = ShadowRenderTargets[3];
|
||||
}
|
||||
|
||||
DeferredPBREffect.ViewMatrix = camera.View;
|
||||
|
||||
DeferredPBREffect.EyePosition = Matrix.Invert(camera.View).Translation;
|
||||
|
||||
int i = 0;
|
||||
|
@ -182,19 +202,66 @@ namespace Kav
|
|||
GraphicsDevice.SetVertexBuffer(FullscreenTriangle);
|
||||
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 1);
|
||||
}
|
||||
|
||||
// SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, DeferredPBREffect);
|
||||
// SpriteBatch.Draw(deferredRenderTarget, Vector2.Zero, Color.White);
|
||||
// SpriteBatch.End();
|
||||
}
|
||||
|
||||
public void ShadowMapRender(
|
||||
Camera camera,
|
||||
PerspectiveCamera camera,
|
||||
IEnumerable<(Model, Matrix)> modelTransforms,
|
||||
DirectionalLight directionalLight
|
||||
) {
|
||||
// set up global light matrix
|
||||
|
||||
var right = Vector3.Cross(Vector3.Up, directionalLight.Direction);
|
||||
var up = Vector3.Cross(directionalLight.Direction, right);
|
||||
var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
|
||||
Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners();
|
||||
|
||||
Vector3 frustumCenter = Vector3.Zero;
|
||||
for (var i = 0; i < frustumCorners.Length; i++)
|
||||
{
|
||||
frustumCenter += frustumCorners[i];
|
||||
}
|
||||
frustumCenter /= 8f;
|
||||
|
||||
var lightView = Matrix.CreateLookAt(frustumCenter, frustumCenter - directionalLight.Direction, camera.View.Right);
|
||||
|
||||
for (var i = 0; i < frustumCorners.Length; i++)
|
||||
{
|
||||
frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView);
|
||||
}
|
||||
|
||||
BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);
|
||||
Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z;
|
||||
|
||||
// render the individual shadow maps
|
||||
|
||||
var frustumDistance = camera.FarPlane - camera.NearPlane;
|
||||
var sectionDistance = frustumDistance / NumShadowCascades;
|
||||
|
||||
for (var i = 0; i < NumShadowCascades; i++)
|
||||
{
|
||||
// divide the view frustum
|
||||
var shadowCamera = new PerspectiveCamera(
|
||||
camera.Position,
|
||||
camera.Forward,
|
||||
camera.Up,
|
||||
camera.FieldOfView,
|
||||
camera.AspectRatio,
|
||||
camera.NearPlane + (i * sectionDistance),
|
||||
camera.NearPlane + ((i + 1) * sectionDistance)
|
||||
);
|
||||
|
||||
RenderShadowMap(shadowCamera, modelTransforms, directionalLight, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderShadowMap(
|
||||
PerspectiveCamera camera,
|
||||
IEnumerable<(Model, Matrix)> modelTransforms,
|
||||
DirectionalLight directionalLight,
|
||||
int numCascades
|
||||
int shadowCascadeIndex
|
||||
) {
|
||||
GraphicsDevice.SetRenderTarget(ShadowRenderTarget);
|
||||
GraphicsDevice.SetRenderTarget(ShadowRenderTargets[shadowCascadeIndex]);
|
||||
GraphicsDevice.Clear(Color.White);
|
||||
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
|
||||
GraphicsDevice.BlendState = BlendState.Opaque;
|
||||
|
@ -230,70 +297,27 @@ namespace Kav
|
|||
0,
|
||||
lightBox.Max.Z - lightBox.Min.Z
|
||||
);
|
||||
DeferredPBREffect.LightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection;
|
||||
|
||||
foreach (var (model, transform) in modelTransforms)
|
||||
{
|
||||
foreach (var modelMesh in model.Meshes)
|
||||
{
|
||||
foreach (var meshPart in modelMesh.MeshParts)
|
||||
{
|
||||
GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer);
|
||||
GraphicsDevice.Indices = meshPart.IndexBuffer;
|
||||
var lightSpaceMatrix = SimpleDepthEffect.View * SimpleDepthEffect.Projection;
|
||||
|
||||
SimpleDepthEffect.Model = transform;
|
||||
|
||||
foreach (var pass in SimpleDepthEffect.CurrentTechnique.Passes)
|
||||
if (shadowCascadeIndex == 0)
|
||||
{
|
||||
pass.Apply();
|
||||
|
||||
GraphicsDevice.DrawIndexedPrimitives(
|
||||
PrimitiveType.TriangleList,
|
||||
0,
|
||||
0,
|
||||
meshPart.VertexBuffer.VertexCount,
|
||||
0,
|
||||
meshPart.Triangles.Length
|
||||
);
|
||||
}
|
||||
DeferredPBREffect.LightSpaceMatrixOne = lightSpaceMatrix;
|
||||
}
|
||||
else if (shadowCascadeIndex == 1)
|
||||
{
|
||||
DeferredPBREffect.LightSpaceMatrixTwo = lightSpaceMatrix;
|
||||
}
|
||||
else if (shadowCascadeIndex == 2)
|
||||
{
|
||||
DeferredPBREffect.LightSpaceMatrixThree = lightSpaceMatrix;
|
||||
}
|
||||
else if (shadowCascadeIndex == 3)
|
||||
{
|
||||
DeferredPBREffect.LightSpaceMatrixFour = lightSpaceMatrix;
|
||||
}
|
||||
|
||||
private void RenderShadowMap(Camera camera, IEnumerable<(Model, Matrix)> modelTransforms, DirectionalLight directionalLight)
|
||||
{
|
||||
var right = Vector3.Cross(Vector3.Up, directionalLight.Direction);
|
||||
var up = Vector3.Cross(directionalLight.Direction, right);
|
||||
var cameraBoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
|
||||
Vector3[] frustumCorners = cameraBoundingFrustum.GetCorners();
|
||||
|
||||
Vector3 frustumCenter = Vector3.Zero;
|
||||
for (var i = 0; i < frustumCorners.Length; i++)
|
||||
{
|
||||
frustumCenter += frustumCorners[i];
|
||||
}
|
||||
frustumCenter /= 8f;
|
||||
|
||||
var lightView = Matrix.CreateLookAt(frustumCenter, frustumCenter - directionalLight.Direction, camera.View.Right);
|
||||
|
||||
for (var i = 0; i < frustumCorners.Length; i++)
|
||||
{
|
||||
frustumCorners[i] = Vector3.Transform(frustumCorners[i], lightView);
|
||||
}
|
||||
|
||||
BoundingBox lightBox = BoundingBox.CreateFromPoints(frustumCorners);
|
||||
Vector3 lightPosition = frustumCenter + directionalLight.Direction * -lightBox.Min.Z;
|
||||
|
||||
SimpleDepthEffect.View = Matrix.CreateLookAt(lightPosition, frustumCenter, camera.View.Right);
|
||||
SimpleDepthEffect.Projection = Matrix.CreateOrthographicOffCenter(
|
||||
lightBox.Min.X,
|
||||
lightBox.Max.X,
|
||||
lightBox.Min.Y,
|
||||
lightBox.Max.Y,
|
||||
0,
|
||||
lightBox.Max.Z - lightBox.Min.Z
|
||||
);
|
||||
DeferredPBREffect.CascadeFarPlanes[shadowCascadeIndex] = camera.FarPlane;
|
||||
|
||||
foreach (var (model, transform) in modelTransforms)
|
||||
{
|
||||
|
@ -325,7 +349,7 @@ namespace Kav
|
|||
}
|
||||
|
||||
public void Render(
|
||||
Camera camera,
|
||||
PerspectiveCamera camera,
|
||||
IEnumerable<(Model, Matrix)> modelTransforms,
|
||||
IEnumerable<PointLight> pointLights,
|
||||
IEnumerable<DirectionalLight> directionalLights
|
||||
|
|
Loading…
Reference in New Issue