/// <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); }