예제 #1
0
        /// <summary>
        /// Generates the shadow map to be used when rendering this light.
        /// </summary>
        /// <remarks>If this instance is not to cast shadows the shadow map is cleared.</remarks>
        /// <param name="renderSystem">The render system to render with.</param>
        /// <param name="shadowMap">The shadow map to render on</param>
        protected virtual void GenerateShadowMap(DeferredRenderSystem renderSystem, RenderTarget2D shadowMap)
        {
            RenderTarget2D intermediateShadowMap = renderSystem.RenderTargets.GetTemporaryRenderTarget(SurfaceFormat.Color); // Intermediate map used for selective sampling
            RectangleF     lightDrawBounds       = this.GetWorldDrawBounds();

            renderSystem.GraphicsDevice.SetRenderTarget(shadowMap);
            renderSystem.ClearCurrentRenderTarget(Color.White); // Clear the core shadow map

            // Only render the shadows if we are to cast them
            if (this.CastsShadows)
            {
                // Render the shadow caused by each object within the lights range
                List <SpriteBase> objectList = this.World.GetSpriteObjectsInArea(lightDrawBounds).Where(currObj => (currObj.RenderOptions & SpriteRenderOptions.CastsShadows) != 0).OrderBy(s => s.LayerDepth).ToList();

                if (objectList.Count > 0)
                {
                    int  lastLayerDepth      = 0;
                    int  lastLayerDepthIndex = 0;
                    bool renderedShadow      = false; // This is used so that we don't bother occluding objects if we didn't even render anything

                    lastLayerDepth = (int)objectList[0].LayerDepth;

                    for (int objectIndex = 0; objectIndex < objectList.Count; objectIndex++)
                    {
                        SpriteBase currObj           = objectList[objectIndex];
                        RectangleF spriteWorldBounds = currObj.SpriteWorldBounds;

                        // If the object we found doesn't cast shadows (or we're inside it), skip it
                        if (spriteWorldBounds.Contains(base.Position))
                        {
                            continue;
                        }

                        this.shadowMapShader.ConfigureShader(renderSystem); // Configure the shader
                        this.shadowMapShader.GetParameter("viewProjection").SetValue(renderSystem.GameCamera.GetViewProjectionMatrix(renderSystem));
                        this.shadowMapShader.GetParameter("cameraPosition").SetValue(renderSystem.GameCamera.PixelPosition);

                        // If we switched light planes remove occlusion from all previous objects
                        if (lastLayerDepth != (int)currObj.LayerDepth)
                        {
                            // Only occlude if we actually rendered something
                            if (renderedShadow)
                            {
                                // Loop through all the objects between the last layer index and the current index
                                for (int clearObjIndex = lastLayerDepthIndex; clearObjIndex < objectIndex; clearObjIndex++)
                                {
                                    this.ProcessSpriteDepthTransition(renderSystem, objectList[clearObjIndex]);
                                }
                            }

                            // Save the last values
                            lastLayerDepth      = (int)currObj.LayerDepth;
                            lastLayerDepthIndex = objectIndex;
                            renderedShadow      = false; // Reset this
                        }

                        // Create the shadow map caused by the current sprite
                        this.shadowMapShader.ApplyPass(0);

                        // Construct the vertex primitive to mask with
                        Vector2[] extrema;
                        if (currObj.SpriteWorldShape != null)
                        {
                            extrema = currObj.SpriteWorldShape.GetRelativeExtrema(base.Position); // Get the extrema that cause the shadow
                        }
                        else
                        {
                            extrema = spriteWorldBounds.GetRelativeExtrema(base.Position); // Get the extrema that cause the shadow
                        }

                        Vector2 widthVector  = Vector2.Normalize(extrema[0] - base.Position); // Get the vector to the first extrema
                        Vector2 heightVector = Vector2.Normalize(extrema[1] - base.Position); // Get the vector to the second extrema

                        VertexPrimitive shadowArea = new VertexPrimitive(PrimitiveType.TriangleStrip, 4);
                        shadowArea.Add(extrema[0]);
                        shadowArea.Add(extrema[1]);

                        // Let's extend the shadow vector until it hits the edge of the draw bounds
                        shadowArea.Add(lightDrawBounds.CastInternalRay(extrema[0], widthVector));
                        shadowArea.Add(lightDrawBounds.CastInternalRay(extrema[1], heightVector));

                        // Let's get the remaining verts that might exist to finish up the rect
                        List <Vector2> interiorVerts = lightDrawBounds.GetInteriorVertices(base.Position, widthVector, heightVector);
                        shadowArea.Add(interiorVerts);              // Add the interior verts (if any exist)

                        renderSystem.DirectScreenPaint(shadowArea); // Render the shadow
                        renderedShadow = true;                      // We just rendered a shadow; keep track of that
                    }

                    // Loop through all the objects between the last layer index and the current index
                    if (renderedShadow)
                    {
                        // Remove the shadows over the LAST shadow plane
                        for (int clearObjIndex = lastLayerDepthIndex; clearObjIndex < objectList.Count; clearObjIndex++)
                        {
                            this.ProcessSpriteDepthTransition(renderSystem, objectList[clearObjIndex]);
                        }

                        // Blur the shadow map if enabled
                        if (!float.IsPositiveInfinity(this.minShadowBlurDistance))
                        {
                            renderSystem.GraphicsDevice.SetRenderTarget(intermediateShadowMap); // We need to render to a temp target

                            // Configure the shader
                            this.shadowMapShader.ConfigureShader(renderSystem, 1);

                            // Set parameters
                            this.shadowMapShader.GetParameter("viewProjection").SetValue(renderSystem.GameCamera.GetViewProjectionMatrix(renderSystem));
                            this.shadowMapShader.GetParameter("cameraPosition").SetValue(renderSystem.GameCamera.PixelPosition);
                            this.shadowMapShader.GetParameter("lightPosition").SetValue(base.PixelPosition);
                            this.shadowMapShader.GetParameter("maxBlurDistance").SetValue(this.World.GetPixelFromWorld(this.maxShadowBlurDistance));
                            this.shadowMapShader.GetParameter("minBlurDistance").SetValue(this.World.GetPixelFromWorld(this.maxShadowBlurDistance));
                            this.shadowMapShader.GetParameter("shadowMap").SetValue(shadowMap);

                            this.shadowMapShader.ApplyPass(0);
                            renderSystem.DirectScreenPaint(lightDrawBounds);        // Render the shadow

                            renderSystem.GraphicsDevice.SetRenderTarget(shadowMap); // Set the shadow map for final render

                            // Update shadow map
                            this.shadowMapShader.GetParameter("shadowMap").SetValue(intermediateShadowMap);

                            this.shadowMapShader.ApplyPass(0);
                            renderSystem.DirectScreenPaint(lightDrawBounds); // Render
                        }
                    }
                }
            }

            renderSystem.SetRenderTargets(RenderTargetTypes.None, 0); // Resolve the render target
            renderSystem.RenderTargets.ReleaseTemporaryRenderTarget(intermediateShadowMap);
        }