diff --git a/Nez.Portable/Graphics/DeferredLighting/DeferredLIghtingRenderlyerExcludeRenderer.cs b/Nez.Portable/Graphics/DeferredLighting/DeferredLIghtingRenderlyerExcludeRenderer.cs
new file mode 100644
index 000000000..0da091316
--- /dev/null
+++ b/Nez.Portable/Graphics/DeferredLighting/DeferredLIghtingRenderlyerExcludeRenderer.cs
@@ -0,0 +1,339 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Nez.Textures;
+
+
+namespace Nez.DeferredLighting
+{
+ ///
+ /// handles deferred lighting. This Renderer should be ordered after any of your Renderers that render to a RenderTexture. Any renderLayers
+ /// rendered by this Renderer should have Renderables with DeferredSpriteMaterials (or null Material to use the default, diffuse only Material).
+ ///
+ public class DeferredLightingRenderLayerExcludeRenderer : Renderer
+ {
+ ///
+ /// we do not want to render into the Scene render texture
+ ///
+ /// true
+ /// false
+ public override bool WantsToRenderToSceneRenderTarget => false;
+
+ ///
+ /// the renderLayers excluded from this renderer
+ ///
+ public int[] ExcludedRenderLayers;
+
+ ///
+ /// ambient lighting color. Alpha is ignored
+ ///
+ /// The color of the ambient.
+ public Color AmbientColor
+ {
+ get => _ambientColor;
+ set => SetAmbientColor(value);
+ }
+
+ ///
+ /// clear color for the diffuse portion of the gbuffer
+ ///
+ /// The color of the clear.
+ public Color ClearColor
+ {
+ get => _clearColor;
+ set => SetClearColor(value);
+ }
+
+ ///
+ /// single pixel texture of a neutral normal map. This will effectively make the object have only diffuse lighting if applied as the normal map.
+ ///
+ /// The null normal map texture.
+ public Texture2D NullNormalMapTexture
+ {
+ get
+ {
+ if (_nullNormalMapTexture == null)
+ _nullNormalMapTexture = Graphics.CreateSingleColorTexture(1, 1, new Color(0.5f, 0.5f, 1f, 0f));
+ return _nullNormalMapTexture;
+ }
+ }
+
+ ///
+ /// if true, all stages of the deferred pipeline are rendered after the final combine
+ ///
+ public bool EnableDebugBufferRender;
+
+
+ int _lightLayer;
+ Color _ambientColor;
+ Color _clearColor;
+ Texture2D _nullNormalMapTexture;
+
+ public RenderTexture DiffuseRT;
+ public RenderTexture NormalRT;
+ public RenderTexture LightRT;
+
+ DeferredLightEffect _lightEffect;
+
+ // light volumes. quad for directional/area and polygon for others
+ QuadMesh _quadMesh;
+ PolygonMesh _polygonMesh;
+ PolygonMesh _quadPolygonMesh;
+
+
+ public DeferredLightingRenderLayerExcludeRenderer(int renderOrder, int lightLayer, params int[] excludedRenderLayers) : base(renderOrder)
+ {
+ // make sure we have a workable Material for our lighting system
+ Material = new DeferredSpriteMaterial(NullNormalMapTexture);
+
+ _lightLayer = lightLayer;
+ ExcludedRenderLayers = excludedRenderLayers;
+
+ _lightEffect = new DeferredLightEffect();
+
+ // meshes used for light volumes
+ _quadMesh = new QuadMesh(Core.GraphicsDevice);
+ _polygonMesh = PolygonMesh.CreateSymmetricalPolygon(10);
+ _quadPolygonMesh = PolygonMesh.CreateRectangle();
+
+ // set some sensible defaults
+ SetAmbientColor(new Color(0.2f, 0.2f, 0.2f))
+ .SetClearColor(Color.CornflowerBlue);
+ }
+
+ ///
+ /// we override render completely here so we can do our thing with multiple render targets
+ ///
+ /// scene.
+ public override void Render(Scene scene)
+ {
+ ClearRenderTargets();
+ RenderSprites(scene);
+ RenderLights(scene);
+ RenderFinalCombine(scene);
+
+ if (EnableDebugBufferRender)
+ RenderAllBuffers(scene);
+ }
+
+ protected override void DebugRender(Scene scene, Camera cam)
+ {
+ for (var i = 0; i < scene.RenderableComponents.Count; i++)
+ {
+ var renderable = scene.RenderableComponents[i];
+ if (!ExcludedRenderLayers.Contains(renderable.RenderLayer) && renderable.Enabled &&
+ renderable.IsVisibleFromCamera(cam))
+ renderable.DebugRender(Graphics.Instance.Batcher);
+ }
+
+ var lightRenderables = scene.RenderableComponents.ComponentsWithRenderLayer(_lightLayer);
+ for (var j = 0; j < lightRenderables.Length; j++)
+ {
+ var renderable = lightRenderables.Buffer[j];
+ if (renderable.Enabled && renderable.IsVisibleFromCamera(cam))
+ renderable.DebugRender(Graphics.Instance.Batcher);
+ }
+ }
+
+
+ #region Configuration
+
+ ///
+ /// ambient lighting color. Alpha is ignored
+ ///
+ /// The ambient color.
+ /// Color.
+ public DeferredLightingRenderLayerExcludeRenderer SetAmbientColor(Color color)
+ {
+ if (_ambientColor != color)
+ {
+ _ambientColor = color;
+ _lightEffect.SetAmbientColor(color);
+ }
+
+ return this;
+ }
+
+ ///
+ /// clear color for the diffuse portion of the gbuffer
+ ///
+ /// The clear color.
+ /// Color.
+ public DeferredLightingRenderLayerExcludeRenderer SetClearColor(Color color)
+ {
+ if (_clearColor != color)
+ {
+ _clearColor = color;
+ _lightEffect.SetClearColor(color);
+ }
+
+ return this;
+ }
+
+ #endregion
+
+
+ #region Rendering
+
+ void ClearRenderTargets()
+ {
+ Core.GraphicsDevice.SetRenderTargets(DiffuseRT.RenderTarget, NormalRT.RenderTarget);
+ _lightEffect.PrepareClearGBuffer();
+ _quadMesh.Render();
+ }
+
+ void RenderSprites(Scene scene)
+ {
+ BeginRender(scene.Camera);
+
+ for (var i = 0; i < scene.RenderableComponents.Count; i++)
+ {
+ var renderable = scene.RenderableComponents[i];
+ if (!ExcludedRenderLayers.Contains(renderable.RenderLayer) && renderable.Enabled &&
+ renderable.IsVisibleFromCamera(scene.Camera))
+ RenderAfterStateCheck(renderable, scene.Camera);
+ }
+
+ if (ShouldDebugRender && Core.DebugRenderEnabled)
+ DebugRender(scene, scene.Camera);
+
+ EndRender();
+ }
+
+ void RenderLights(Scene scene)
+ {
+ // bind the normalMap and update the Effect with our camera
+ _lightEffect.SetNormalMap(NormalRT);
+ _lightEffect.UpdateForCamera(scene.Camera);
+
+ Core.GraphicsDevice.SetRenderTarget(LightRT);
+ Core.GraphicsDevice.Clear(Color.Transparent);
+ Core.GraphicsDevice.BlendState = BlendState.Additive;
+ Core.GraphicsDevice.DepthStencilState = DepthStencilState.None;
+ Core.GraphicsDevice.RasterizerState = RasterizerState.CullNone;
+
+ var renderables = scene.RenderableComponents.ComponentsWithRenderLayer(_lightLayer);
+ for (var i = 0; i < renderables.Length; i++)
+ {
+ Insist.IsTrue(renderables.Buffer[i] is DeferredLight, "Found a Renderable in the lightLayer that is not a DeferredLight!");
+ var renderable = renderables.Buffer[i];
+ if (renderable.Enabled)
+ {
+ var light = renderable as DeferredLight;
+ if (light is DirLight || light.IsVisibleFromCamera(scene.Camera))
+ RenderLight(light);
+ }
+ }
+ }
+
+ void RenderFinalCombine(Scene scene)
+ {
+ Core.GraphicsDevice.SetRenderTarget(scene.SceneRenderTarget);
+ Core.GraphicsDevice.BlendState = BlendState.Opaque;
+ Core.GraphicsDevice.DepthStencilState = DepthStencilState.None;
+
+ // combine everything. ambient color is set in the shader when the property is set so no need to reset it
+ _lightEffect.PrepareForFinalCombine(DiffuseRT, LightRT, NormalRT);
+ _quadMesh.Render();
+ }
+
+ void RenderAllBuffers(Scene scene)
+ {
+ var tempRT = RenderTarget.GetTemporary(scene.SceneRenderTarget.Width, scene.SceneRenderTarget.Height);
+
+ Core.GraphicsDevice.SetRenderTarget(tempRT);
+
+ var halfWidth = tempRT.Width / 2;
+ var halfHeight = tempRT.Height / 2;
+
+ Graphics.Instance.Batcher.Begin(BlendState.Opaque);
+ Graphics.Instance.Batcher.Draw(LightRT, new Rectangle(0, 0, halfWidth, halfHeight));
+ Graphics.Instance.Batcher.Draw(DiffuseRT, new Rectangle(halfWidth, 0, halfWidth, halfHeight));
+ Graphics.Instance.Batcher.Draw(NormalRT, new Rectangle(0, halfHeight, halfWidth, halfHeight));
+ Graphics.Instance.Batcher.Draw(scene.SceneRenderTarget, new Rectangle(halfWidth, halfHeight, halfWidth, halfHeight));
+ Graphics.Instance.Batcher.End();
+
+ Core.GraphicsDevice.SetRenderTarget(scene.SceneRenderTarget);
+ Graphics.Instance.Batcher.Begin(BlendState.Opaque);
+ Graphics.Instance.Batcher.Draw(tempRT, Vector2.Zero);
+ Graphics.Instance.Batcher.End();
+
+ RenderTarget.ReleaseTemporary(tempRT);
+ }
+
+ #endregion
+
+
+ #region Light rendering
+
+ void RenderLight(DeferredLight light)
+ {
+ // check SpotLight first because it is a subclass of PointLight!
+ if (light is SpotLight)
+ RenderLight(light as SpotLight);
+ else if (light is PointLight)
+ RenderLight(light as PointLight);
+ else if (light is AreaLight)
+ RenderLight(light as AreaLight);
+ else if (light is DirLight)
+ RenderLight(light as DirLight);
+ }
+
+ void RenderLight(DirLight light)
+ {
+ _lightEffect.UpdateForLight(light);
+ _quadMesh.Render();
+ }
+
+ void RenderLight(PointLight light)
+ {
+ _lightEffect.UpdateForLight(light);
+ _polygonMesh.Render();
+ }
+
+ void RenderLight(SpotLight light)
+ {
+ _lightEffect.UpdateForLight(light);
+ _polygonMesh.Render();
+ }
+
+ void RenderLight(AreaLight light)
+ {
+ _lightEffect.UpdateForLight(light);
+ _quadPolygonMesh.Render();
+ }
+
+ #endregion
+
+
+ public override void OnSceneBackBufferSizeChanged(int newWidth, int newHeight)
+ {
+ // create our RenderTextures if we havent and resize them if we have
+ if (DiffuseRT == null)
+ {
+ DiffuseRT = new RenderTexture(newWidth, newHeight, SurfaceFormat.Color, DepthFormat.None);
+ NormalRT = new RenderTexture(newWidth, newHeight, SurfaceFormat.Color, DepthFormat.None);
+ LightRT = new RenderTexture(newWidth, newHeight, SurfaceFormat.Color, DepthFormat.None);
+ }
+ else
+ {
+ DiffuseRT.OnSceneBackBufferSizeChanged(newWidth, newHeight);
+ NormalRT.OnSceneBackBufferSizeChanged(newWidth, newHeight);
+ LightRT.OnSceneBackBufferSizeChanged(newWidth, newHeight);
+ }
+ }
+
+ public override void Unload()
+ {
+ _lightEffect.Dispose();
+
+ DiffuseRT.Dispose();
+ NormalRT.Dispose();
+ LightRT.Dispose();
+
+ if (_nullNormalMapTexture != null)
+ _nullNormalMapTexture.Dispose();
+
+ base.Unload();
+ }
+ }
+}