Browse Source

Added full deferred rendering in addition to light pre-pass.
Added missing vertex colored light pre-pass material shader permutation.

Lasse Öörni 14 years ago
parent
commit
d34a73bfb9
51 changed files with 720 additions and 156 deletions
  1. 2 1
      Bin/CoreData/Techniques/Diff.xml
  2. 2 1
      Bin/CoreData/Techniques/DiffAlphaMask.xml
  3. 2 1
      Bin/CoreData/Techniques/DiffNormal.xml
  4. 2 1
      Bin/CoreData/Techniques/DiffNormalAlphaMask.xml
  5. 2 1
      Bin/CoreData/Techniques/NoTexture.xml
  6. 2 2
      Bin/Data/Scripts/Editor.as
  7. 2 2
      Bin/Data/Scripts/Editor/EditorSettings.as
  8. 1 1
      Bin/Data/Scripts/LightTest.as
  9. 1 1
      Bin/Data/Scripts/TestScene.as
  10. 2 2
      Bin/Data/Scripts/TestSceneOld.as
  11. 4 0
      Bin/Data/UI/EditorSettingsDialog.xml
  12. 1 0
      Docs/GettingStarted.dox
  13. 31 16
      Docs/Reference.dox
  14. 2 1
      Docs/ScriptAPI.dox
  15. 8 5
      Engine/Engine/DebugHud.cpp
  16. 5 3
      Engine/Engine/Engine.cpp
  17. 10 3
      Engine/Engine/GraphicsAPI.cpp
  18. 1 1
      Engine/Graphics/Batch.h
  19. 11 2
      Engine/Graphics/Direct3D9/D3D9Graphics.cpp
  20. 5 1
      Engine/Graphics/Direct3D9/D3D9Graphics.h
  21. 1 1
      Engine/Graphics/Drawable.cpp
  22. 13 3
      Engine/Graphics/GraphicsDefs.h
  23. 15 5
      Engine/Graphics/OpenGL/OGLGraphics.cpp
  24. 5 1
      Engine/Graphics/OpenGL/OGLGraphics.h
  25. 25 20
      Engine/Graphics/Renderer.cpp
  26. 7 7
      Engine/Graphics/Renderer.h
  27. 2 1
      Engine/Graphics/Technique.cpp
  28. 113 59
      Engine/Graphics/View.cpp
  29. 7 7
      Engine/Graphics/View.h
  30. 4 2
      SourceAssets/GLSLShaders/CMakeLists.txt
  31. 51 0
      SourceAssets/GLSLShaders/Deferred.frag
  32. 39 0
      SourceAssets/GLSLShaders/Deferred.vert
  33. 23 0
      SourceAssets/GLSLShaders/Deferred.xml
  34. 93 0
      SourceAssets/GLSLShaders/DeferredLight.frag
  35. 33 0
      SourceAssets/GLSLShaders/DeferredLight.vert
  36. 17 0
      SourceAssets/GLSLShaders/DeferredLight.xml
  37. 5 0
      SourceAssets/GLSLShaders/Fog.frag
  38. 4 0
      SourceAssets/GLSLShaders/Material.frag
  39. 2 0
      SourceAssets/GLSLShaders/Material.xml
  40. 0 0
      SourceAssets/GLSLShaders/Prepass.frag
  41. 0 0
      SourceAssets/GLSLShaders/Prepass.vert
  42. 2 2
      SourceAssets/GLSLShaders/Prepass.xml
  43. 92 0
      SourceAssets/GLSLShaders/PrepassLight.frag
  44. 33 0
      SourceAssets/GLSLShaders/PrepassLight.vert
  45. 17 0
      SourceAssets/GLSLShaders/PrepassLight.xml
  46. 1 0
      SourceAssets/GLSLShaders/Samplers.frag
  47. 4 2
      SourceAssets/HLSLShaders/CMakeLists.txt
  48. 5 0
      SourceAssets/HLSLShaders/Fog.hlsl
  49. 7 0
      SourceAssets/HLSLShaders/Material.hlsl
  50. 2 0
      SourceAssets/HLSLShaders/Material.xml
  51. 2 1
      SourceAssets/HLSLShaders/Samplers.hlsl

+ 2 - 1
Bin/CoreData/Techniques/Diff.xml

@@ -3,6 +3,7 @@
     <pass name="litbase" vs="ForwardLit" ps="ForwardLit_DiffAmbient" />
     <pass name="light" vs="ForwardLit" ps="ForwardLit_Diff" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
-    <pass name="gbuffer" vs="GBuffer" ps="GBuffer" />
+    <pass name="prepass" vs="Prepass" ps="Prepass" />
     <pass name="material" vs="Material" ps="Material_Diff" depthtest="equal" depthwrite="false" />
+    <pass name="deferred" vs="Deferred" ps="Deferred_Diff" />
 </technique>

+ 2 - 1
Bin/CoreData/Techniques/DiffAlphaMask.xml

@@ -3,6 +3,7 @@
     <pass name="litbase" vs="ForwardLit" ps="ForwardLit_DiffAmbient"  alphatest="true" />
     <pass name="light" vs="ForwardLit" ps="ForwardLit_Diff"  alphatest="true" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow_Mask" ps="Shadow_Mask" alphatest="true" />
-    <pass name="gbuffer" vs="GBuffer" ps="GBuffer_Mask" />
+    <pass name="prepass" vs="Prepass" ps="Prepass_Mask" />
     <pass name="material" vs="Material" ps="Material_DiffMask" depthtest="equal" depthwrite="false" />
+    <pass name="deferred" vs="Deferred" ps="Deferred_DiffMask" />
 </technique>

+ 2 - 1
Bin/CoreData/Techniques/DiffNormal.xml

@@ -3,6 +3,7 @@
     <pass name="litbase" vs="ForwardLit_Normal" ps="ForwardLit_DiffNormalAmbient" />
     <pass name="light" vs="ForwardLit_Normal" ps="ForwardLit_DiffNormal" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
-    <pass name="gbuffer" vs="GBuffer_Normal" ps="GBuffer_Normal" />
+    <pass name="prepass" vs="Prepass_Normal" ps="Prepass_Normal" />
     <pass name="material" vs="Material" ps="Material_Diff" depthtest="equal" depthwrite="false" />
+    <pass name="deferred" vs="Deferred_Normal" ps="Deferred_DiffNormal" />
 </technique>

+ 2 - 1
Bin/CoreData/Techniques/DiffNormalAlphaMask.xml

@@ -3,6 +3,7 @@
     <pass name="litbase" vs="ForwardLit_Normal" ps="ForwardLit_DiffNormalAmbient" alphatest="true" />
     <pass name="light" vs="ForwardLit_Normal" ps="ForwardLit_DiffNormal" alphatest="true" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow_Mask" ps="Shadow_Mask" alphatest="true" />
-    <pass name="gbuffer" vs="GBuffer_Normal" ps="GBuffer_NormalMask" />
+    <pass name="prepass" vs="Prepass_Normal" ps="Prepass_NormalMask" />
     <pass name="material" vs="Material" ps="Material_DiffMask" depthtest="equal" depthwrite="false" />
+    <pass name="deferred" vs="Deferred_Normal" ps="Deferred_DiffNormalMask" />
 </technique>

+ 2 - 1
Bin/CoreData/Techniques/NoTexture.xml

@@ -3,6 +3,7 @@
     <pass name="litbase" vs="ForwardLit" ps="ForwardLit_Ambient" />
     <pass name="light" vs="ForwardLit" ps="ForwardLit" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
-    <pass name="gbuffer" vs="GBuffer" ps="GBuffer" />
+    <pass name="prepass" vs="Prepass" ps="Prepass" />
     <pass name="material" vs="Material" ps="Material" depthtest="equal" depthwrite="false" />
+    <pass name="deferred" vs="Deferred" ps="Deferred" />
 </technique>

+ 2 - 2
Bin/Data/Scripts/Editor.as

@@ -98,7 +98,7 @@ void LoadConfig()
 
     if (!renderingElem.isNull)
     {
-        renderer.lightPrepass = renderingElem.GetInt("rendermode") == 1;
+        renderer.renderMode = RenderMode(renderingElem.GetInt("rendermode"));
         renderer.textureQuality = renderingElem.GetInt("texturequality");
         renderer.materialQuality = renderingElem.GetInt("materialquality");
         SetShadowResolution(renderingElem.GetInt("shadowresolution"));
@@ -136,7 +136,7 @@ void SaveConfig()
     objectElem.SetBool("uselocalids", useLocalIDs);
     objectElem.SetInt("pickmode", pickMode);
 
-    renderingElem.SetInt("rendermode", renderer.lightPrepass ? 1 : 0);
+    renderingElem.SetInt("rendermode", renderer.renderMode);
     renderingElem.SetInt("texturequality", renderer.textureQuality);
     renderingElem.SetInt("materialquality", renderer.materialQuality);
     renderingElem.SetInt("shadowresolution", GetShadowResolution());

+ 2 - 2
Bin/Data/Scripts/Editor/EditorSettings.as

@@ -56,7 +56,7 @@ void UpdateEditorSettingsDialog()
     pickModeEdit.selection = pickMode;
 
     DropDownList@ renderModeEdit = settingsDialog.GetChild("RenderModeEdit", true);
-    renderModeEdit.selection = renderer.lightPrepass ? 1 : 0;
+    renderModeEdit.selection = renderer.renderMode;
 
     DropDownList@ textureQualityEdit = settingsDialog.GetChild("TextureQualityEdit", true);
     textureQualityEdit.selection = renderer.textureQuality;
@@ -229,7 +229,7 @@ void EditPickMode(StringHash eventType, VariantMap& eventData)
 void EditRenderMode(StringHash eventType, VariantMap& eventData)
 {
     DropDownList@ edit = eventData["Element"].GetUIElement();
-    renderer.lightPrepass = edit.selection == 1;
+    renderer.renderMode = RenderMode(edit.selection);
 }
 
 void EditTextureQuality(StringHash eventType, VariantMap& eventData)

+ 1 - 1
Bin/Data/Scripts/LightTest.as

@@ -190,7 +190,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
         if (input.keyPress['1'])
-            renderer.lightPrepass = !renderer.lightPrepass;
+            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
 
         if (input.keyPress['2'])
         {

+ 1 - 1
Bin/Data/Scripts/TestScene.as

@@ -270,7 +270,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
         if (input.keyPress['1'])
-            renderer.lightPrepass = !renderer.lightPrepass;
+            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
         
         if (input.keyPress['2'])
         {

+ 2 - 2
Bin/Data/Scripts/TestSceneOld.as

@@ -355,8 +355,8 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
         if (input.keyPress['1'])
-            renderer.lightPrepass = !renderer.lightPrepass;
-        
+            renderer.renderMode = RenderMode((renderer.renderMode + 1) % 3);
+
         if (input.keyPress['2'])
         {
             int quality = renderer.textureQuality;

+ 4 - 0
Bin/Data/UI/EditorSettingsDialog.xml

@@ -168,10 +168,14 @@
         <element type="Text" style="FileSelectorFilterText" name="RenderPrepass">
             <text value="Pre-pass" />
         </element>
+        <element type="Text" style="FileSelectorFilterText" name="RenderDeferred">
+            <text value="Deferred" />
+        </element>
         <element type="DropDownList" name="RenderModeEdit">
             <fixedwidth value="100" />
             <popupitem name="RenderForward" />
             <popupitem name="RenderPrepass" />
+            <popupitem name="RenderDeferred" />
         </element>
     </element>
     <element>

+ 1 - 0
Docs/GettingStarted.dox

@@ -134,6 +134,7 @@ Urho3D.exe understands the following command line options:
 -headless   Headless mode. No application window will be created
 -logdebug   Display debug level log messages also in release mode
 -prepass    Use light pre-pass rendering
+-deferred   Use deferred rendering
 -lqshadows  Use low-quality (1-sample) shadow filtering
 -noshadows  Disable shadow rendering
 -nolimit    Disable frame limiter

+ 31 - 16
Docs/Reference.dox

@@ -375,7 +375,7 @@ When setting the initial screen mode, Graphics does a few checks:
 - For Direct3D9, the supported shader model is checked. 2.0 is minimum, but 3.0 will be used if available. %Shader model 2.0 can be forced by calling \ref Graphics::SetForceSM2 "SetForceSM2()".
 - For OpenGL, version 2.0 with EXT_framebuffer_object, EXT_packed_depth_stencil, EXT_texture_compression_s3tc and EXT_texture_filter_anisotropic extensions is checked for.
 - Are hardware shadow maps supported? Both ATI & NVIDIA style shadow maps can be used. If neither are available, no shadows will be rendered.
-- Is light pre-pass rendering supported? This requires either readable hardware depth-stencil texture, or multiple rendertarget and R32F texture format support.
+- Are light pre-pass and deferred rendering modes supported? These require sufficient multiple rendertarget support, and either R32F texture format or readable hardware depth.
 
 \section Rendering_Renderer Renderer
 
@@ -387,7 +387,7 @@ By default there is one viewport, but the amount can be increased with the funct
 
 Viewports can have a chain of post-processing effects. See \ref Postprocessing "Post-processing" for more details.
 
-Either forward or light pre-pass rendering can be chosen, see \ref Renderer::SetLightPrepass "SetLightPrepass()". %Light pre-pass will be advantageous once there is a large number of per-pixel lights affecting each object, but its disadvantages are the lack of hardware multisampling and inability to choose the lighting model per material. In place of multisample antialiasing, a FXAA post-processing edge filter can be used, see the TestScene script application for an example of how to use.
+Either forward, light pre-pass or deferred rendering can be chosen, see \ref Renderer::SetRenderMode "SetRenderMode()". Deferred rendering modes will be advantageous once there is a large number of per-pixel lights affecting each object, but their disadvantages are the lack of hardware multisampling and inability to choose the lighting model per material. In place of multisample antialiasing, a FXAA post-processing edge filter can be used, see the TestScene script application for an example of how to use.
 
 The steps for rendering each viewport on each frame are roughly the following:
 
@@ -398,7 +398,7 @@ The steps for rendering each viewport on each frame are roughly the following:
 
 The rendering operations proceed in the following order:
 
-- Opaque geometry ambient pass, or G-buffer pass in case of light pre-pass rendering.
+- Opaque geometry ambient pass, or G-buffer pass in deferred rendering modes.
 - Opaque geometry per-pixel lighting passes. For shadow casting lights, the shadow map is rendered first.
 - (%Light pre-pass only) Opaque geometry material pass, which renders the objects with accumulated per-pixel lighting.
 - Pre-alpha rendering pass for custom render ordering such as the skybox.
@@ -437,22 +437,22 @@ Note that many more optimization opportunities are possible at the content level
 
 See also \ref Materials "Materials", \ref Lights "Lights and shadows", \ref Particles "Particle systems", \ref Postprocessing "Post-processing", \ref Zones "Zones", and \ref AuxiliaryViews "Auxiliary views".
 
-See \ref ForwardPrepass "Forward and light pre-pass rendering" for detailed discussion on the two rendering modes.
+See \ref RenderingModes "Rendering modes" for detailed discussion on the forward, light pre-pass and deferred rendering modes.
 
 See \ref APIDifferences "Differences between Direct3D9 and OpenGL" for what to watch out for when using the low-level rendering functionality directly.
 
 
-\page ForwardPrepass Forward and light pre-pass rendering
+\page RenderingModes Rendering modes
 
-Urho3D implements both forward and light pre-pass rendering modes. Where they differ is how per-pixel lighting is calculated for opaque objects; transparent objects always use forward rendering.
+Urho3D implements both forward, light pre-pass and deferred rendering modes. Where they differ is how per-pixel lighting is calculated for opaque objects; transparent objects always use forward rendering.
 
-\section ForwardPrepass_Forward Forward rendering
+\section RenderingModes_Forward Forward rendering
 
 Forward rendering begins with an ambient light pass for the objects; this also adds any per-vertex lights. Then, the objects are re-rendered for each per-pixel light affecting them (basic multipass rendering), up to the maximum per-pixel light count which is by default unlimited, but can be reduced with \ref Drawable::SetMaxLights "SetMaxLights()". The render operations are sorted by light, ie. render the effect of the first light on all affected objects first, then the second etc. If shadow maps are re-used (default on), a shadow casting light's shadow map will be updated immediately before rendering the lit objects. When shadow maps are not re-used, all shadow maps are updated first even before drawing the ambient pass.
 
 Materials can also define an optimization pass for forward rendering where the ambient light and the first per-pixel light are combined. This pass can not be used, however, if there are per-vertex lights affecting the object, or if the ambient light has a per-vertex gradient.
 
-\section ForwardPrepass_Prepass Light pre-pass rendering
+\section RenderingModes_Prepass Light pre-pass rendering
 
 %Light pre-pass requires a minimum of two passes per object. First the normal, specular power, depth and lightmask (8 low bits only) of opaque objects are rendered to the following G-buffer. If the INTZ readable hardware depth-stencil texture format is available, the second color rendertarget will be omitted:
 
@@ -464,15 +464,29 @@ After the G-buffer is complete, light volumes (spot and point lights) or fullscr
 
 Finally the opaque objects are re-rendered during the material pass, which combines ambient and vertex lighting with per-pixel lighting from the light accumulation buffer. After this rendering proceeds to the pre-alpha pass, transparent object rendering pass, and the post-alpha pass, just like forward rendering.
 
-\section ForwardPrepass_Comparision Advantages and disadvantages
+\section RenderingModes_Deferred Deferred rendering
 
-Whether using forward or light pre-pass rendering is more advantageous depends on the scene and lighting complexity. 
+Deferred rendering needs to render each opaque object only once to the G-buffer, but this rendering pass is much heavier than in light pre-pass rendering, as also ambient, emissive and diffuse albedo information is output at the same time. The G-buffer is the following, with the last color rendertarget omitted if hardware depth can be read:
 
-If the scene contains a large number of complex objects lit by multiple lights, forward rendering quickly increases the total draw call and vertex count due to re-rendering the objects for each light. However, light pre-pass rendering has a higher fixed cost due to the generation of the G-buffer. Also, in forward per-pixel lighting more calculations (such as light direction and shadow map coordinates) can be done at the vertex shader level, while in light pre-pass all calculations need to happen per-pixel. This means that for a low light count, for example 1-2 per object, forward rendering will run faster based on the more efficient lighting calculations alone.
+- RT0: Final rendertarget with ambient, per-vertex and emissive color (D3DFMT_X8R8G8B8)
+- RT1: Diffuse albedo and specular intensity (D3DFMT_A8R8G8B8)
+- RT2: World-space normal and specular power (D3DFMT_A8R8G8B8)
+- RT3: Linear depth (D3DFMT_R32F)
+- DS: Hardware depth and lightmask (D3DFMT_D24S8 or INTZ)
+
+After the G-buffer has been rendered, light volumes will be rendered into the final rendertarget to accumulate per-pixel lighting. As the material albedo is available, all lighting calculations are final and output both the diffuse and specular color at the same time. After light accumulation rendering proceeds to pre-alpha, transparent, and post-alpha passes, as in other rendering modes.
+
+\section RenderingModes_Comparision Advantages and disadvantages
+
+Whether using forward or deferred rendering modes is more advantageous depends on the scene and lighting complexity.
+
+If the scene contains a large number of complex objects lit by multiple lights, forward rendering quickly increases the total draw call and vertex count due to re-rendering the objects for each light. However, light pre-pass and deferred rendering have a higher fixed cost due to the generation of the G-buffer. Also, in forward per-pixel lighting more calculations (such as light direction and shadow map coordinates) can be done at the vertex shader level, while in deferred all calculations need to happen per-pixel. This means that for a low light count, for example 1-2 per object, forward rendering will run faster based on the more efficient lighting calculations alone.
+
+Forward rendering makes it possible to use hardware multisampling and different shading models in different materials if needed, while neither is possible in the deferred modes. Also, only forward rendering allows to calculate the material's diffuse and specular light response with the most accuracy. %Light pre-pass rendering needs to reconstruct light specular color from the accumulated diffuse light color, which is inaccurate in case of overlapping lights. Deferred rendering on the other hand can not use the material's full specular color, it only stores a monochromatic intensity based on the green component into the G-buffer.
 
-Forward rendering also enables hardware multisampling and using different shading models in different materials if needed, while neither is possible in light pre-pass rendering.
+%Light pre-pass rendering has a much more lightweight G-buffer pass, but it must render all opaque geometry twice. %Light accumulation in pre-pass mode is slightly faster than in deferred. Despite this, unless there is significant overdraw, in vertex-heavy scenes deferred rendering will likely be faster than light pre-pass.
 
-Finally note that due to OpenGL framebuffer object limitations an extra framebuffer blit has to happen at the end of light pre-pass rendering, which costs some performance. Also, because multiple rendertargets on OpenGL must have the same format, an R32F texture can not be used for linear depth, but instead 24-bit depth is manually encoded and decoded into RGB channels.
+Finally note that due to OpenGL framebuffer object limitations an extra framebuffer blit has to happen at the end in both light pre-pass and deferred rendering, which costs some performance. Also, because multiple rendertargets on OpenGL must have the same format, an R32F texture can not be used for linear depth, but instead 24-bit depth is manually encoded and decoded into RGB channels.
 
 
 \page APIDifferences Differences between Direct3D9 and OpenGL
@@ -546,7 +560,7 @@ A technique definition looks like this:
 
 \code
 <technique>
-    <pass name="base|litbase|light|prealpha|postalpha|gbuffer|material|shadow" vs="VertexShaderName" ps="PixelShaderName"
+    <pass name="base|litbase|light|prealpha|postalpha|prepass|material|deferred|shadow" vs="VertexShaderName" ps="PixelShaderName"
         alphatest="true|false" blend="replace|add|multiply|alpha|addalpha|premulalpha|invdestalpha"
         depthtest="always|equal|less|lessequal|greater|greaterequal" depthwrite="true|false" />
     <pass ... />
@@ -561,8 +575,9 @@ The purposes of the different passes are:
 - light: Renders one per-pixel light's contribution additively.
 - prealpha: Custom rendering pass after opaque geometry. Can be used for example to render the skybox.
 - postalpha: Custom rendering pass after transparent geometry.
-- gbuffer: %Light pre-pass only - renders normals, specular power and depth to the G-buffer.
+- prepass: %Light pre-pass only - renders normals, specular power and depth to the G-buffer.
 - material: %Light pre-pass only - renders opaque geometry final color by combining ambient light, per-vertex lights and per-pixel light accumulation.
+- deferred: Deferred rendering only - renders ambient light and per-vertex lights to the output rendertarget, and diffuse albedo, normals, specular intensity + power and depth to the G-buffer.
 - shadow: Renders depth only for shadow map generation.
 
 By default draw calls within passes are sorted by render state, but transparent base and light passes, as well as the postalpha pass, are sorted by distance back to front.
@@ -594,7 +609,7 @@ It is possible to limit which objects are affected by each light, by calling \re
 
 Care must be utilized when doing light culling with lightmasks, because they easily create situations where a light's influence is cut off unnaturally. However, they can be helpful in preventing light spill into undesired areas, for example lights inside one room bleeding into another, without having to resort into shadow-casting lights.
 
-In light pre-pass rendering, light culling happens by writing the objects' lightmasks to the stencil buffer during G-buffer rendering, and comparing the stencil buffer to the light's light mask when rendering light volumes. In this case lightmasks are limited to the low 8 bits only.
+In light pre-pass and deferred rendering, light culling happens by writing the objects' lightmasks to the stencil buffer during G-buffer rendering, and comparing the stencil buffer to the light's light mask when rendering light volumes. In this case lightmasks are limited to the low 8 bits only.
 
 \section Lights_ShadowedLights Shadowed lights
 

+ 2 - 1
Docs/ScriptAPI.dox

@@ -2273,6 +2273,7 @@ Properties:<br>
 - uint numBatches (readonly)
 - bool sm3Support (readonly)
 - bool lightPrepassSupport (readonly)
+- bool deferredSupport (readonly)
 - bool hardwareDepthSupport (readonly)
 - bool hardwareShadowSupport (readonly)
 - bool hiresShadowSupport (readonly)
@@ -2291,7 +2292,7 @@ Properties:<br>
 - String& typeName (readonly)
 - uint numViewports
 - Viewport@[] viewports
-- bool lightPrepass
+- RenderMode renderMode
 - bool specularLighting
 - int textureAnisotropy
 - TextureFilterMode textureFilterMode

+ 8 - 5
Engine/Engine/DebugHud.cpp

@@ -35,6 +35,12 @@
 
 #include "DebugNew.h"
 
+static const String renderModeTexts[] = {
+    "Forward",
+    "Prepass",
+    "Deferred"
+};
+
 static const String qualityTexts[] = {
     "Low",
     "Med",
@@ -128,12 +134,9 @@ void DebugHud::Update(float timeStep)
     {
         String mode;
         
-        if (renderer->GetLightPrepass())
-            mode += "Prepass ";
-        else
-            mode += "Forward ";
+        mode += renderModeTexts[renderer->GetRenderMode()];
         
-        mode += "Tex: " + qualityTexts[renderer->GetTextureQuality()];
+        mode += " Tex: " + qualityTexts[renderer->GetTextureQuality()];
         
         mode += " Mat: " + qualityTexts[renderer->GetMaterialQuality()];
         

+ 5 - 3
Engine/Engine/Engine.cpp

@@ -72,6 +72,7 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     if (initialized_)
         return true;
     
+    RenderMode mode = RENDER_FORWARD;
     int width = 0;
     int height = 0;
     int multiSample = 1;
@@ -81,7 +82,6 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     bool vsync = false;
     bool tripleBuffer = false;
     bool forceSM2 = false;
-    bool prepass = false;
     bool shadows = true;
     bool lqShadows = false;
     bool sound = true;
@@ -110,7 +110,9 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
             else if (argument == "mono")
                 stereo = false;
             else if (argument == "prepass")
-                prepass = true;
+                mode = RENDER_PREPASS;
+            else if (argument == "deferred")
+                mode = RENDER_DEFERRED;
             else if (argument == "noshadows")
                 shadows = false;
             else if (argument == "lqshadows")
@@ -227,7 +229,7 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
         if (!graphics->SetMode(width, height, fullscreen, vsync, tripleBuffer, multiSample))
             return false;
         
-        renderer->SetLightPrepass(prepass);
+        renderer->SetRenderMode(mode);
         renderer->SetDrawShadows(shadows);
         if (shadows && lqShadows)
             renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);

+ 10 - 3
Engine/Engine/GraphicsAPI.cpp

@@ -290,8 +290,9 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterEnumValue("PassType", "PASS_LIGHT", PASS_LIGHT);
     engine->RegisterEnumValue("PassType", "PASS_PREALPHA", PASS_PREALPHA);
     engine->RegisterEnumValue("PassType", "PASS_POSTALPHA", PASS_POSTALPHA);
-    engine->RegisterEnumValue("PassType", "PASS_GBUFFER", PASS_GBUFFER);
+    engine->RegisterEnumValue("PassType", "PASS_PREPASS", PASS_PREPASS);
     engine->RegisterEnumValue("PassType", "PASS_MATERIAL", PASS_MATERIAL);
+    engine->RegisterEnumValue("PassType", "PASS_DEFERRED", PASS_DEFERRED);
     engine->RegisterEnumValue("PassType", "PASS_SHADOW", PASS_SHADOW);
     
     engine->RegisterEnum("BlendMode");
@@ -779,6 +780,7 @@ static void RegisterGraphics(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Graphics", "uint get_numBatches() const", asMETHOD(Graphics, GetNumBatches), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_sm3Support() const", asMETHOD(Graphics, GetSM3Support), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_lightPrepassSupport() const", asMETHOD(Graphics, GetLightPrepassSupport), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Graphics", "bool get_deferredSupport() const", asMETHOD(Graphics, GetDeferredSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hardwareDepthSupport() const", asMETHOD(Graphics, GetHardwareDepthSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hardwareShadowSupport() const", asMETHOD(Graphics, GetHardwareShadowSupport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Graphics", "bool get_hiresShadowSupport() const", asMETHOD(Graphics, GetHiresShadowSupport), asCALL_THISCALL);
@@ -796,6 +798,11 @@ static Renderer* GetRenderer()
 
 static void RegisterRenderer(asIScriptEngine* engine)
 {
+    engine->RegisterEnum("RenderMode");
+    engine->RegisterEnumValue("RenderMode", "RENDER_FORWARD", RENDER_FORWARD);
+    engine->RegisterEnumValue("RenderMode", "RENDER_PREPASS", RENDER_PREPASS);
+    engine->RegisterEnumValue("RenderMode", "RENDER_DEFERRED", RENDER_DEFERRED);
+    
     engine->RegisterGlobalProperty("const int QUALITY_LOW", (void*)&QUALITY_LOW);
     engine->RegisterGlobalProperty("const int QUALITY_MEDIUM", (void*)&QUALITY_MEDIUM);
     engine->RegisterGlobalProperty("const int QUALITY_HIGH", (void*)&QUALITY_HIGH);
@@ -811,8 +818,8 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "uint get_numViewports() const", asMETHOD(Renderer, GetNumViewports), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool set_viewports(uint, Viewport@+)", asMETHOD(Renderer, SetViewport), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "Viewport@+ get_viewports(uint) const", asMETHOD(Renderer, GetViewport), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "void set_lightPrepass(bool)", asMETHOD(Renderer, SetLightPrepass), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Renderer", "bool get_lightPrepass() const", asMETHOD(Renderer, GetLightPrepass), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_renderMode(RenderMode)", asMETHOD(Renderer, SetRenderMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "RenderMode get_renderMode() const", asMETHOD(Renderer, GetRenderMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_specularLighting(bool)", asMETHOD(Renderer, SetSpecularLighting), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool get_specularLighting() const", asMETHOD(Renderer, GetSpecularLighting), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_textureAnisotropy(int)", asMETHOD(Renderer, SetTextureAnisotropy), asCALL_THISCALL);

+ 1 - 1
Engine/Graphics/Batch.h

@@ -99,7 +99,7 @@ struct Batch
     bool overrideView_;
     /// Base batch flag. This tells to draw the object fully without light optimizations.
     bool isBase_;
-    /// 8-bit light mask for stencil marking in light pre-pass rendering.
+    /// 8-bit light mask for stencil marking in deferred rendering.
     unsigned char lightMask_;
 };
 

+ 11 - 2
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -177,6 +177,7 @@ Graphics::Graphics(Context* context) :
     deviceLost_(false),
     systemDepthStencil_(false),
     lightPrepassSupport_(false),
+    deferredSupport_(false),
     hardwareDepthSupport_(false),
     hardwareShadowSupport_(false),
     hiresShadowSupport_(false),
@@ -2002,6 +2003,7 @@ void Graphics::CheckFeatureSupport()
 {
     // Reset features first
     lightPrepassSupport_ = false;
+    deferredSupport_ = false;
     hardwareShadowSupport_ = false;
     hiresShadowSupport_ = false;
     streamOffsetSupport_ = false;
@@ -2070,7 +2072,7 @@ void Graphics::CheckFeatureSupport()
             hasSM3_ = true;
     }
     
-    // Check for readable hardware depth-stencil format (INTZ) and light pre-pass support
+    // Check for readable hardware depth-stencil format (INTZ), light pre-pass and deferred rendering support
     if (impl_->CheckFormatSupport((D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z'), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_TEXTURE))
     {
         // Sampling INTZ buffer directly while also using it for depth test results in performance loss on ATI GPUs,
@@ -2080,15 +2082,22 @@ void Graphics::CheckFeatureSupport()
             hardwareDepthSupport_ = true;
             lightPrepassSupport_ = true;
             depthStencilFormat = MAKEFOURCC('I', 'N', 'T', 'Z');
+            if (impl_->deviceCaps_.NumSimultaneousRTs >= 3)
+                deferredSupport_ = true;
         }
     }
     
     if (!hardwareDepthSupport_)
     {
-        // If hardware depth is not supported, must support 2 rendertargets and R32F format for light pre-pass
+        // If hardware depth is not supported, must support 2 rendertargets and R32F format for light pre-pass,
+        // and 4 for deferred rendering
         if (impl_->deviceCaps_.NumSimultaneousRTs >= 2 && impl_->CheckFormatSupport(D3DFMT_R32F, D3DUSAGE_RENDERTARGET,
             D3DRTYPE_TEXTURE))
+        {
             lightPrepassSupport_ = true;
+            if (impl_->deviceCaps_.NumSimultaneousRTs >= 4)
+                deferredSupport_ = true;
+        }
     }
     
     // Check for stream offset (needed for instancing)

+ 5 - 1
Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -147,7 +147,7 @@ public:
     void SetDepthStencil(RenderSurface* depthStencil);
     /// %Set depth-stencil surface.
     void SetDepthStencil(Texture2D* texture);
-    /// %Set view texture (light pre-pass final output rendertarget) to prevent it from being sampled.
+    /// %Set view texture (deferred rendering final output rendertarget) to prevent it from being sampled.
     void SetViewTexture(Texture* texture);
     /// %Set viewport.
     void SetViewport(const IntRect& rect);
@@ -220,6 +220,8 @@ public:
     bool GetSM3Support() const { return hasSM3_; }
     /// Return whether light pre-pass rendering is supported.
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
+    /// Return whether deferred rendering is supported.
+    bool GetDeferredSupport() const { return deferredSupport_; }
     /// Return whether hardware depth can be read as a texture.
     bool GetHardwareDepthSupport() const { return hardwareDepthSupport_; }
     /// Return whether shadow map depth compare is done in hardware.
@@ -381,6 +383,8 @@ private:
     bool systemDepthStencil_;
     /// Light pre-pass rendering support flag.
     bool lightPrepassSupport_;
+    /// Deferred rendering support flag.
+    bool deferredSupport_;
     /// Hardware depth texture support flag.
     bool hardwareDepthSupport_;
     /// Hardware shadow map depth compare support flag.

+ 1 - 1
Engine/Graphics/Drawable.cpp

@@ -265,7 +265,7 @@ void Drawable::LimitVertexLights(bool removeConvertedLights)
     const BoundingBox& box = GetWorldBoundingBox();
     for (unsigned i = vertexLights_.Size() - 1; i < vertexLights_.Size(); --i)
     {
-        // If necessary (light pre-pass rendering), remove lights that were converted to per-vertex
+        // If necessary (deferred rendering), remove lights that were converted to per-vertex
         if (removeConvertedLights && !vertexLights_[i]->GetPerVertex())
             vertexLights_.Erase(i);
         else

+ 13 - 3
Engine/Graphics/GraphicsDefs.h

@@ -26,6 +26,14 @@
 #include "HashBase.h"
 #include "StringHash.h"
 
+/// Rendering modes.
+enum RenderMode
+{
+    RENDER_FORWARD = 0,
+    RENDER_PREPASS,
+    RENDER_DEFERRED
+};
+
 /// Primitive type.
 enum PrimitiveType
 {
@@ -165,13 +173,14 @@ enum TextureUsage
 /// Rendering passes.
 enum PassType
 {
-    PASS_BASE,
+    PASS_BASE = 0,
     PASS_LITBASE,
     PASS_LIGHT,
     PASS_PREALPHA,
     PASS_POSTALPHA,
-    PASS_GBUFFER,
+    PASS_PREPASS,
     PASS_MATERIAL,
+    PASS_DEFERRED,
     PASS_SHADOW,
     MAX_PASSES
 };
@@ -238,10 +247,11 @@ extern StringHash PSP_LIGHTMATRICES;
 enum TextureUnit
 {
     TU_DIFFUSE = 0,
-    TU_DEPTHBUFFER = 0,
+    TU_ALBEDOBUFFER = 0,
     TU_NORMAL = 1,
     TU_NORMALBUFFER = 1,
     TU_EMISSIVE = 2,
+    TU_DEPTHBUFFER = 2,
     TU_SPECULAR = 3,
     TU_DETAIL = 4,
     TU_ENVIRONMENT = 5,

+ 15 - 5
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -130,6 +130,7 @@ Graphics::Graphics(Context* context_) :
     tripleBuffer_(false),
     flushGPU_(true),
     lightPrepassSupport_(false),
+    deferredSupport_(false),
     hardwareDepthSupport_(false),
     numPrimitives_(0),
     numBatches_(0),
@@ -1877,10 +1878,14 @@ unsigned Graphics::GetDepthStencilFormat()
 
 void Graphics::CheckFeatureSupport()
 {
-    // Check supported features: light pre-pass rendering and hardware depth texture
+    // Check supported features: light pre-pass and deferred rendering and hardware depth texture
     lightPrepassSupport_ = false;
+    deferredSupport_ = false;
     hardwareDepthSupport_ = false;
-    
+
+    int numSupportedRTs = 1;
+    glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &numSupportedRTs);
+
     // For now hardware depth texture is only tested for on NVIDIA hardware because of visual artifacts and slowdown on ATI
     String vendorString = String((const char*)glGetString(GL_VENDOR)).ToUpper();
     if (vendorString.Find("NVIDIA") != String::NPOS)
@@ -1894,7 +1899,11 @@ void Graphics::CheckFeatureSupport()
         
         // If hardware depth textures work, this means also light pre-pass is automatically supported
         if (CheckFramebuffer())
+        {
             lightPrepassSupport_ = true;
+            if (numSupportedRTs >= 3)
+                deferredSupport_ = true;
+        }
         else
             hardwareDepthSupport_ = false;
         
@@ -1903,11 +1912,11 @@ void Graphics::CheckFeatureSupport()
     
     if (!hardwareDepthSupport_)
     {
-        // If hardware depth is not supported, must support 2 rendertargets for light pre-pass
-        int numSupportedRTs = 1;
-        glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &numSupportedRTs);
+        // If hardware depth is not supported, must support 2 rendertargets for light pre-pass, and 4 for deferred
         if (numSupportedRTs >= 2)
             lightPrepassSupport_ = true;
+        if (numSupportedRTs >= 4)
+            deferredSupport_ = true;
     }
 }
 
@@ -2046,6 +2055,7 @@ void Graphics::SetTextureUnitMappings()
     textureUnits_["ShadowMap"] = TU_SHADOWMAP;
     textureUnits_["FaceSelectCubeMap"] = TU_FACESELECT;
     textureUnits_["IndirectionCubeMap"] = TU_INDIRECTION;
+    textureUnits_["AlbedoBuffer"] = TU_ALBEDOBUFFER;
     textureUnits_["NormalBuffer"] = TU_NORMALBUFFER;
     textureUnits_["DepthBuffer"] = TU_DEPTHBUFFER;
     textureUnits_["LightBuffer"] = TU_LIGHTBUFFER;

+ 5 - 1
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -170,7 +170,7 @@ public:
     void SetDepthStencil(RenderSurface* depthStencil);
     /// Set depth-stencil surface.
     void SetDepthStencil(Texture2D* texture);
-    /// %Set view texture (light pre-pass final output rendertarget) to prevent it from being sampled.
+    /// %Set view texture (deferred rendering final output rendertarget) to prevent it from being sampled.
     void SetViewTexture(Texture* texture);
     /// Set viewport.
     void SetViewport(const IntRect& rect);
@@ -241,6 +241,8 @@ public:
     bool GetSM3Support() const { return false; }
     /// Return whether light pre-pass rendering is supported.
     bool GetLightPrepassSupport() const { return lightPrepassSupport_; }
+    /// Return whether deferred rendering is supported.
+    bool GetDeferredSupport() const { return deferredSupport_; }
     /// Return whether hardware depth texture is supported.
     bool GetHardwareDepthSupport() const { return hardwareDepthSupport_; }
     /// Return whether shadow map depth compare is done in hardware. Always true on OpenGL.
@@ -390,6 +392,8 @@ private:
     bool flushGPU_;
     /// Light prepass support flag.
     bool lightPrepassSupport_;
+    /// Deferred rendering support flag.
+    bool deferredSupport_;
     /// Hardware depth support flag.
     bool hardwareDepthSupport_;
     /// Number of primitives this frame.

+ 25 - 20
Engine/Graphics/Renderer.cpp

@@ -278,7 +278,7 @@ Renderer::Renderer(Context* context) :
     occlusionBufferSize_(256),
     occluderSizeThreshold_(0.1f),
     shadersChangedFrameNumber_(M_MAX_UNSIGNED),
-    lightPrepass_(false),
+    renderMode_(RENDER_FORWARD),
     specularLighting_(true),
     drawShadows_(true),
     reuseShadowMaps_(true),
@@ -328,27 +328,29 @@ bool Renderer::SetViewport(unsigned index, Viewport* viewport)
     return true;
 }
 
-void Renderer::SetLightPrepass(bool enable)
+void Renderer::SetRenderMode(RenderMode mode)
 {
     if (!initialized_)
     {
-        LOGERROR("Can not switch light pre-pass rendering before setting initial screen mode");
+        LOGERROR("Can not switch rendering mode before setting initial screen mode");
         return;
     }
     
-    if (!graphics_->GetLightPrepassSupport())
-        enable = false;
+    if (mode == RENDER_PREPASS && !graphics_->GetLightPrepassSupport())
+        mode = RENDER_FORWARD;
+    if (mode == RENDER_DEFERRED && !graphics_->GetDeferredSupport())
+        mode = RENDER_FORWARD;
     
-    if (enable != lightPrepass_)
+    if (mode != renderMode_)
     {
-        // Light prepass is incompatible with hardware multisampling, so set new screen mode with 1x sampling if in use
-        if (graphics_->GetMultiSample() > 1)
+        // Deferred rendering is incompatible with hardware multisampling, so set new screen mode with 1x sampling if in use
+        if (mode != RENDER_FORWARD && graphics_->GetMultiSample() > 1)
         {
             graphics_->SetMode(graphics_->GetWidth(), graphics_->GetHeight(), graphics_->GetFullscreen(), graphics_->GetVSync(),
                 graphics_->GetTripleBuffer(), 1);
         }
         
-        lightPrepass_ = enable;
+        renderMode_ = mode;
         shadersDirty_ = true;
     }
 }
@@ -1101,7 +1103,8 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, b
         }
         else
         {
-            if (type == PASS_BASE || type == PASS_MATERIAL)
+            // Check if pass has vertex lighting support
+            if (type == PASS_BASE || type == PASS_MATERIAL || type == PASS_DEFERRED)
             {
                 unsigned numVertexLights = 0;
                 if (batch.lightQueue_)
@@ -1347,19 +1350,19 @@ void Renderer::LoadShaders()
     lightVS_.Clear();
     lightPS_.Clear();
     
-    if (lightPrepass_)
+    if (renderMode_ != RENDER_FORWARD)
     {
         lightVS_.Resize(MAX_DEFERRED_LIGHT_VS_VARIATIONS);
         lightPS_.Resize(MAX_DEFERRED_LIGHT_PS_VARIATIONS);
         
         unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
+        String shaderName = renderMode_ == RENDER_PREPASS ? "PrepassLight_" : "DeferredLight_";
         
         for (unsigned i = 0; i < MAX_DEFERRED_LIGHT_VS_VARIATIONS; ++i)
-            lightVS_[i] = GetVertexShader("LightVolume_" + deferredLightVSVariations[i]);
+            lightVS_[i] = GetVertexShader(shaderName + deferredLightVSVariations[i]);
         
         for (unsigned i = 0; i < lightPS_.Size(); ++i)
         {
-            /// \todo Allow specifying the light volume shader name for different lighting models
             String ortho, hwDepth;
             #ifdef USE_OPENGL
             hwDepth = hwVariations[graphics_->GetHardwareDepthSupport() ? 1 : 0];
@@ -1372,11 +1375,11 @@ void Renderer::LoadShaders()
             
             if (i & DLPS_SHADOW)
             {
-                lightPS_[i] = GetPixelShader("LightVolume_" + ortho + lightPSVariations[i % DLPS_ORTHO] +
+                lightPS_[i] = GetPixelShader(shaderName + ortho + lightPSVariations[i % DLPS_ORTHO] +
                     shadowVariations[shadows] + hwDepth);
             }
             else
-                lightPS_[i] = GetPixelShader("LightVolume_" + ortho + lightPSVariations[i % DLPS_ORTHO] + hwDepth);
+                lightPS_[i] = GetPixelShader(shaderName + ortho + lightPSVariations[i % DLPS_ORTHO] + hwDepth);
         }
     }
     
@@ -1385,11 +1388,13 @@ void Renderer::LoadShaders()
 
 void Renderer::LoadMaterialShaders(Technique* technique)
 {
-    if (lightPrepass_ && technique->HasPass(PASS_GBUFFER))
+    if (renderMode_ == RENDER_PREPASS && technique->HasPass(PASS_PREPASS))
     {
-        LoadPassShaders(technique, PASS_GBUFFER);
+        LoadPassShaders(technique, PASS_PREPASS);
         LoadPassShaders(technique, PASS_MATERIAL);
     }
+    else if (renderMode_ == RENDER_DEFERRED && technique->HasPass(PASS_DEFERRED))
+        LoadPassShaders(technique, PASS_DEFERRED);
     else
     {
         LoadPassShaders(technique, PASS_BASE);
@@ -1420,7 +1425,7 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
         pixelShaderName += "_";
     
     // If hardware depth is used, choose a G-buffer shader that does not write depth manually
-    if (type == PASS_GBUFFER)
+    if (type == PASS_PREPASS || type == PASS_DEFERRED)
     {
         unsigned hwDepth = graphics_->GetHardwareDepthSupport() ? 1 : 0;
         vertexShaderName += hwVariations[hwDepth];
@@ -1471,8 +1476,8 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
     }
     else
     {
-        // Load vertex light variations for forward ambient pass and pre-pass material pass
-        if (type == PASS_BASE || type == PASS_MATERIAL)
+        // Load vertex light variations for forward ambient pass, deferred G-buffer pass and pre-pass material pass
+        if (type == PASS_BASE || type == PASS_MATERIAL || type == PASS_DEFERRED)
         {
             vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
             for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)

+ 7 - 7
Engine/Graphics/Renderer.h

@@ -170,8 +170,8 @@ public:
     void SetNumViewports(unsigned num);
     /// %Set a viewport. Return true if successful.
     bool SetViewport(unsigned index, Viewport* viewport);
-    /// %Set light prepass rendering on/off.
-    void SetLightPrepass(bool enable);
+    /// %Set rendering mode (forward / light pre-pass / deferred.)
+    void SetRenderMode(RenderMode mode);
     /// %Set specular lighting on/off.
     void SetSpecularLighting(bool enable);
     /// %Set texture anisotropy.
@@ -208,8 +208,8 @@ public:
     unsigned GetNumViewports() const { return viewports_.Size(); }
     /// Return viewport.
     Viewport* GetViewport(unsigned index) const;
-    /// Return whether light prepass rendering is enabled.
-    bool GetLightPrepass() const { return lightPrepass_; }
+    /// Return rendering mode.
+    RenderMode GetRenderMode() const { return renderMode_; }
     /// Return whether specular lighting is enabled.
     bool GetSpecularLighting() const { return specularLighting_; }
     /// Return whether drawing shadows is enabled.
@@ -293,7 +293,7 @@ public:
     Geometry* GetLightGeometry(Light* light);
     /// Allocate a shadow map. If shadow map reuse is disabled, a different map is returned each time.
     Texture2D* GetShadowMap(Light* light, Camera* camera, unsigned viewWidth, unsigned viewHeight);
-    /// Allocate a rendertarget or depth-stencil texture for light pre-pass rendering or postprocessing. Should only be called during actual rendering, not before.
+    /// Allocate a rendertarget or depth-stencil texture for deferred rendering or postprocessing. Should only be called during actual rendering, not before.
     Texture2D* GetScreenBuffer(int width, int height, unsigned format, bool filtered = false);
     /// Allocate a depth-stencil surface that does not need to be readable. Should only be called during actual rendering, not before.
     RenderSurface* GetDepthStencil(int width, int height);
@@ -454,8 +454,8 @@ private:
     unsigned numBatches_;
     /// Frame number on which shaders last changed.
     unsigned shadersChangedFrameNumber_;
-    /// Light prepass mode flag.
-    bool lightPrepass_;
+    /// Rendering mode.
+    RenderMode renderMode_;
     /// Specular lighting flag.
     bool specularLighting_;
     /// Draw shadows flag.

+ 2 - 1
Engine/Graphics/Technique.cpp

@@ -38,8 +38,9 @@ static const String passNames[] =
     "light",
     "prealpha",
     "postalpha",
-    "gbuffer",
+    "prepass",
     "material",
+    "deferred",
     "shadow",
     ""
 };

+ 113 - 59
Engine/Graphics/View.cpp

@@ -181,7 +181,7 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     if (!octree)
         return false;
     
-    lightPrepass_ = renderer_->GetLightPrepass();
+    renderMode_ = renderer_->GetRenderMode();
     octree_ = octree;
     camera_ = camera;
     renderTarget_ = renderTarget;
@@ -312,7 +312,7 @@ void View::Render()
     graphics_->SetTexture(TU_INDIRECTION, renderer_->GetIndirectionCubeMap());
     
     // Set "view texture" to prevent destination texture sampling in case we do not render to the destination directly
-    // ie. when using light pre-pass and/or doing post-processing
+    // ie. when using deferred rendering and/or doing post-processing
     if (renderTarget_)
         graphics_->SetViewTexture(renderTarget_->GetParentTexture());
     
@@ -324,10 +324,10 @@ void View::Render()
     #endif
     
     // Render
-    if (lightPrepass_)
-        RenderBatchesLightPrepass();
-    else
+    if (renderMode_ == RENDER_FORWARD)
         RenderBatchesForward();
+    else
+        RenderBatchesDeferred();
     
     #ifdef USE_OPENGL
     camera_->SetFlipVertical(false);
@@ -655,8 +655,8 @@ void View::GetBatches()
                         maxLightsDrawables_.Insert(drawable);
                 }
                 
-                // In light pre-pass mode, store the light volume batch now
-                if (lightPrepass_)
+                // In deferred modes, store the light volume batch now
+                if (renderMode_ != RENDER_FORWARD)
                 {
                     Batch volumeBatch;
                     volumeBatch.geometry_ = renderer_->GetLightGeometry(light);
@@ -740,10 +740,10 @@ void View::GetBatches()
                 
                 Pass* pass = 0;
                 
-                // In light prepass mode check for G-buffer and material passes first
-                if (lightPrepass_)
+                // In deferred modes check for G-buffer and material passes first
+                if (renderMode_ == RENDER_PREPASS)
                 {
-                    pass = tech->GetPass(PASS_GBUFFER);
+                    pass = tech->GetPass(PASS_PREPASS);
                     if (pass)
                     {
                         // If the opaque object has a zero lightmask, have to skip light buffer optimization
@@ -759,6 +759,9 @@ void View::GetBatches()
                     }
                 }
                 
+                if (renderMode_ == RENDER_DEFERRED)
+                    pass = tech->GetPass(PASS_DEFERRED);
+                
                 // Next check for forward base pass
                 if (!pass)
                 {
@@ -770,17 +773,17 @@ void View::GetBatches()
                 
                 if (pass)
                 {
-                    // Check for vertex lights (both forward unlit and light pre-pass material pass)
+                    // Check for vertex lights (both forward unlit, light pre-pass material pass, and deferred G-buffer)
                     const PODVector<Light*>& vertexLights = drawable->GetVertexLights();
                     if (!vertexLights.Empty())
                     {
-                        // In light pre-pass mode, check if this is an opaque object that has converted its lights to per-vertex
+                        // In deferred modes, check if this is an opaque object that has converted its lights to per-vertex
                         // due to overflowing the pixel light count. These need to be skipped as the per-pixel accumulation
                         // already renders the light
                         /// \todo Sub-geometries might need different interpretation if opaque & alpha are mixed
                         if (!vertexLightsProcessed)
                         {
-                            drawable->LimitVertexLights(lightPrepass_ && pass->GetBlendMode() == BLEND_REPLACE);
+                            drawable->LimitVertexLights(renderMode_ != RENDER_FORWARD && pass->GetBlendMode() == BLEND_REPLACE);
                             vertexLightsProcessed = true;
                         }
                         
@@ -804,8 +807,18 @@ void View::GetBatches()
                     
                     if (pass->GetBlendMode() == BLEND_REPLACE)
                     {
-                        FinalizeBatch(baseBatch, tech, pass);
-                        baseQueue_.AddBatch(baseBatch);
+                        if (pass->GetType() != PASS_DEFERRED)
+                        {
+                            FinalizeBatch(baseBatch, tech, pass);
+                            baseQueue_.AddBatch(baseBatch);
+                        }
+                        else
+                        {
+                            // Allow G-buffer pass instancing only if lightmask matches zone lightmask
+                            baseBatch.lightMask_ = GetLightMask(drawable);
+                            FinalizeBatch(baseBatch, tech, pass, baseBatch.lightMask_ == (baseBatch.zone_->GetLightMask() & 0xff));
+                            gbufferQueue_.AddBatch(baseBatch);
+                        }
                     }
                     else
                     {
@@ -853,7 +866,7 @@ void View::UpdateGeometries()
         queue->AddWorkItem(item);
         item.start_ = &preAlphaQueue_;
         queue->AddWorkItem(item);
-        if (lightPrepass_)
+        if (renderMode_ != RENDER_FORWARD)
         {
             item.start_ = &gbufferQueue_;
             queue->AddWorkItem(item);
@@ -937,7 +950,8 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
             continue;
         
         // Do not create pixel lit forward passes for materials that render into the G-buffer
-        if (lightPrepass_ && tech->HasPass(PASS_GBUFFER))
+        if ((renderMode_ == RENDER_PREPASS && tech->HasPass(PASS_PREPASS)) || (renderMode_ == RENDER_DEFERRED &&
+            tech->HasPass(PASS_DEFERRED)))
             continue;
         
         Pass* pass = 0;
@@ -1072,7 +1086,7 @@ void View::RenderBatchesForward()
         graphics_->ResolveToTexture(screenBuffers_[0], viewRect_);
 }
 
-void View::RenderBatchesLightPrepass()
+void View::RenderBatchesDeferred()
 {
     // If not reusing shadowmaps, render all of them first
     if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !lightQueues_.Empty())
@@ -1087,26 +1101,34 @@ void View::RenderBatchesLightPrepass()
     }
     
     bool hwDepth = graphics_->GetHardwareDepthSupport();
+    // In light prepass mode the albedo buffer is used for light accumulation instead
+    Texture2D* albedoBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, Graphics::GetRGBAFormat());
     Texture2D* normalBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, Graphics::GetRGBAFormat());
-    Texture2D* lightBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, Graphics::GetRGBAFormat());
     Texture2D* depthBuffer = renderer_->GetScreenBuffer(rtSize_.x_, rtSize_.y_, hwDepth ? Graphics::GetDepthStencilFormat() :
         Graphics::GetLinearDepthFormat());
     
     RenderSurface* renderTarget = screenBuffers_.Size() ? screenBuffers_[0]->GetRenderSurface() : renderTarget_;
     RenderSurface* depthStencil;
     
+    if (renderMode_ == RENDER_PREPASS)
+        graphics_->SetRenderTarget(0, normalBuffer);
+    else
+    {
+        graphics_->SetRenderTarget(0, renderTarget);
+        graphics_->SetRenderTarget(1, albedoBuffer);
+        graphics_->SetRenderTarget(2, normalBuffer);
+    }
+    
     // Hardware depth support: render to RGBA normal buffer and read hardware depth
     if (hwDepth)
     {
         depthStencil = depthBuffer->GetRenderSurface();
-        graphics_->SetRenderTarget(0, normalBuffer);
     }
     // No hardware depth support: render to RGBA normal buffer and R32F depth
     else
     {
         depthStencil = renderer_->GetDepthStencil(rtSize_.x_, rtSize_.y_);
-        graphics_->SetRenderTarget(0, normalBuffer);
-        graphics_->SetRenderTarget(1, depthBuffer);
+        graphics_->SetRenderTarget(renderMode_ == RENDER_PREPASS ? 1 : 3, depthBuffer);
     }
     
     graphics_->SetDepthStencil(depthStencil);
@@ -1121,16 +1143,41 @@ void View::RenderBatchesLightPrepass()
         RenderBatchQueue(gbufferQueue_);
     }
     
-    // Clear the light accumulation buffer. However, skip the clear if the first light is a directional light with full mask
-    bool optimizeLightBuffer = !hasZeroLightMask_ && !lightQueues_.Empty() && lightQueues_.Front().light_->GetLightType() ==
-        LIGHT_DIRECTIONAL && (lightQueues_.Front().light_->GetLightMask() & 0xff) == 0xff;
-    
-    graphics_->ResetRenderTarget(1);
-    graphics_->SetRenderTarget(0, lightBuffer);
-    graphics_->SetDepthStencil(depthStencil);
-    graphics_->SetViewport(viewRect_);
-    if (!optimizeLightBuffer)
-        graphics_->Clear(CLEAR_COLOR);
+    RenderSurface* lightRenderTarget = renderMode_ == RENDER_PREPASS ? albedoBuffer->GetRenderSurface() : renderTarget;
+    if (renderMode_ == RENDER_PREPASS)
+    {
+        // Clear the light accumulation buffer. However, skip the clear if the first light is a directional light with full mask
+        bool optimizeLightBuffer = !hasZeroLightMask_ && !lightQueues_.Empty() && lightQueues_.Front().light_->GetLightType() ==
+            LIGHT_DIRECTIONAL && (lightQueues_.Front().light_->GetLightMask() & 0xff) == 0xff;
+        
+        graphics_->SetRenderTarget(0, lightRenderTarget);
+        graphics_->ResetRenderTarget(1);
+        graphics_->SetDepthStencil(depthStencil);
+        graphics_->SetViewport(viewRect_);
+        if (!optimizeLightBuffer)
+            graphics_->Clear(CLEAR_COLOR);
+    }
+    else
+    {
+        // Clear destination rendertarget with fog color
+        graphics_->SetAlphaTest(false);
+        graphics_->SetBlendMode(BLEND_REPLACE);
+        graphics_->SetColorWrite(true);
+        graphics_->SetDepthTest(CMP_ALWAYS);
+        graphics_->SetDepthWrite(false);
+        graphics_->SetScissorTest(false);
+        graphics_->SetStencilTest(true, CMP_EQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0);
+        graphics_->SetRenderTarget(0, renderTarget);
+        graphics_->ResetRenderTarget(1);
+        graphics_->ResetRenderTarget(2);
+        graphics_->ResetRenderTarget(3);
+        graphics_->SetDepthStencil(depthStencil);
+        graphics_->SetViewport(viewRect_);
+        graphics_->SetShaders(renderer_->GetVertexShader("Basic"), renderer_->GetPixelShader("Basic"));
+        graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, farClipZone_->GetFogColor());
+        graphics_->ClearParameterSource(PSP_MATDIFFCOLOR);
+        DrawFullscreenQuad(camera_, false);
+    }
     
     if (!lightQueues_.Empty())
     {
@@ -1143,13 +1190,15 @@ void View::RenderBatchesLightPrepass()
             if (renderer_->GetReuseShadowMaps() && i->shadowMap_)
             {
                 RenderShadowMap(*i);
-                graphics_->SetRenderTarget(0, lightBuffer);
+                graphics_->SetRenderTarget(0, lightRenderTarget);
                 graphics_->SetDepthStencil(depthStencil);
                 graphics_->SetViewport(viewRect_);
             }
             
-            graphics_->SetTexture(TU_DEPTHBUFFER, depthBuffer);
+            if (renderMode_ == RENDER_DEFERRED)
+                graphics_->SetTexture(TU_ALBEDOBUFFER, albedoBuffer);
             graphics_->SetTexture(TU_NORMALBUFFER, normalBuffer);
+            graphics_->SetTexture(TU_DEPTHBUFFER, depthBuffer);
             
             for (unsigned j = 0; j < i->volumeBatches_.Size(); ++j)
             {
@@ -1159,31 +1208,36 @@ void View::RenderBatchesLightPrepass()
         }
     }
     
-    graphics_->SetTexture(TU_DEPTHBUFFER, 0);
+    graphics_->SetTexture(TU_ALBEDOBUFFER, 0);
     graphics_->SetTexture(TU_NORMALBUFFER, 0);
+    graphics_->SetTexture(TU_DEPTHBUFFER, 0);
     
-    // Clear destination rendertarget with fog color
-    graphics_->SetAlphaTest(false);
-    graphics_->SetBlendMode(BLEND_REPLACE);
-    graphics_->SetColorWrite(true);
-    graphics_->SetDepthTest(CMP_ALWAYS);
-    graphics_->SetDepthWrite(false);
-    graphics_->SetScissorTest(false);
-    graphics_->SetStencilTest(true, CMP_EQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0);
-    graphics_->SetRenderTarget(0, renderTarget);
-    graphics_->SetDepthStencil(depthStencil);
-    graphics_->SetViewport(viewRect_);
-    graphics_->SetShaders(renderer_->GetVertexShader("Basic"), renderer_->GetPixelShader("Basic"));
-    graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, farClipZone_->GetFogColor());
-    graphics_->ClearParameterSource(PSP_MATDIFFCOLOR);
-    DrawFullscreenQuad(camera_, false);
+    if (renderMode_ == RENDER_PREPASS)
+    {
+        // Clear destination rendertarget with fog color
+        graphics_->SetAlphaTest(false);
+        graphics_->SetBlendMode(BLEND_REPLACE);
+        graphics_->SetColorWrite(true);
+        graphics_->SetDepthTest(CMP_ALWAYS);
+        graphics_->SetDepthWrite(false);
+        graphics_->SetScissorTest(false);
+        graphics_->SetStencilTest(true, CMP_EQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0);
+        graphics_->SetRenderTarget(0, renderTarget);
+        graphics_->SetDepthStencil(depthStencil);
+        graphics_->SetViewport(viewRect_);
+        graphics_->SetShaders(renderer_->GetVertexShader("Basic"), renderer_->GetPixelShader("Basic"));
+        graphics_->SetShaderParameter(PSP_MATDIFFCOLOR, farClipZone_->GetFogColor());
+        graphics_->ClearParameterSource(PSP_MATDIFFCOLOR);
+        DrawFullscreenQuad(camera_, false);
+    }
     
     if (!baseQueue_.IsEmpty())
     {
         // Render opaque objects with deferred lighting result
         PROFILE(RenderBase);
         
-        graphics_->SetTexture(TU_LIGHTBUFFER, lightBuffer);
+        if (renderMode_ == RENDER_PREPASS)
+            graphics_->SetTexture(TU_LIGHTBUFFER, albedoBuffer);
         
         RenderBatchQueue(baseQueue_);
         
@@ -1219,8 +1273,8 @@ void View::AllocateScreenBuffers()
 {
     unsigned neededBuffers = 0;
     #ifdef USE_OPENGL
-    // Due to FBO limitations, in OpenGL light pre-pass mode need to render to texture first and then blit to the backbuffer
-    if (lightPrepass_ && !renderTarget_)
+    // Due to FBO limitations, in OpenGL deferred modes need to render to texture first and then blit to the backbuffer
+    if (renderMode_ != RENDER_FORWARD && !renderTarget_)
         neededBuffers = 1;
     #endif
     
@@ -1724,9 +1778,9 @@ IntRect View::GetShadowMapViewport(Light* light, unsigned splitIndex, Texture2D*
     unsigned width = shadowMap->GetWidth();
     unsigned height = shadowMap->GetHeight();
     int maxCascades = renderer_->GetMaxShadowCascades();
-    // Due to instruction count limits, light prepass in SM2.0 can only support up to 3 cascades
+    // Due to instruction count limits, deferred modes in SM2.0 can only support up to 3 cascades
     #ifndef USE_OPENGL
-    if (lightPrepass_ && !graphics_->GetSM3Support())
+    if (renderMode_ != RENDER_FORWARD && !graphics_->GetSM3Support())
         maxCascades = Max(maxCascades, 3);
     #endif
     
@@ -2269,7 +2323,7 @@ void View::PrepareInstancingBuffer()
     
     totalInstances += baseQueue_.GetNumInstances(renderer_);
     totalInstances += preAlphaQueue_.GetNumInstances(renderer_);
-    if (lightPrepass_)
+    if (renderMode_ != RENDER_FORWARD)
         totalInstances += gbufferQueue_.GetNumInstances(renderer_);
     
     for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
@@ -2289,7 +2343,7 @@ void View::PrepareInstancingBuffer()
         {
             baseQueue_.SetTransforms(renderer_, lockedData, freeIndex);
             preAlphaQueue_.SetTransforms(renderer_, lockedData, freeIndex);
-            if (lightPrepass_)
+            if (renderMode_ != RENDER_FORWARD)
                 gbufferQueue_.SetTransforms(renderer_, lockedData, freeIndex);
             
             for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
@@ -2310,9 +2364,9 @@ void View::SetupLightVolumeBatch(Batch& batch)
     LightType type = light->GetLightType();
     float lightDist;
     
-    // Use replace blend mode for the first light volume, and additive for the rest
+    // Use replace blend mode for the first pre-pass light volume, and additive for the rest
     graphics_->SetAlphaTest(false);
-    graphics_->SetBlendMode(light == lightQueues_.Front().light_ ? BLEND_REPLACE : BLEND_ADD);
+    graphics_->SetBlendMode(renderMode_ == RENDER_PREPASS && light == lightQueues_.Front().light_ ? BLEND_REPLACE : BLEND_ADD);
     graphics_->SetDepthWrite(false);
     
     if (type != LIGHT_DIRECTIONAL)

+ 7 - 7
Engine/Graphics/View.h

@@ -123,11 +123,11 @@ private:
     void GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue);
     /// Render batches using forward rendering.
     void RenderBatchesForward();
-    /// Render batches using light pre-pass rendering.
-    void RenderBatchesLightPrepass();
+    /// Render batches using light pre-pass or deferred rendering.
+    void RenderBatchesDeferred();
     /// Allocate needed screen buffers for post-processing and/or framebuffer blitting.
     void AllocateScreenBuffers();
-    /// Blit the framebuffer to destination. Used in OpenGL light pre-pass mode.
+    /// Blit the framebuffer to destination. Used in OpenGL deferred rendering modes.
     void BlitFramebuffer();
     /// Run post-processing effects.
     void RunPostProcesses();
@@ -208,7 +208,7 @@ private:
     RenderSurface* renderTarget_;
     /// Post-processing effects.
     Vector<SharedPtr<PostProcess> > postProcesses_;
-    /// Intermediate screen buffers used in postprocessing and OpenGL light pre-pass framebuffer blit.
+    /// Intermediate screen buffers used in postprocessing and OpenGL deferred framebuffer blit.
     PODVector<Texture2D*> screenBuffers_;
     /// Viewport rectangle.
     IntRect viewRect_;
@@ -254,7 +254,7 @@ private:
     BatchQueue baseQueue_;
     /// Pre-transparent pass batches.
     BatchQueue preAlphaQueue_;
-    /// Light pre-pass G-buffer batches.
+    /// Deferred rendering G-buffer batches.
     BatchQueue gbufferQueue_;
     /// Transparent geometry batches.
     BatchQueue alphaQueue_;
@@ -274,8 +274,8 @@ private:
     int highestZonePriority_;
     /// Current stencil value for light optimization.
     unsigned char lightStencilValue_;
-    /// Light prepass flag.
-    bool lightPrepass_;
+    /// Rendering mode.
+    RenderMode renderMode_;
     /// Camera zone's override flag.
     bool cameraZoneOverride_;
     /// Draw shadows flag.

+ 4 - 2
SourceAssets/GLSLShaders/CMakeLists.txt

@@ -4,13 +4,15 @@ add_shader (Ambient)
 add_shader (Basic)
 add_shader (Bloom)
 add_shader (CopyFramebuffer)
+add_shader (Deferred)
+add_shader (DeferredLight)
 add_shader (EdgeFilter)
 add_shader (ForwardLit)
-add_shader (GBuffer)
 add_shader (GreyScale)
-add_shader (LightVolume)
 add_shader (LitParticle)
 add_shader (Material)
+add_shader (Prepass)
+add_shader (PrepassLight)
 add_shader (Shadow)
 add_shader (Stencil)
 add_shader (Unlit)

+ 51 - 0
SourceAssets/GLSLShaders/Deferred.frag

@@ -0,0 +1,51 @@
+#include "Uniforms.frag"
+#include "Samplers.frag"
+#include "Fog.frag"
+
+varying vec2 vTexCoord;
+varying vec4 vVertexLighting;
+varying vec3 vNormal;
+#ifdef NORMALMAP
+    varying vec3 vTangent;
+    varying vec3 vBitangent;
+#endif
+
+void main()
+{
+    #ifdef DIFFMAP
+        vec4 diffInput = texture2D(sDiffMap, vTexCoord);
+        #ifdef ALPHAMASK
+            if (diffInput.a < 0.5)
+                discard;
+        #endif
+        vec3 diffColor = cMatDiffColor.rgb * diffInput.rgb;
+    #else
+        vec3 diffColor = cMatDiffColor.rgb;
+    #endif
+
+    #ifdef VERTEXCOLOR
+        diffColor *= vColor.rgb;
+    #endif
+
+    #ifdef NORMALMAP
+        mat3 tbn = mat3(vTangent, vBitangent, vNormal);
+        vec3 normal = tbn * DecodeNormal(texture2D(sNormalMap, vTexCoord));
+    #else
+        vec3 normal = vNormal;
+    #endif
+
+    #ifdef SPECMAP
+        float specIntensity = cMatSpecColor.g * texture2D(sSpecMap, vTexCoord).g;
+    #else
+        float specIntensity = cMatSpecColor.g;
+    #endif
+
+    float specPower = cMatSpecColor.a / 255.0;
+
+    gl_FragData[0] = vec4(GetFog(vVertexLighting.rgb * diffColor, vVertexLighting.a), 1.0);
+    gl_FragData[1] = GetFogFactor(vVertexLighting.a) * vec4(diffColor, specIntensity);
+    gl_FragData[2] = vec4(normal * 0.5 + 0.5, specPower);
+    #ifndef HWDEPTH
+        gl_FragData[3] = vVertexLighting.a;
+    #endif
+}

+ 39 - 0
SourceAssets/GLSLShaders/Deferred.vert

@@ -0,0 +1,39 @@
+#include "Uniforms.vert"
+#include "Transform.vert"
+#include "Lighting.vert"
+
+varying vec2 vTexCoord;
+#ifdef VERTEXCOLOR
+    varying vec4 vColor;
+#endif
+varying vec4 vVertexLighting;
+varying vec3 vNormal;
+#ifdef NORMALMAP
+    varying vec3 vTangent;
+    varying vec3 vBitangent;
+#endif
+
+void main()
+{
+    mat4 modelMatrix = iModelMatrix;
+    vec3 worldPos = GetWorldPos(modelMatrix);
+    gl_Position = GetClipPos(worldPos);
+    vTexCoord = GetTexCoord(iTexCoord);
+
+    vNormal = GetWorldNormal(modelMatrix);
+    #ifdef NORMALMAP
+        vTangent = GetWorldTangent(modelMatrix);
+        vBitangent = cross(vTangent, vNormal) * iTangent.w;
+    #endif
+    
+    vVertexLighting = vec4(GetAmbient(GetZonePos(worldPos)), GetDepth(gl_Position));
+    #ifdef NUMVERTEXLIGHTS
+    vec3 normal = GetWorldNormal(modelMatrix);
+    for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
+        vVertexLighting.rgb += GetVertexLight(i, worldPos, normal) * cVertexLights[i * 3].rgb;
+    #endif
+    
+    #ifdef VERTEXCOLOR
+        vColor = iColor;
+    #endif
+}

+ 23 - 0
SourceAssets/GLSLShaders/Deferred.xml

@@ -0,0 +1,23 @@
+<shaders>
+    <shader name="Deferred" type="vs">
+        <option name="Normal" define="NORMALMAP" />
+        <option name="HW" define="HWDEPTH" />
+        <variation name="" />
+        <variation name="1VL" define="NUMVERTEXLIGHTS=1" />
+        <variation name="2VL" define="NUMVERTEXLIGHTS=2" />
+        <variation name="3VL" define="NUMVERTEXLIGHTS=3" />
+        <variation name="4VL" define="NUMVERTEXLIGHTS=4" />
+        <variation name="5VL" define="NUMVERTEXLIGHTS=5" />
+        <variation name="6VL" define="NUMVERTEXLIGHTS=6" />
+        <option name="" /> <!-- Dummy option to separate the two variation groups -->
+        <variation name="" />
+        <variation name="Skinned" define="SKINNED" />
+    </shader>
+    <shader name="Deferred" type="ps">
+        <option name="Diff" define="DIFFMAP" />
+        <option name="Normal" define="NORMALMAP" require="DIFFMAP" />
+        <option name="SpecMap" define="SPECMAP" require="DIFFMAP" />
+        <option name="Mask" define="ALPHAMASK" require="DIFFMAP" />
+        <option name="HW" define="HWDEPTH" />
+    </shader>
+</shaders>

+ 93 - 0
SourceAssets/GLSLShaders/DeferredLight.frag

@@ -0,0 +1,93 @@
+#include "Uniforms.frag"
+#include "Samplers.frag"
+#include "Lighting.frag"
+
+#ifdef DIRLIGHT
+    varying vec2 vScreenPos;
+#else
+    varying vec4 vScreenPos;
+#endif
+varying vec3 vFarRay;
+#ifdef ORTHO
+    varying vec3 vNearRay;
+#endif
+
+void main()
+{
+    // If rendering a directional light quad, optimize out the w divide
+    #ifdef DIRLIGHT
+        #if defined(HWDEPTH) && defined(ORTHO)
+            float depth = texture2D(sDepthBuffer, vScreenPos).r;
+        #elif defined(HWDEPTH) && !defined(ORTHO)
+            float depth = ReconstructDepth(texture2D(sDepthBuffer, vScreenPos).r);
+        #else
+            float depth = DecodeDepth(texture2D(sDepthBuffer, vScreenPos).rgb);
+        #endif
+        #ifdef ORTHO
+            vec3 worldPos = mix(vNearRay, vFarRay, depth);
+        #else
+            vec3 worldPos = vFarRay * depth;
+        #endif
+        vec4 albedoInput = texture2D(sAlbedoBuffer, vScreenPos);
+        vec4 normalInput = texture2D(sNormalBuffer, vScreenPos);
+    #else
+        #if defined(HWDEPTH) && defined(ORTHO)
+            float depth = texture2DProj(sDepthBuffer, vScreenPos).r;
+        #elif defined(HWDEPTH) && !defined(ORTHO)
+            float depth = ReconstructDepth(texture2DProj(sDepthBuffer, vScreenPos).r);
+        #else
+            float depth = DecodeDepth(texture2DProj(sDepthBuffer, vScreenPos).rgb);
+        #endif
+        #ifdef ORTHO
+            vec3 worldPos = mix(vNearRay, vFarRay, depth) / vScreenPos.w;
+        #else
+            vec3 worldPos = vFarRay * depth / vScreenPos.w;
+        #endif
+        vec4 albedoInput = texture2DProj(sAlbedoBuffer, vScreenPos);
+        vec4 normalInput = texture2DProj(sNormalBuffer, vScreenPos);
+    #endif
+
+    vec3 normal = normalize(normalInput.rgb * 2.0 - 1.0);
+    vec4 projWorldPos = vec4(worldPos, 1.0);
+    vec3 lightColor;
+    vec3 lightDir;
+    float diff;
+
+    #ifdef DIRLIGHT
+        lightDir = cLightDirPS;
+        diff = GetDiffuseDir(normal, lightDir);
+    #else
+        vec3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
+        diff = GetDiffusePointOrSpot(normal, lightVec, lightDir);
+    #endif
+
+    #ifdef SHADOW
+        #if defined(DIRLIGHT)
+            vec4 shadowPos = GetDirShadowPosDeferred(cLightMatricesPS, projWorldPos, depth);
+            diff *= min(GetShadow(shadowPos) + GetShadowFade(depth), 1.0);
+        #elif defined(SPOTLIGHT)
+            vec4 shadowPos = cLightMatricesPS[1] * projWorldPos;
+            diff *= GetShadow(shadowPos);
+        #else
+            vec3 shadowPos = worldPos - cLightPosPS.xyz;
+            diff *= GetCubeShadow(shadowPos);
+        #endif
+    #endif
+
+    #if defined(SPOTLIGHT)
+        vec4 spotPos = cLightMatricesPS[0] * projWorldPos;
+        lightColor = spotPos.w > 0.0 ? texture2DProj(sLightSpotMap, spotPos).rgb * cLightColor.rgb : vec3(0.0);
+    #elif defined(CUBEMASK)
+        mat3 lightVecRot = mat3(cLightMatricesPS[0][0].xyz, cLightMatricesPS[0][1].xyz, cLightMatricesPS[0][2].xyz);
+        lightColor = textureCube(sLightCubeMap, lightVecRot * lightVec).rgb * cLightColor.rgb;
+    #else
+        lightColor = cLightColor.rgb;
+    #endif
+
+    #ifdef SPECULAR
+        float spec = lightColor.g * GetSpecular(normal, -worldPos, lightDir, normalInput.a * 255.0);
+        gl_FragColor = diff * vec4(lightColor * albedoInput.rgb + spec * cLightColor.a * albedoInput.aaa, 0.0);
+    #else
+        gl_FragColor = diff * vec4(lightColor * albedoInput.rgb, 0.0);
+    #endif
+}

+ 33 - 0
SourceAssets/GLSLShaders/DeferredLight.vert

@@ -0,0 +1,33 @@
+#include "Uniforms.vert"
+#include "Transform.vert"
+#include "ScreenPos.vert"
+
+#ifdef DIRLIGHT
+    varying vec2 vScreenPos;
+#else
+    varying vec4 vScreenPos;
+#endif
+varying vec3 vFarRay;
+#ifdef ORTHO
+    varying vec3 vNearRay;
+#endif
+
+void main()
+{
+    mat4 modelMatrix = iModelMatrix;
+    vec3 worldPos = GetWorldPos(modelMatrix);
+    gl_Position = GetClipPos(worldPos);
+    #ifdef DIRLIGHT
+        vScreenPos = GetScreenPosPreDiv(gl_Position);
+        vFarRay = GetFarRay(gl_Position);
+        #ifdef ORTHO
+            vNearRay = GetNearRay(gl_Position);
+        #endif
+    #else
+        vScreenPos = GetScreenPos(gl_Position);
+        vFarRay = GetFarRay(gl_Position) * gl_Position.w;
+        #ifdef ORTHO
+            vNearRay = GetNearRay(gl_Position) * gl_Position.w;
+        #endif
+    #endif
+}

+ 17 - 0
SourceAssets/GLSLShaders/DeferredLight.xml

@@ -0,0 +1,17 @@
+<shaders>
+    <shader name="DeferredLight" type="vs">
+        <option name="Ortho" define="ORTHO" />
+        <option name="Dir" define="DIRLIGHT" />
+    </shader>
+    <shader name="DeferredLight" type="ps">
+        <option name="Ortho" define="ORTHO" />
+        <variation name="Dir" define="DIRLIGHT" />
+        <variation name="Spot" define="SPOTLIGHT" />
+        <variation name="Point" define="POINTLIGHT" />
+        <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
+        <option name="Spec" define="SPECULAR" />
+        <option name="Shadow" define="SHADOW" />
+        <option name="LQ" define="LQSHADOW" />
+        <option name="HW" define="HWDEPTH" />
+    </shader>
+</shaders>

+ 5 - 0
SourceAssets/GLSLShaders/Fog.frag

@@ -7,3 +7,8 @@ vec3 GetLitFog(vec3 color, float depth)
 {
     return color * clamp((cFogParams.x - depth) * cFogParams.y, 0.0, 1.0);
 }
+
+float GetFogFactor(float depth)
+{
+    return clamp((cFogParams.x - depth) * cFogParams.y, 0.0, 1.0);
+}

+ 4 - 0
SourceAssets/GLSLShaders/Material.frag

@@ -23,6 +23,10 @@ void main()
         vec3 diffColor = cMatDiffColor.rgb;
     #endif
 
+    #ifdef VERTEXCOLOR
+        diffColor *= vColor.rgb;
+    #endif
+    
     #ifdef SPECMAP
         vec3 specColor = cMatSpecColor.rgb * texture2D(sSpecMap, vTexCoord).g;
     #else

+ 2 - 0
SourceAssets/GLSLShaders/Material.xml

@@ -1,5 +1,6 @@
 <shaders>
     <shader name="Material" type="vs">
+        <option name="VCol" define="VERTEXCOLOR" />
         <variation name="" />
         <variation name="1VL" define="NUMVERTEXLIGHTS=1" />
         <variation name="2VL" define="NUMVERTEXLIGHTS=2" />
@@ -15,5 +16,6 @@
         <option name="Diff" define="DIFFMAP" />
         <option name="SpecMap" define="SPECMAP" />
         <option name="Mask" define="ALPHAMASK" include="Diff" />
+        <option name="VCol" define="VERTEXCOLOR" /> 
     </shader>
 </shaders>

+ 0 - 0
SourceAssets/GLSLShaders/GBuffer.frag → SourceAssets/GLSLShaders/Prepass.frag


+ 0 - 0
SourceAssets/GLSLShaders/GBuffer.vert → SourceAssets/GLSLShaders/Prepass.vert


+ 2 - 2
SourceAssets/GLSLShaders/GBuffer.xml → SourceAssets/GLSLShaders/Prepass.xml

@@ -1,11 +1,11 @@
 <shaders>
-    <shader name="GBuffer" type="vs">
+    <shader name="Prepass" type="vs">
         <option name="Normal" define="NORMALMAP" />
         <option name="HW" define="HWDEPTH" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
     </shader>
-    <shader name="GBuffer" type="ps">
+    <shader name="Prepass" type="ps">
         <option name="Normal" define="NORMALMAP" />
         <option name="Mask" define="ALPHAMASK" />
         <option name="HW" define="HWDEPTH" />

+ 92 - 0
SourceAssets/GLSLShaders/PrepassLight.frag

@@ -0,0 +1,92 @@
+#include "Uniforms.frag"
+#include "Samplers.frag"
+#include "Lighting.frag"
+
+#ifdef DIRLIGHT
+    varying vec2 vScreenPos;
+#else
+    varying vec4 vScreenPos;
+#endif
+varying vec3 vFarRay;
+#ifdef ORTHO
+    varying vec3 vNearRay;
+#endif
+
+void main()
+{
+    // If rendering a directional light quad, optimize out the w divide
+    #ifdef DIRLIGHT
+        #if defined(HWDEPTH) && defined(ORTHO)
+            float depth = texture2D(sDepthBuffer, vScreenPos).r;
+        #elif defined(HWDEPTH) && !defined(ORTHO)
+            float depth = ReconstructDepth(texture2D(sDepthBuffer, vScreenPos).r);
+        #else
+            float depth = DecodeDepth(texture2D(sDepthBuffer, vScreenPos).rgb);
+        #endif
+        #ifdef ORTHO
+            vec3 worldPos = mix(vNearRay, vFarRay, depth);
+        #else
+            vec3 worldPos = vFarRay * depth;
+        #endif
+        vec4 normalInput = texture2D(sNormalBuffer, vScreenPos);
+    #else
+        #if defined(HWDEPTH) && defined(ORTHO)
+            float depth = texture2DProj(sDepthBuffer, vScreenPos).r;
+        #elif defined(HWDEPTH) && !defined(ORTHO)
+            float depth = ReconstructDepth(texture2DProj(sDepthBuffer, vScreenPos).r);
+        #else
+            float depth = DecodeDepth(texture2DProj(sDepthBuffer, vScreenPos).rgb);
+        #endif
+        #ifdef ORTHO
+            vec3 worldPos = mix(vNearRay, vFarRay, depth) / vScreenPos.w;
+        #else
+            vec3 worldPos = vFarRay * depth / vScreenPos.w;
+        #endif
+        vec4 normalInput = texture2DProj(sNormalBuffer, vScreenPos);
+    #endif
+
+    vec3 normal = normalize(normalInput.rgb * 2.0 - 1.0);
+    vec4 projWorldPos = vec4(worldPos, 1.0);
+    vec3 lightColor;
+    vec3 lightDir;
+    float diff;
+
+    // Accumulate light at half intensity to allow 2x "overburn"
+    #ifdef DIRLIGHT
+        lightDir = cLightDirPS;
+        diff = 0.5 * GetDiffuseDir(normal, lightDir);
+    #else
+        vec3 lightVec = (cLightPosPS.xyz - worldPos) * cLightPosPS.w;
+        diff = 0.5 * GetDiffusePointOrSpot(normal, lightVec, lightDir);
+    #endif
+
+    #ifdef SHADOW
+        #if defined(DIRLIGHT)
+            vec4 shadowPos = GetDirShadowPosDeferred(cLightMatricesPS, projWorldPos, depth);
+            diff *= min(GetShadow(shadowPos) + GetShadowFade(depth), 1.0);
+        #elif defined(SPOTLIGHT)
+            vec4 shadowPos = cLightMatricesPS[1] * projWorldPos;
+            diff *= GetShadow(shadowPos);
+        #else
+            vec3 shadowPos = worldPos - cLightPosPS.xyz;
+            diff *= GetCubeShadow(shadowPos);
+        #endif
+    #endif
+
+    #if defined(SPOTLIGHT)
+        vec4 spotPos = cLightMatricesPS[0] * projWorldPos;
+        lightColor = spotPos.w > 0.0 ? texture2DProj(sLightSpotMap, spotPos).rgb * cLightColor.rgb : vec3(0.0);
+    #elif defined(CUBEMASK)
+        mat3 lightVecRot = mat3(cLightMatricesPS[0][0].xyz, cLightMatricesPS[0][1].xyz, cLightMatricesPS[0][2].xyz);
+        lightColor = textureCube(sLightCubeMap, lightVecRot * lightVec).rgb * cLightColor.rgb;
+    #else
+        lightColor = cLightColor.rgb;
+    #endif
+
+    #ifdef SPECULAR
+        float spec = lightColor.g * GetSpecular(normal, -worldPos, lightDir, normalInput.a * 255.0);
+        gl_FragColor = diff * vec4(lightColor, spec * cLightColor.a);
+    #else
+        gl_FragColor = diff * vec4(lightColor, 0.0);
+    #endif
+}

+ 33 - 0
SourceAssets/GLSLShaders/PrepassLight.vert

@@ -0,0 +1,33 @@
+#include "Uniforms.vert"
+#include "Transform.vert"
+#include "ScreenPos.vert"
+
+#ifdef DIRLIGHT
+    varying vec2 vScreenPos;
+#else
+    varying vec4 vScreenPos;
+#endif
+varying vec3 vFarRay;
+#ifdef ORTHO
+    varying vec3 vNearRay;
+#endif
+
+void main()
+{
+    mat4 modelMatrix = iModelMatrix;
+    vec3 worldPos = GetWorldPos(modelMatrix);
+    gl_Position = GetClipPos(worldPos);
+    #ifdef DIRLIGHT
+        vScreenPos = GetScreenPosPreDiv(gl_Position);
+        vFarRay = GetFarRay(gl_Position);
+        #ifdef ORTHO
+            vNearRay = GetNearRay(gl_Position);
+        #endif
+    #else
+        vScreenPos = GetScreenPos(gl_Position);
+        vFarRay = GetFarRay(gl_Position) * gl_Position.w;
+        #ifdef ORTHO
+            vNearRay = GetNearRay(gl_Position) * gl_Position.w;
+        #endif
+    #endif
+}

+ 17 - 0
SourceAssets/GLSLShaders/PrepassLight.xml

@@ -0,0 +1,17 @@
+<shaders>
+    <shader name="PrepassLight" type="vs">
+        <option name="Ortho" define="ORTHO" />
+        <option name="Dir" define="DIRLIGHT" />
+    </shader>
+    <shader name="PrepassLight" type="ps">
+        <option name="Ortho" define="ORTHO" />
+        <variation name="Dir" define="DIRLIGHT" />
+        <variation name="Spot" define="SPOTLIGHT" />
+        <variation name="Point" define="POINTLIGHT" />
+        <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
+        <option name="Spec" define="SPECULAR" />
+        <option name="Shadow" define="SHADOW" />
+        <option name="LQ" define="LQSHADOW" />
+        <option name="HW" define="HWDEPTH" />
+    </shader>
+</shaders>

+ 1 - 0
SourceAssets/GLSLShaders/Samplers.frag

@@ -12,6 +12,7 @@ uniform sampler2D sLightSpotMap;
 uniform samplerCube sLightCubeMap;
 uniform samplerCube sFaceSelectCubeMap;
 uniform samplerCube sIndirectionCubeMap;
+uniform sampler2D sAlbedoBuffer;
 uniform sampler2D sNormalBuffer;
 uniform sampler2D sDepthBuffer;
 uniform sampler2D sLightBuffer;

+ 4 - 2
SourceAssets/HLSLShaders/CMakeLists.txt

@@ -5,11 +5,13 @@ add_shader (Basic)
 add_shader (Bloom)
 add_shader (EdgeFilter)
 add_shader (ForwardLit)
-add_shader (GBuffer)
+add_shader (Deferred)
+add_shader (DeferredLight)
 add_shader (GreyScale)
 add_shader (LitParticle)
-add_shader (LightVolume)
 add_shader (Material)
+add_shader (Prepass)
+add_shader (PrepassLight)
 add_shader (Shadow)
 add_shader (Stencil)
 add_shader (Unlit)

+ 5 - 0
SourceAssets/HLSLShaders/Fog.hlsl

@@ -7,3 +7,8 @@ float3 GetLitFog(float3 color, float depth)
 {
     return color * saturate((cFogParams.x - depth) * cFogParams.y);
 }
+
+float GetFogFactor(float depth)
+{
+    return saturate((cFogParams.x - depth) * cFogParams.y);
+}

+ 7 - 0
SourceAssets/HLSLShaders/Material.hlsl

@@ -11,6 +11,9 @@ void VS(float4 iPos : POSITION,
     #ifdef NUMVERTEXLIGHTS
         float3 iNormal : NORMAL,
     #endif
+    #ifdef VERTEXCOLOR
+        float4 iColor : COLOR0,
+    #endif
     #ifdef SKINNED
         float4 iBlendWeights : BLENDWEIGHT,
         int4 iBlendIndices : BLENDINDICES,
@@ -64,6 +67,10 @@ void PS(float2 iTexCoord : TEXCOORD0,
         float3 diffColor = cMatDiffColor.rgb;
     #endif
 
+    #ifdef VERTEXCOLOR
+        diffColor *= iColor.rgb;
+    #endif
+
     #ifdef SPECMAP
         float3 specColor = cMatSpecColor.rgb * tex2D(sSpecMap, iTexCoord).g;
     #else

+ 2 - 0
SourceAssets/HLSLShaders/Material.xml

@@ -1,5 +1,6 @@
 <shaders>
     <shader name="Material" type="vs">
+        <option name="VCol" define="VERTEXCOLOR" />
         <variation name="" />
         <variation name="1VL" define="NUMVERTEXLIGHTS=1" />
         <variation name="2VL" define="NUMVERTEXLIGHTS=2" />
@@ -16,5 +17,6 @@
         <option name="Diff" define="DIFFMAP" />
         <option name="SpecMap" define="SPECMAP" />
         <option name="Mask" define="ALPHAMASK" include="Diff" />
+        <option name="VCol" define="VERTEXCOLOR" />
     </shader>
 </shaders>

+ 2 - 1
SourceAssets/HLSLShaders/Samplers.hlsl

@@ -12,8 +12,9 @@ sampler2D sLightSpotMap : register(S8);
 samplerCUBE sLightCubeMap : register(S8);
 samplerCUBE sFaceSelectCubeMap : register(S9);
 samplerCUBE sIndirectionCubeMap : register(S10);
-sampler2D sDepthBuffer : register(S0);
+sampler2D sAlbedoBuffer : register(S0);
 sampler2D sNormalBuffer : register(S1);
+sampler2D sDepthBuffer : register(S2);
 sampler2D sLightBuffer : register(S7);
 
 float4 Sample(sampler2D map, float2 texCoord)