Esempio n. 1
0
 public override void ApplyViewParameters(RenderDrawContext context, ParameterCollection parameters, LightShadowMapTexture shadowMapTexture)
 {
     parameters.Set(ShadowMapCasterParaboloidProjectionKeys.DepthParameters, GetShadowMapDepthParameters(shadowMapTexture));
 }
Esempio n. 2
0
        private Vector2 ComputeCascadeSplits(RenderContext context, RenderView sourceView, ref LightShadowMapTexture lightShadowMap)
        {
            var shadow = (LightDirectionalShadowMap)lightShadowMap.Shadow;

            var cameraNear  = sourceView.NearClipPlane;
            var cameraFar   = sourceView.FarClipPlane;
            var cameraRange = cameraFar - cameraNear;

            var minDistance = cameraNear + LightDirectionalShadowMap.DepthRangeParameters.DefaultMinDistance;
            var maxDistance = cameraNear + LightDirectionalShadowMap.DepthRangeParameters.DefaultMaxDistance;

            if (shadow.DepthRange.IsAutomatic)
            {
                minDistance = Math.Max(sourceView.MinimumDistance, cameraNear);
                maxDistance = Math.Max(sourceView.MaximumDistance, minDistance);

                if (lightShadowMap.CurrentMinDistance <= 0)
                {
                    lightShadowMap.CurrentMinDistance = minDistance;
                }

                if (lightShadowMap.CurrentMaxDistance <= 0)
                {
                    lightShadowMap.CurrentMaxDistance = maxDistance;
                }

                // Increase the maximum depth in small logarithmic steps, decrease it in larger logarithmic steps
                var threshold = maxDistance > lightShadowMap.CurrentMaxDistance ? DepthIncreaseThreshold : DepthDecreaseThreshold;
                maxDistance = lightShadowMap.CurrentMaxDistance = LogCeiling(maxDistance / lightShadowMap.CurrentMaxDistance, threshold) * lightShadowMap.CurrentMaxDistance;

                // Increase/decrease the distance between maximum and minimum depth in small/large logarithmic steps
                var range        = maxDistance - minDistance;
                var currentRange = lightShadowMap.CurrentMaxDistance - lightShadowMap.CurrentMinDistance;
                threshold   = range > currentRange ? DepthIncreaseThreshold : DepthDecreaseThreshold;
                minDistance = maxDistance - LogCeiling(range / currentRange, threshold) * currentRange;
                minDistance = lightShadowMap.CurrentMinDistance = Math.Max(minDistance, cameraNear);
            }
            else
            {
                minDistance = cameraNear + shadow.DepthRange.ManualMinDistance;
                maxDistance = cameraNear + shadow.DepthRange.ManualMaxDistance;
            }

            var manualPartitionMode      = shadow.PartitionMode as LightDirectionalShadowMap.PartitionManual;
            var logarithmicPartitionMode = shadow.PartitionMode as LightDirectionalShadowMap.PartitionLogarithmic;

            if (logarithmicPartitionMode != null)
            {
                var minZ = minDistance;
                var maxZ = maxDistance;

                var range    = maxZ - minZ;
                var ratio    = maxZ / minZ;
                var logRatio = MathUtil.Clamp(1.0f - logarithmicPartitionMode.PSSMFactor, 0.0f, 1.0f);

                for (int cascadeLevel = 0; cascadeLevel < lightShadowMap.CascadeCount; ++cascadeLevel)
                {
                    // Compute cascade split (between znear and zfar)
                    float distrib  = (float)(cascadeLevel + 1) / lightShadowMap.CascadeCount;
                    float logZ     = (float)(minZ * Math.Pow(ratio, distrib));
                    float uniformZ = minZ + range * distrib;
                    float distance = MathUtil.Lerp(uniformZ, logZ, logRatio);
                    cascadeSplitRatios[cascadeLevel] = distance;
                }
            }
            else if (manualPartitionMode != null)
            {
                if (lightShadowMap.CascadeCount == 1)
                {
                    cascadeSplitRatios[0] = minDistance + manualPartitionMode.SplitDistance1 * maxDistance;
                }
                else if (lightShadowMap.CascadeCount == 2)
                {
                    cascadeSplitRatios[0] = minDistance + manualPartitionMode.SplitDistance1 * maxDistance;
                    cascadeSplitRatios[1] = minDistance + manualPartitionMode.SplitDistance3 * maxDistance;
                }
                else if (lightShadowMap.CascadeCount == 4)
                {
                    cascadeSplitRatios[0] = minDistance + manualPartitionMode.SplitDistance0 * maxDistance;
                    cascadeSplitRatios[1] = minDistance + manualPartitionMode.SplitDistance1 * maxDistance;
                    cascadeSplitRatios[2] = minDistance + manualPartitionMode.SplitDistance2 * maxDistance;
                    cascadeSplitRatios[3] = minDistance + manualPartitionMode.SplitDistance3 * maxDistance;
                }
            }

            // Convert distance splits to ratios cascade in the range [0, 1]
            for (int i = 0; i < cascadeSplitRatios.Length; i++)
            {
                cascadeSplitRatios[i] = (cascadeSplitRatios[i] - cameraNear) / cameraRange;
            }

            return(new Vector2(minDistance, maxDistance));
        }
Esempio n. 3
0
 private float GetShadowMapFarPlane(LightShadowMapTexture shadowMapTexture)
 {
     return(GetLightClippingPlanes(shadowMapTexture.Light as LightPoint).Y);
 }
Esempio n. 4
0
        public override void Collect(RenderContext context, RenderView sourceView, LightShadowMapTexture lightShadowMap)
        {
            var shadow = (LightDirectionalShadowMap)lightShadowMap.Shadow;

            // TODO: Min and Max distance can be auto-computed from readback from Z buffer

            var viewToWorld = sourceView.View;

            viewToWorld.Invert();

            // Update the frustum infos
            UpdateFrustum(sourceView);

            // Computes the cascade splits
            var minMaxDistance = ComputeCascadeSplits(context, sourceView, ref lightShadowMap);
            var direction      = lightShadowMap.LightComponent.Direction;

            // Fake value
            // It will be setup by next loop
            Vector3 side        = Vector3.UnitX;
            Vector3 upDirection = Vector3.UnitX;

            // Select best Up vector
            // TODO: User preference?
            foreach (var vectorUp in VectorUps)
            {
                if (Math.Abs(Vector3.Dot(direction, vectorUp)) < (1.0 - 0.0001))
                {
                    side        = Vector3.Normalize(Vector3.Cross(vectorUp, direction));
                    upDirection = Vector3.Normalize(Vector3.Cross(direction, side));
                    break;
                }
            }

            int cascadeCount = lightShadowMap.CascadeCount;

            // Get new shader data from pool
            ShaderData shaderData;

            if (cascadeCount == 1)
            {
                shaderData = shaderDataPoolCascade1.Add();
            }
            else if (cascadeCount == 2)
            {
                shaderData = shaderDataPoolCascade2.Add();
            }
            else
            {
                shaderData = shaderDataPoolCascade4.Add();
            }
            lightShadowMap.ShaderData = shaderData;
            shaderData.Texture        = lightShadowMap.Atlas.Texture;
            shaderData.DepthBias      = shadow.BiasParameters.DepthBias;
            shaderData.OffsetScale    = shadow.BiasParameters.NormalOffsetScale;

            float splitMaxRatio = (minMaxDistance.X - sourceView.NearClipPlane) / (sourceView.FarClipPlane - sourceView.NearClipPlane);
            float splitMinRatio = 0;

            for (int cascadeLevel = 0; cascadeLevel < cascadeCount; ++cascadeLevel)
            {
                var oldSplitMinRatio = splitMinRatio;
                // Calculate frustum corners for this cascade
                splitMinRatio = splitMaxRatio;
                splitMaxRatio = cascadeSplitRatios[cascadeLevel];

                for (int j = 0; j < 4; j++)
                {
                    // Calculate frustum in WS and VS
                    float overlap = 0;
                    if (cascadeLevel > 0 && shadow.DepthRange.IsBlendingCascades)
                    {
                        overlap = 0.2f * (splitMinRatio - oldSplitMinRatio);
                    }

                    var frustumRangeWS = frustumCornersWS[j + 4] - frustumCornersWS[j];
                    var frustumRangeVS = frustumCornersVS[j + 4] - frustumCornersVS[j];

                    cascadeFrustumCornersWS[j]     = frustumCornersWS[j] + frustumRangeWS * (splitMinRatio - overlap);
                    cascadeFrustumCornersWS[j + 4] = frustumCornersWS[j] + frustumRangeWS * splitMaxRatio;


                    cascadeFrustumCornersVS[j]     = frustumCornersVS[j] + frustumRangeVS * (splitMinRatio - overlap);
                    cascadeFrustumCornersVS[j + 4] = frustumCornersVS[j] + frustumRangeVS * splitMaxRatio;
                }

                Vector3 cascadeMinBoundLS;
                Vector3 cascadeMaxBoundLS;
                Vector3 target;

                if (shadow.StabilizationMode == LightShadowMapStabilizationMode.ViewSnapping || shadow.StabilizationMode == LightShadowMapStabilizationMode.ProjectionSnapping)
                {
                    // Make sure we are using the same direction when stabilizing
                    var boundingVS = BoundingSphere.FromPoints(cascadeFrustumCornersVS);

                    // Compute bounding box center & radius
                    target = Vector3.TransformCoordinate(boundingVS.Center, viewToWorld);
                    var radius = boundingVS.Radius;

                    //if (shadow.AutoComputeMinMax)
                    //{
                    //    var snapRadius = (float)Math.Ceiling(radius / snapRadiusValue) * snapRadiusValue;
                    //    Debug.WriteLine("Radius: {0} SnapRadius: {1} (snap: {2})", radius, snapRadius, snapRadiusValue);
                    //    radius = snapRadius;
                    //}

                    cascadeMaxBoundLS = new Vector3(radius, radius, radius);
                    cascadeMinBoundLS = -cascadeMaxBoundLS;

                    if (shadow.StabilizationMode == LightShadowMapStabilizationMode.ViewSnapping)
                    {
                        // Snap camera to texel units (so that shadow doesn't jitter when light doesn't change direction but camera is moving)
                        // Technique from ShaderX7 - Practical Cascaded Shadows Maps -  p310-311
                        var   shadowMapHalfSize = lightShadowMap.Size * 0.5f;
                        float x = (float)Math.Ceiling(Vector3.Dot(target, upDirection) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize;
                        float y = (float)Math.Ceiling(Vector3.Dot(target, side) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize;
                        float z = Vector3.Dot(target, direction);

                        //target = up * x + side * y + direction * R32G32B32_Float.Dot(target, direction);
                        target = upDirection * x + side * y + direction * z;
                    }
                }
                else
                {
                    var cascadeBoundWS = BoundingBox.FromPoints(cascadeFrustumCornersWS);
                    target = cascadeBoundWS.Center;

                    // Computes the bouding box of the frustum cascade in light space
                    var lightViewMatrix = Matrix.LookAtRH(target, target + direction, upDirection);
                    cascadeMinBoundLS = new Vector3(float.MaxValue);
                    cascadeMaxBoundLS = new Vector3(-float.MaxValue);
                    for (int i = 0; i < cascadeFrustumCornersWS.Length; i++)
                    {
                        Vector3 cornerViewSpace;
                        Vector3.TransformCoordinate(ref cascadeFrustumCornersWS[i], ref lightViewMatrix, out cornerViewSpace);

                        cascadeMinBoundLS = Vector3.Min(cascadeMinBoundLS, cornerViewSpace);
                        cascadeMaxBoundLS = Vector3.Max(cascadeMaxBoundLS, cornerViewSpace);
                    }

                    // TODO: Adjust orthoSize by taking into account filtering size
                }

                // Update the shadow camera. The calculation of the eye position assumes RH coordinates.
                var    viewMatrix       = Matrix.LookAtRH(target - direction * cascadeMaxBoundLS.Z, target, upDirection); // View;;
                var    nearClip         = 0.0f;
                var    farClip          = cascadeMaxBoundLS.Z - cascadeMinBoundLS.Z;
                var    projectionMatrix = Matrix.OrthoOffCenterRH(cascadeMinBoundLS.X, cascadeMaxBoundLS.X, cascadeMinBoundLS.Y, cascadeMaxBoundLS.Y, nearClip, farClip); // Projection
                Matrix viewProjectionMatrix;
                Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out viewProjectionMatrix);

                // Stabilize the Shadow matrix on the projection
                if (shadow.StabilizationMode == LightShadowMapStabilizationMode.ProjectionSnapping)
                {
                    var shadowPixelPosition = viewProjectionMatrix.TranslationVector * lightShadowMap.Size * 0.5f; // shouln't it be scale and not translation ?
                    shadowPixelPosition.Z = 0;
                    var shadowPixelPositionRounded = new Vector3((float)Math.Round(shadowPixelPosition.X), (float)Math.Round(shadowPixelPosition.Y), 0.0f);

                    var shadowPixelOffset = new Vector4(shadowPixelPositionRounded - shadowPixelPosition, 0.0f);
                    shadowPixelOffset     *= 2.0f / lightShadowMap.Size;
                    projectionMatrix.Row4 += shadowPixelOffset;
                    Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out viewProjectionMatrix);
                }

                shaderData.ViewMatrix[cascadeLevel]       = viewMatrix;
                shaderData.ProjectionMatrix[cascadeLevel] = projectionMatrix;
                shaderData.DepthRange[cascadeLevel]       = new Vector2(nearClip, farClip); //////////////////////

                // Cascade splits in light space using depth: Store depth on first CascaderCasterMatrix in last column of each row
                shaderData.CascadeSplits[cascadeLevel] = MathUtil.Lerp(sourceView.NearClipPlane, sourceView.FarClipPlane, cascadeSplitRatios[cascadeLevel]);

                var shadowMapRectangle = lightShadowMap.GetRectangle(cascadeLevel);

                var cascadeTextureCoords = new Vector4((float)shadowMapRectangle.Left / lightShadowMap.Atlas.Width,
                                                       (float)shadowMapRectangle.Top / lightShadowMap.Atlas.Height,
                                                       (float)shadowMapRectangle.Right / lightShadowMap.Atlas.Width,
                                                       (float)shadowMapRectangle.Bottom / lightShadowMap.Atlas.Height);

                shaderData.TextureCoords[cascadeLevel] = cascadeTextureCoords;

                //// Add border (avoid using edges due to bilinear filtering and blur)
                //var borderSizeU = VsmBlurSize / lightShadowMap.Atlas.Width;
                //var borderSizeV = VsmBlurSize / lightShadowMap.Atlas.Height;
                //cascadeTextureCoords.X += borderSizeU;
                //cascadeTextureCoords.Y += borderSizeV;
                //cascadeTextureCoords.Z -= borderSizeU;
                //cascadeTextureCoords.W -= borderSizeV;

                float leftX   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Width * 0.5f;
                float leftY   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Height * 0.5f;
                float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z);
                float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W);

                // Compute receiver view proj matrix
                Matrix adjustmentMatrix = Matrix.Scaling(leftX, -leftY, 1.0f) * Matrix.Translation(centerX, centerY, 0.0f);
                // Calculate View Proj matrix from World space to Cascade space
                Matrix.Multiply(ref viewProjectionMatrix, ref adjustmentMatrix, out shaderData.WorldToShadowCascadeUV[cascadeLevel]);

                // Allocate shadow render view
                var shadowRenderView = CreateRenderView();
                shadowRenderView.RenderView       = sourceView;
                shadowRenderView.ShadowMapTexture = lightShadowMap;
                shadowRenderView.Rectangle        = shadowMapRectangle;
                shadowRenderView.View             = viewMatrix;
                shadowRenderView.ViewSize         = new Vector2(shadowMapRectangle.Width, shadowMapRectangle.Height);
                shadowRenderView.Projection       = projectionMatrix;
                shadowRenderView.ViewProjection   = viewProjectionMatrix;
                shadowRenderView.NearClipPlane    = nearClip;
                shadowRenderView.FarClipPlane     = farClip;

                // Add the render view for the current frame
                context.RenderSystem.Views.Add(shadowRenderView);
            }
        }
        public override void Collect(RenderContext context, RenderView sourceView, LightShadowMapTexture lightShadowMap)
        {
            // TODO: Min and Max distance can be auto-computed from readback from Z buffer  // Yeah sure... good luck with that.
            var shadow = (LightStandardShadowMap)lightShadowMap.Shadow;

            // Computes the cascade splits
            var lightComponent = lightShadowMap.LightComponent;
            var spotLight      = (LightSpot)lightComponent.Type;

            // Get new shader data from pool
            var shaderData = shaderDataPool.Add();

            lightShadowMap.ShaderData = shaderData;
            shaderData.Texture        = lightShadowMap.Atlas.Texture;

            shaderData.DepthBias   = shadow.BiasParameters.DepthBias;
            shaderData.OffsetScale = shadow.BiasParameters.NormalOffsetScale;

            // TODO: Calculation of near and far is hardcoded/approximated. We should find a better way to calculate it.
            var nearClip = 0.01f;                                   // TODO: This should be configurable.
            var farClip  = spotLight.Range * 2.0f;                  // TODO: For some reason this multiplication by two is required. This should be investigated and fixed properly.

            shaderData.DepthRange = new Vector2(nearClip, farClip); //////////////////////////////////////////

            // Update the shadow camera
            var viewMatrix = lightComponent.Entity.Transform.WorldMatrix;

            viewMatrix.Invert();
            var projectionMatrix = Matrix.PerspectiveFovRH(spotLight.AngleOuterInRadians, spotLight.AspectRatio, nearClip, farClip); // Perspective Projection for spotlights

            Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out var viewProjectionMatrix);

            var shadowMapRectangle = lightShadowMap.GetRectangle(0);

            var cascadeTextureCoords = new Vector4(
                (float)shadowMapRectangle.Left / lightShadowMap.Atlas.Width,
                (float)shadowMapRectangle.Top / lightShadowMap.Atlas.Height,
                (float)shadowMapRectangle.Right / lightShadowMap.Atlas.Width,
                (float)shadowMapRectangle.Bottom / lightShadowMap.Atlas.Height);

            //// Add border (avoid using edges due to bilinear filtering and blur)
            //var borderSizeU = VsmBlurSize / lightShadowMap.Atlas.Width;
            //var borderSizeV = VsmBlurSize / lightShadowMap.Atlas.Height;
            //cascadeTextureCoords.X += borderSizeU;
            //cascadeTextureCoords.Y += borderSizeV;
            //cascadeTextureCoords.Z -= borderSizeU;
            //cascadeTextureCoords.W -= borderSizeV;

            float leftX   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Width * 0.5f;
            float leftY   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Height * 0.5f;
            float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z);
            float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W);

            // Compute receiver view proj matrix
            Matrix adjustmentMatrix = Matrix.Scaling(leftX, -leftY, 1.0f) * Matrix.Translation(centerX, centerY, 0.0f);

            // Calculate View Proj matrix from World space to Cascade space
            Matrix.Multiply(ref viewProjectionMatrix, ref adjustmentMatrix, out shaderData.WorldToShadowCascadeUV);

            //Matrix rotationMatrix = Matrix.RotationZ(rotationZ);
            //Matrix.Multiply(ref viewProjectionMatrix, ref rotationMatrix, out shaderData.worldToShadowProjectiveTextureUV);

            shaderData.ViewMatrix       = viewMatrix;
            shaderData.ProjectionMatrix = projectionMatrix;

            // Allocate shadow render view
            var shadowRenderView = CreateRenderView();

            shadowRenderView.RenderView       = sourceView;
            shadowRenderView.ShadowMapTexture = lightShadowMap;
            shadowRenderView.Rectangle        = lightShadowMap.GetRectangle(0);
            // Compute view parameters
            shadowRenderView.View       = shaderData.ViewMatrix;
            shadowRenderView.Projection = shaderData.ProjectionMatrix;
            Matrix.Multiply(ref shadowRenderView.View, ref shadowRenderView.Projection, out shadowRenderView.ViewProjection);
            shadowRenderView.ViewSize      = new Vector2(shadowMapRectangle.Width, shadowMapRectangle.Height);
            shadowRenderView.NearClipPlane = nearClip;
            shadowRenderView.FarClipPlane  = farClip;

            // Add the render view for the current frame
            context.RenderSystem.Views.Add(shadowRenderView);

            // Collect objects in shadow views
            context.VisibilityGroup.TryCollect(shadowRenderView);
        }
Esempio n. 6
0
 public virtual void ApplyViewParameters(RenderDrawContext context, ParameterCollection parameters, LightShadowMapTexture shadowMapTexture)
 {
 }
Esempio n. 7
0
 public abstract void Collect(RenderContext context, RenderView sourceView, LightShadowMapTexture lightShadowMap);
        public override void Collect(RenderContext context, RenderView sourceView, LightShadowMapTexture lightShadowMap)
        {
            var shaderData = shaderDataPool.Add();

            lightShadowMap.ShaderData  = shaderData;
            shaderData.Texture         = lightShadowMap.Atlas.Texture;
            shaderData.DepthBias       = lightShadowMap.Light.Shadow.BiasParameters.DepthBias;
            shaderData.OffsetScale     = lightShadowMap.Light.Shadow.BiasParameters.NormalOffsetScale;
            shaderData.DepthParameters = GetShadowMapDepthParameters(lightShadowMap);

            var clippingPlanes = GetLightClippingPlanes((LightPoint)lightShadowMap.Light);

            var textureMapSize = lightShadowMap.GetRectangle(0).Size;

            // Calculate angle of the projection with border pixels taken into account to allow filtering
            float halfMapSize = (float)textureMapSize.Width / 2;
            float halfFov     = (float)Math.Atan((halfMapSize + BorderPixels) / halfMapSize);

            shaderData.Projection = Matrix.PerspectiveFovRH(halfFov * 2, 1.0f, clippingPlanes.X, clippingPlanes.Y);

            Vector2 atlasSize = new Vector2(lightShadowMap.Atlas.Width, lightShadowMap.Atlas.Height);

            for (int i = 0; i < 6; i++)
            {
                Rectangle faceRectangle = lightShadowMap.GetRectangle(i);
                shaderData.FaceOffsets[i] = new Vector2(faceRectangle.Left + BorderPixels, faceRectangle.Top + BorderPixels) / atlasSize;

                // Compute view parameters
                GetViewParameters(lightShadowMap, i, out shaderData.View[i]);

                // Allocate shadow render view
                var shadowRenderView = CreateRenderView();
                shadowRenderView.RenderView       = sourceView;
                shadowRenderView.ShadowMapTexture = lightShadowMap;
                shadowRenderView.Rectangle        = faceRectangle;
                shadowRenderView.ViewSize         = new Vector2(faceRectangle.Width, faceRectangle.Height);

                shadowRenderView.NearClipPlane = clippingPlanes.X;
                shadowRenderView.FarClipPlane  = clippingPlanes.Y;

                shadowRenderView.View       = shaderData.View[i];
                shadowRenderView.Projection = shaderData.Projection;
                Matrix.Multiply(ref shadowRenderView.View, ref shadowRenderView.Projection, out shadowRenderView.ViewProjection);

                // Create projection matrix with adjustment
                var textureCoords = new Vector4(
                    (float)shadowRenderView.Rectangle.Left / lightShadowMap.Atlas.Width,
                    (float)shadowRenderView.Rectangle.Top / lightShadowMap.Atlas.Height,
                    (float)shadowRenderView.Rectangle.Right / lightShadowMap.Atlas.Width,
                    (float)shadowRenderView.Rectangle.Bottom / lightShadowMap.Atlas.Height);
                float leftX   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Width * 0.5f;
                float leftY   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Height * 0.5f;
                float centerX = 0.5f * (textureCoords.X + textureCoords.Z);
                float centerY = 0.5f * (textureCoords.Y + textureCoords.W);

                var projectionToShadow = Matrix.Scaling(leftX, -leftY, 1.0f) * Matrix.Translation(centerX, centerY, 0.0f);
                Matrix.Multiply(ref shadowRenderView.ViewProjection, ref projectionToShadow, out shaderData.WorldToShadow[i]);

                shadowRenderView.VisiblityIgnoreDepthPlanes = false;

                // Add the render view for the current frame
                context.RenderSystem.Views.Add(shadowRenderView);
            }
        }