Ejemplo n.º 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="LightManager"/> class.
        /// </summary>
        /// <param name="game">The game.</param>
        public LightManager(Game game)
        {
            // Load Effects, Models and Quad Renderer
            directionalLightEffect = game.Content.Load <Effect>("Shaders/Lights/DirectionalLight");
            pointLightEffect       = game.Content.Load <Effect>("Shaders/Lights/PointLight");
            hemisphericLightEffect = game.Content.Load <Effect>("Shaders/Lights/HemisphericLight");
            sphereModel            = game.Content.Load <Model>("Models/Sphere");
            fullscreenQuad         = new QuadRenderer(game);
            game.Components.Add(fullscreenQuad);
            halfPixel = new Vector2()
            {
                X = 0.5f / (float)game.GraphicsDevice.PresentationParameters.BackBufferWidth,
                Y = 0.5f / (float)game.GraphicsDevice.PresentationParameters.BackBufferHeight
            };

            // Load the Color Map for the Hemispheric Light
            hemisphericColorMap = game.Content.Load <Texture2D>("Textures/ColorMap");

            // Make our directional light source
            light           = new Lights.DirectionalLight();
            light.Direction = new Vector3(-1, -1, -1);
            light.Color     = new Vector3(0.7f, 0.7f, 0.7f);

            // Instantiate the PointLights List
            pointLights = new List <Lights.PointLight>();
        }
Ejemplo n.º 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 RenderTarget2D Draw(SceneManager scene, RenderTarget2D depthTexture, Lights.DirectionalLight light, Camera mainCamera)
        {
            if (enabled)
            {
                // Set our targets
                graphicsDevice.SetRenderTarget(shadowMap);
                graphicsDevice.Clear(ClearOptions.Target, Color.White, 1.0f, 0);
                graphicsDevice.Clear(ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

                // Get corners of the main camera's bounding frustum
                Matrix cameraTransform, viewMatrix;
                mainCamera.GetWorldMatrix(out cameraTransform);
                mainCamera.GetViewMatrix(out viewMatrix);
                mainCamera.BoundingFrustum.GetCorners(frustumCornersWS);
                Vector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersVS);
                for (int i = 0; i < 4; i++)
                {
                    farFrustumCornersVS[i] = frustumCornersVS[i + 4];
                }

                CalculateFrustum(light, mainCamera);

                DrawShadowMap(scene);

                DrawShadowOcclusion(mainCamera, depthTexture);

                return(shadowOcclusion);
            }
            else
            {
                // If we're disabled, just clear our 1x1 texture to white and return it
                graphicsDevice.SetRenderTarget(disabledShadowOcclusion);
                graphicsDevice.Clear(ClearOptions.Target, Color.White, 1.0f, 0);

                return(disabledShadowOcclusion);
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ShadowRenderer"/> class.
        /// </summary>
        /// <param name="device">The device.</param>
        /// <param name="contentManager">The content.</param>
        public CascadeShadowRenderer(GraphicsDevice device, ContentManager contentManager)
        {
            // Load the Shadow Effect
            shadowEffect = contentManager.Load <Effect>("Shaders/Shadows/CascadeShadowMap");

            // Instantiate the SpriteBatch
            spriteBatch = new SpriteBatch(device);

            // Create the ShadowMap RenderTarget
            shadowMap = new RenderTarget2D(device, SHADOWMAPSIZE * NUMBER_OF_SPLITS, SHADOWMAPSIZE,
                                           false, SurfaceFormat.Single, DepthFormat.Depth24Stencil8, 0, RenderTargetUsage.DiscardContents);

            // Create the Shadow Occlusion RenderTarget
            shadowOcclusion = new RenderTarget2D(device, device.PresentationParameters.BackBufferWidth,
                                                 device.PresentationParameters.BackBufferHeight,
                                                 false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);

            // Create the Disabled Shadow Occlusion RenderTarget
            disabledShadowOcclusion = new RenderTarget2D(device, 1, 1, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8);

            // Create the Fullscreen Quad
            fullscreenQuad = new FullScreenQuad(device);

            // Get the Techniques
            // 0 - GenerateShadowMap
            // 1 - CreateShadowTerm2x2PCF
            // 2 - CreateShadowTerm3x3PCF
            // 3 - CreateShadowTerm5x5PCF
            // 4 - CreateShadowTerm7x7PCF
            shadowOcclusionTechniques = this.shadowEffect.Techniques;

            // Create a default Directional light
            light           = new Lights.DirectionalLight();
            light.Direction = new Vector3(-1, -1, -1);
            light.Color     = new Vector3(0.7f, 0.7f, 0.7f);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Renders the specified device.
        /// </summary>
        /// <param name="device">The device.</param>
        /// <param name="camera">The camera.</param>
        /// <param name="light">The light.</param>
        RenderTarget2D Draw(GraphicsDevice device, RenderTarget2D depthRT, SceneManager scene, Camera camera, Lights.DirectionalLight light)
        {
            if (isEnabled)
            {
                device.SetRenderTarget(shadowMap);
                device.Clear(ClearOptions.Target, Color.White, 1.0f, 0);
                device.Clear(ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

                // Get corners of the main camera's BoundingFrustum
                Matrix cameraTransform, viewMatrix;
                camera.GetWorldMatrix(out cameraTransform);
                camera.GetViewMatrix(out viewMatrix);
                camera.BoundingFrustum.GetCorners(frustumCornersWS);

                Vector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersVS);

                for (int i = 0; i < 4; i++)
                {
                    farFrustumCornerVS[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 = NUMBER_OF_SPLITS;
                float near = camera.NearClip, far = camera.FarClip;
                splitDepths[0] = near;
                splitDepths[NUMBER_OF_SPLITS] = 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 < NUMBER_OF_SPLITS; i++)
                {
                    float minZ = splitDepths[i];
                    float maxZ = splitDepths[i + 1];
                    lightCameras[i] = CalculateFrustum(light, camera, minZ, maxZ);
                    DrawShadowMap(device, scene, i);
                }

                // Render the shadow occlusion
                DrawShadowOcclusion(device, camera, depthRT);
                return(shadowOcclusion);
            }
            else
            {
                // Disabled.  Clear our 1x1 texture to white.
                device.SetRenderTarget(disabledShadowOcclusion);
                device.Clear(ClearOptions.Target, Color.White, 1.0f, 0);
                return(disabledShadowOcclusion);
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Calculates the frustum.
        /// </summary>
        /// <param name="light">The light.</param>
        /// <param name="camera">The camera.</param>
        /// <param name="minZ">The min Z.</param>
        /// <param name="maxZ">The max Z.</param>
        /// <returns></returns>
        protected OrthographicCamera CalculateFrustum(Lights.DirectionalLight light, Camera camera, float minZ, float maxZ)
        {
            // Shorten the view frustum according to the shadow view distance
            Matrix cameraMatrix;

            camera.GetWorldMatrix(out cameraMatrix);

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

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

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

            // Position the shadow-caster camera so that it's looking at the centroid,
            // and backed up in the direction of the sunlight
            Matrix viewMatrix = Matrix.CreateLookAt(Vector3.Zero - light.Direction * 100, Vector3.Zero, new Vector3(0, 1, 0));

            // Determine the position of the frustum corners in light space
            Vector3.Transform(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;
                }
            }


            // We snap the camera to 1 pixel increments so that moving the camera does not cause the shadows to jitter.
            // This is a matter of integer dividing by the world space size of a texel
            float diagonalLength = (frustumCornersWS[0] - frustumCornersWS[6]).Length();

            diagonalLength += 2;    //Without this, the shadow map isn't big enough in the world.
            float   worldsUnitsPerTexel = diagonalLength / (float)SHADOWMAPSIZE;
            Vector3 vBorderOffset       = (new Vector3(diagonalLength, diagonalLength, diagonalLength) - (maxes - mins)) * 0.5f;

            maxes += vBorderOffset;
            mins  -= vBorderOffset;

            mins  /= worldsUnitsPerTexel;
            mins.X = (float)Math.Floor(mins.X);
            mins.Y = (float)Math.Floor(mins.Y);
            mins.Z = (float)Math.Floor(mins.Z);
            mins  *= worldsUnitsPerTexel;

            maxes  /= worldsUnitsPerTexel;
            maxes.X = (float)Math.Floor(maxes.X);
            maxes.Y = (float)Math.Floor(maxes.Y);
            maxes.Z = (float)Math.Floor(maxes.Z);
            maxes  *= worldsUnitsPerTexel;

            // 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.SetViewMatrix(ref viewMatrix);

            return(lightCamera);
        }
Ejemplo n.º 6
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>
        protected void CalculateFrustum(Lights.DirectionalLight light, Camera mainCamera)
        {
            // Shorten the view frustum according to the shadow view distance
            Matrix cameraMatrix;

            mainCamera.GetWorldMatrix(out cameraMatrix);

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

            for (int i = 0; i < 8; i++)
            {
                frustumCentroid += frustumCornersWS[i];
            }

            frustumCentroid /= 8;

            // 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((mainCamera.FarClip - mainCamera.NearClip), Vector3.Distance(frustumCornersVS[4], frustumCornersVS[5])) + 50.0f;
            Matrix viewMatrix       = Matrix.CreateLookAt(frustumCentroid - (light.Direction * distFromCentroid), frustumCentroid, new Vector3(0, 1, 0));

            // Determine the position of the frustum corners in light space
            Vector3.Transform(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;

            lightCamera = new OrthographicCamera(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - nearClipOffset, -mins.Z);
            lightCamera.SetViewMatrix(ref viewMatrix);
        }