Exemple #1
0
        /// <summary>
        /// Determines the size of the frustum needed to cover the viewable area,
        /// then creates an appropriate orthographic projection.
        /// </summary>
        /// <param name="light">The directional light to use</param>
        /// <param name="mainCamera">The camera viewing the scene</param>
        /// <param name="minZ"></param>
        /// <param name="maxZ"></param>
        protected OrthographicCamera CalculateFrustum(DirectionalLight light, ICamera mainCamera, float minZ, float maxZ)
        {
            // Shorten the view frustum according to the shadow view distance
            Matrix cameraMatrix;

            cameraMatrix = mainCamera.ViewInverse;
            //mainCamera.GetWorldMatrix( out cameraMatrix );

            for (int i = 0; i < 4; i++)
            {
                splitFrustumCornersVS[i] = frustumCornersVS[i + 4] * (minZ / mainCamera.FarClip);
            }

            for (int i = 4; i < 8; i++)
            {
                splitFrustumCornersVS[i] = frustumCornersVS[i] * (maxZ / mainCamera.FarClip);
            }

            Vector3.TransformCoordinate(splitFrustumCornersVS, ref cameraMatrix, frustumCornersWS);

            // Find the centroid
            Vector3 frustumCentroid = new Vector3(0, 0, 0);

            for (int i = 0; i < 8; i++)
            {
                frustumCentroid += frustumCornersWS[i];
            }
            frustumCentroid *= (1 / 8.0F);

            // Position the shadow-caster camera so that it's looking at the centroid,
            // and backed up in the direction of the sunlight
            float  distFromCentroid = MathHelper.Max((maxZ - minZ), Vector3.Distance(splitFrustumCornersVS[4], splitFrustumCornersVS[5])) + 50.0f;
            Matrix viewMatrix       = Matrix.LookAtRH(frustumCentroid - (light.Direction * distFromCentroid), frustumCentroid, new Vector3(0, 1, 0));

            // Determine the position of the frustum corners in light space
            Vector3.TransformCoordinate(frustumCornersWS, ref viewMatrix, frustumCornersLS);

            // Calculate an orthographic projection by sizing a bounding box
            // to the frustum coordinates in light space
            Vector3 mins  = frustumCornersLS[0];
            Vector3 maxes = frustumCornersLS[0];

            for (int i = 0; i < 8; i++)
            {
                if (frustumCornersLS[i].X > maxes.X)
                {
                    maxes.X = frustumCornersLS[i].X;
                }
                else if (frustumCornersLS[i].X < mins.X)
                {
                    mins.X = frustumCornersLS[i].X;
                }
                if (frustumCornersLS[i].Y > maxes.Y)
                {
                    maxes.Y = frustumCornersLS[i].Y;
                }
                else if (frustumCornersLS[i].Y < mins.Y)
                {
                    mins.Y = frustumCornersLS[i].Y;
                }
                if (frustumCornersLS[i].Z > maxes.Z)
                {
                    maxes.Z = frustumCornersLS[i].Z;
                }
                else if (frustumCornersLS[i].Z < mins.Z)
                {
                    mins.Z = frustumCornersLS[i].Z;
                }
            }


            // Create an orthographic camera for use as a shadow caster
            const float        nearClipOffset = 100.0f;
            OrthographicCamera lightCamera    = new OrthographicCamera(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - nearClipOffset, -mins.Z);

            lightCamera.View = viewMatrix;



            if (RenderDebug)
            {
                game.LineManager3D.AddCenteredBox(frustumCentroid - (light.Direction * distFromCentroid), 1.0f,
                                                  new Color4(0, 1, 0));
                game.LineManager3D.AddLine(frustumCentroid - (light.Direction * distFromCentroid), frustumCentroid,
                                           new Color4(1, 1, 0));
                game.LineManager3D.AddCenteredBox(frustumCentroid, 1.0f, new Color4(1, 0, 0));
                game.LineManager3D.AddViewFrustum(new BoundingFrustum(mainCamera.ViewProjection), new Color4(1, 0, 1));
                game.LineManager3D.AddViewFrustum(frustumCornersWS, new Color4(1, 0, 0));
                Vector3[]   temps  = new Vector3[8];
                Matrix      tm     = Matrix.Invert(viewMatrix);
                BoundingBox bb     = new BoundingBox(mins, maxes);
                Vector3[]   temps2 = bb.GetCorners();
                Vector3.TransformCoordinate(temps2, ref tm, temps);
                game.LineManager3D.AddViewFrustum(temps, new Color4(0, 1, 0));
                game.LineManager3D.AddViewFrustum(new BoundingFrustum(lightCamera.ViewProjection), new Color4(1, 0, 1));
            }

            return(lightCamera);
        }
Exemple #2
0
        /// <summary>
        /// Renders a list of models to the shadow map, and returns a surface
        /// containing the shadow occlusion factor
        /// </summary>
        /// <param name="modelList">The list of models to render</param>
        /// <param name="depthTexture">Texture containing depth for the scene</param>
        /// <param name="light">The light for which the shadow is being calculated</param>
        /// <param name="mainCamera">The camera viewing the scene containing the light</param>
        /// <returns>The shadow occlusion texture</returns>
        public void UpdateShadowMap(RenderPrimitives renderDelegate, DirectionalLight light, ICamera mainCamera)
        {
            context.ClearDepthStencilView(shadowMapDsv, DepthStencilClearFlags.Depth, 1, 0);
            context.ClearState();
            context.OutputMerger.SetTargets(shadowMapDsv);



            // Get corners of the main camera's bounding frustum
            Matrix cameraTransform, viewMatrix;

            viewMatrix      = mainCamera.View;
            cameraTransform = Matrix.Invert(viewMatrix);



            BoundingFrustum frustum = new BoundingFrustum(mainCamera.ViewProjection);

            frustum.GetCorners(frustumCornersWS);


            Vector3.TransformCoordinate(frustumCornersWS, ref viewMatrix, frustumCornersVS);
            for (int i = 0; i < 4; i++)
            {
                farFrustumCornersVS[i] = frustumCornersVS[i + 4];
            }

            // Calculate the cascade splits.  We calculate these so that each successive
            // split is larger than the previous, giving the closest split the most amount
            // of shadow detail.
            float N = NumSplits;
            float near = mainCamera.NearClip, far = mainCamera.FarClip;

            splitDepths[0]         = near;
            splitDepths[NumSplits] = far;
            const float splitConstant = 0.95f;

            for (int i = 1; i < splitDepths.Length - 1; i++)
            {
                splitDepths[i] = splitConstant * near * (float)Math.Pow(far / near, i / N) + (1.0f - splitConstant) * ((near + (i / N)) * (far - near));
            }



            // Render our scene geometry to each split of the cascade
            for (int i = 0; i < NumSplits; i++)
            {
                float minZ = splitDepths[i];
                float maxZ = splitDepths[i + 1];

                lightCameras[i] = CalculateFrustum(light, mainCamera, minZ, maxZ);

                renderShadowMap(renderDelegate, i);
            }


            // We'll use these clip planes to determine which split a pixel belongs to
            for (int i = 0; i < NumSplits; i++)
            {
                lightClipPlanes[i].X = -splitDepths[i];
                lightClipPlanes[i].Y = -splitDepths[i + 1];

                lightViewProjectionMatrices[i] = lightCameras[i].ViewProjection;
                //lightProjectionMatrices[i] = lightCameras[i].Projection;
                //lightCameras[ i ].GetViewProjMatrix( out  );
            }
        }