Browse Source

Initial light pre-pass rendering. Lighting not rendered yet.

Lasse Öörni 14 years ago
parent
commit
fefa7bf6cd

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

@@ -3,4 +3,6 @@
     <pass name="litbase" vs="BlinnPhong" ps="BlinnPhong_DiffAmbient" />
     <pass name="litbase" vs="BlinnPhong" ps="BlinnPhong_DiffAmbient" />
     <pass name="light" vs="BlinnPhong" ps="BlinnPhong_Diff" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="light" vs="BlinnPhong" ps="BlinnPhong_Diff" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="gbuffer" vs="GBuffer" ps="GBuffer" />
+    <pass name="material" vs="Material" ps="Material_Diff" depthtest="equal" depthwrite="false" />
 </technique>
 </technique>

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

@@ -3,4 +3,6 @@
     <pass name="litbase" vs="BlinnPhong_Normal" ps="BlinnPhong_DiffNormalAmbient" />
     <pass name="litbase" vs="BlinnPhong_Normal" ps="BlinnPhong_DiffNormalAmbient" />
     <pass name="light" vs="BlinnPhong_Normal" ps="BlinnPhong_DiffNormal" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="light" vs="BlinnPhong_Normal" ps="BlinnPhong_DiffNormal" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="gbuffer" vs="GBuffer_Normal" ps="GBuffer_Normal" />
+    <pass name="material" vs="Material" ps="Material_Diff" depthtest="equal" depthwrite="false" />
 </technique>
 </technique>

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

@@ -3,4 +3,6 @@
     <pass name="litbase" vs="BlinnPhong" ps="BlinnPhong_Ambient" />
     <pass name="litbase" vs="BlinnPhong" ps="BlinnPhong_Ambient" />
     <pass name="light" vs="BlinnPhong" ps="BlinnPhong" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="light" vs="BlinnPhong" ps="BlinnPhong" depthtest="equal" depthwrite="false" blend="add" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
     <pass name="shadow" vs="Shadow" ps="Shadow" />
+    <pass name="gbuffer" vs="GBuffer" ps="GBuffer" />
+    <pass name="material" vs="Material" ps="Material" depthtest="equal" depthwrite="false" />
 </technique>
 </technique>

+ 12 - 9
Bin/Data/Scripts/LightTest.as

@@ -182,6 +182,9 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
 
         if (input.keyPress['1'])
         if (input.keyPress['1'])
+            renderer.lightPrepass = !renderer.lightPrepass;
+
+        if (input.keyPress['2'])
         {
         {
             int quality = renderer.textureQuality;
             int quality = renderer.textureQuality;
             ++quality;
             ++quality;
@@ -190,7 +193,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
             renderer.textureQuality = quality;
         }
         }
 
 
-        if (input.keyPress['2'])
+        if (input.keyPress['3'])
         {
         {
             int quality = renderer.materialQuality;
             int quality = renderer.materialQuality;
             ++quality;
             ++quality;
@@ -199,13 +202,13 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
             renderer.materialQuality = quality;
         }
         }
 
 
-        if (input.keyPress['3'])
+        if (input.keyPress['4'])
             renderer.specularLighting = !renderer.specularLighting;
             renderer.specularLighting = !renderer.specularLighting;
 
 
-        if (input.keyPress['4'])
+        if (input.keyPress['5'])
             renderer.drawShadows = !renderer.drawShadows;
             renderer.drawShadows = !renderer.drawShadows;
 
 
-        if (input.keyPress['5'])
+        if (input.keyPress['6'])
         {
         {
             int size = renderer.shadowMapSize;
             int size = renderer.shadowMapSize;
             size *= 2;
             size *= 2;
@@ -214,20 +217,20 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
             renderer.shadowMapSize = size;
         }
         }
 
 
-        if (input.keyPress['6'])
+        if (input.keyPress['7'])
             renderer.shadowQuality = renderer.shadowQuality + 1;
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
 
-        if (input.keyPress['7'])
+        if (input.keyPress['8'])
         {
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
         }
-        
-        if (input.keyPress['8'])
-            renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
 
         if (input.keyPress['9'])
         if (input.keyPress['9'])
+            renderer.dynamicInstancing = !renderer.dynamicInstancing;
+
+        if (input.keyPress['0'])
             renderer.lightStencilMasking = !renderer.lightStencilMasking;
             renderer.lightStencilMasking = !renderer.lightStencilMasking;
 
 
         if (input.keyPress['C'])
         if (input.keyPress['C'])

+ 12 - 9
Bin/Data/Scripts/TestScene.as

@@ -256,6 +256,9 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
 
         if (input.keyPress['1'])
         if (input.keyPress['1'])
+            renderer.lightPrepass = !renderer.lightPrepass;
+        
+        if (input.keyPress['2'])
         {
         {
             int quality = renderer.textureQuality;
             int quality = renderer.textureQuality;
             ++quality;
             ++quality;
@@ -264,7 +267,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
             renderer.textureQuality = quality;
         }
         }
 
 
-        if (input.keyPress['2'])
+        if (input.keyPress['3'])
         {
         {
             int quality = renderer.materialQuality;
             int quality = renderer.materialQuality;
             ++quality;
             ++quality;
@@ -273,13 +276,13 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
             renderer.materialQuality = quality;
         }
         }
 
 
-        if (input.keyPress['3'])
+        if (input.keyPress['4'])
             renderer.specularLighting = !renderer.specularLighting;
             renderer.specularLighting = !renderer.specularLighting;
 
 
-        if (input.keyPress['4'])
+        if (input.keyPress['5'])
             renderer.drawShadows = !renderer.drawShadows;
             renderer.drawShadows = !renderer.drawShadows;
 
 
-        if (input.keyPress['5'])
+        if (input.keyPress['6'])
         {
         {
             int size = renderer.shadowMapSize;
             int size = renderer.shadowMapSize;
             size *= 2;
             size *= 2;
@@ -288,20 +291,20 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
             renderer.shadowMapSize = size;
         }
         }
 
 
-        if (input.keyPress['6'])
+        if (input.keyPress['7'])
             renderer.shadowQuality = renderer.shadowQuality + 1;
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
 
-        if (input.keyPress['7'])
+        if (input.keyPress['8'])
         {
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
         }
-        
-        if (input.keyPress['8'])
-            renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
 
         if (input.keyPress['9'])
         if (input.keyPress['9'])
+            renderer.dynamicInstancing = !renderer.dynamicInstancing;
+
+        if (input.keyPress['0'])
             renderer.lightStencilMasking = !renderer.lightStencilMasking;
             renderer.lightStencilMasking = !renderer.lightStencilMasking;
 
 
         if (input.keyPress[' '])
         if (input.keyPress[' '])

+ 11 - 8
Bin/Data/Scripts/TestSceneOld.as

@@ -339,6 +339,9 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
             cameraNode.TranslateRelative(Vector3(10, 0, 0) * timeStep * speedMultiplier);
 
 
         if (input.keyPress['1'])
         if (input.keyPress['1'])
+            renderer.lightPrepass = !renderer.lightPrepass;
+        
+        if (input.keyPress['2'])
         {
         {
             int quality = renderer.textureQuality;
             int quality = renderer.textureQuality;
             ++quality;
             ++quality;
@@ -347,7 +350,7 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.textureQuality = quality;
             renderer.textureQuality = quality;
         }
         }
 
 
-        if (input.keyPress['2'])
+        if (input.keyPress['3'])
         {
         {
             int quality = renderer.materialQuality;
             int quality = renderer.materialQuality;
             ++quality;
             ++quality;
@@ -356,13 +359,13 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.materialQuality = quality;
             renderer.materialQuality = quality;
         }
         }
 
 
-        if (input.keyPress['3'])
+        if (input.keyPress['4'])
             renderer.specularLighting = !renderer.specularLighting;
             renderer.specularLighting = !renderer.specularLighting;
 
 
-        if (input.keyPress['4'])
+        if (input.keyPress['5'])
             renderer.drawShadows = !renderer.drawShadows;
             renderer.drawShadows = !renderer.drawShadows;
 
 
-        if (input.keyPress['5'])
+        if (input.keyPress['6'])
         {
         {
             int size = renderer.shadowMapSize;
             int size = renderer.shadowMapSize;
             size *= 2;
             size *= 2;
@@ -371,20 +374,20 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
             renderer.shadowMapSize = size;
             renderer.shadowMapSize = size;
         }
         }
 
 
-        if (input.keyPress['6'])
+        if (input.keyPress['7'])
             renderer.shadowQuality = renderer.shadowQuality + 1;
             renderer.shadowQuality = renderer.shadowQuality + 1;
 
 
-        if (input.keyPress['7'])
+        if (input.keyPress['8'])
         {
         {
             bool occlusion = renderer.maxOccluderTriangles > 0;
             bool occlusion = renderer.maxOccluderTriangles > 0;
             occlusion = !occlusion;
             occlusion = !occlusion;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
             renderer.maxOccluderTriangles = occlusion ? 5000 : 0;
         }
         }
 
 
-        if (input.keyPress['8'])
+        if (input.keyPress['9'])
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
             renderer.dynamicInstancing = !renderer.dynamicInstancing;
 
 
-        if (input.keyPress['9'])
+        if (input.keyPress['0'])
             renderer.lightStencilMasking = !renderer.lightStencilMasking;
             renderer.lightStencilMasking = !renderer.lightStencilMasking;
 
 
         if (input.keyPress[' '])
         if (input.keyPress[' '])

+ 1 - 0
Docs/GettingStarted.dox

@@ -128,6 +128,7 @@ Urho3D.exe understands the following command line options:
 -w          Start in windowed mode
 -w          Start in windowed mode
 -b<length>  Sound buffer length in milliseconds
 -b<length>  Sound buffer length in milliseconds
 -r<freq>    Sound mixing frequency in Hz
 -r<freq>    Sound mixing frequency in Hz
+-prepass    Use light pre-pass rendering
 -headless   Headless mode. No application window will be created
 -headless   Headless mode. No application window will be created
 -logdebug   Display debug level log messages also in release mode
 -logdebug   Display debug level log messages also in release mode
 -lqshadows  Use low-quality (1-sample) shadow filtering
 -lqshadows  Use low-quality (1-sample) shadow filtering

+ 5 - 0
Engine/Engine/DebugHud.cpp

@@ -128,6 +128,11 @@ void DebugHud::Update(float timeStep)
     {
     {
         String mode;
         String mode;
         
         
+        if (renderer->GetLightPrepass())
+            mode += "Prepass ";
+        else
+            mode += "Forward ";
+        
         mode += "Tex: " + qualityTexts[renderer->GetTextureQuality()];
         mode += "Tex: " + qualityTexts[renderer->GetTextureQuality()];
         
         
         mode += " Mat: " + qualityTexts[renderer->GetMaterialQuality()];
         mode += " Mat: " + qualityTexts[renderer->GetMaterialQuality()];

+ 6 - 3
Engine/Engine/Engine.cpp

@@ -82,6 +82,7 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
     bool tripleBuffer = false;
     bool tripleBuffer = false;
     bool forceSM2 = false;
     bool forceSM2 = false;
     bool forceFallback = false;
     bool forceFallback = false;
+    bool prepass = false;
     bool shadows = true;
     bool shadows = true;
     bool lqShadows = false;
     bool lqShadows = false;
     bool sound = true;
     bool sound = true;
@@ -109,6 +110,8 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
                 interpolate = false;
                 interpolate = false;
             else if (argument == "mono")
             else if (argument == "mono")
                 stereo = false;
                 stereo = false;
+            else if (argument == "prepass")
+                prepass = true;
             else if (argument == "noshadows")
             else if (argument == "noshadows")
                 shadows = false;
                 shadows = false;
             else if (argument == "lqshadows")
             else if (argument == "lqshadows")
@@ -228,9 +231,9 @@ bool Engine::Initialize(const String& windowTitle, const String& logName, const
         if (!graphics->SetMode(width, height, fullscreen, vsync, tripleBuffer, multiSample))
         if (!graphics->SetMode(width, height, fullscreen, vsync, tripleBuffer, multiSample))
             return false;
             return false;
         
         
-        if (!shadows)
-            renderer->SetDrawShadows(false);
-        else if (lqShadows)
+        renderer->SetLightPrepass(prepass);
+        renderer->SetDrawShadows(shadows);
+        if (shadows && lqShadows)
             renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);
             renderer->SetShadowQuality(SHADOWQUALITY_LOW_16BIT);
         
         
         if (sound)
         if (sound)

+ 41 - 1
Engine/Graphics/Batch.cpp

@@ -124,7 +124,38 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
         
         
         graphics->SetShaderParameter(VSP_DEPTHMODE, depthMode);
         graphics->SetShaderParameter(VSP_DEPTHMODE, depthMode);
     }
     }
-
+    
+    if (graphics->NeedParameterUpdate(VSP_FRUSTUMSIZE, camera_))
+    {
+        Vector3 nearVector, farVector;
+        camera_->GetFrustumSize(nearVector, farVector);
+        Vector4 viewportParams(farVector.x_, farVector.y_, farVector.z_, 0.0f);
+        
+        graphics->SetShaderParameter(VSP_FRUSTUMSIZE, viewportParams);
+    }
+    
+    IntRect viewport = graphics->GetViewport();
+    unsigned viewportHash = (viewport.left_) | (viewport.top_ << 8) | (viewport.right_ << 16) | (viewport.bottom_ << 24);
+    if (graphics->NeedParameterUpdate(VSP_GBUFFEROFFSETS, (const void*)viewportHash))
+    {
+        IntVector2 screenSize = graphics->GetRenderTargetDimensions();
+        
+        float gBufferWidth = (float)screenSize.x_;
+        float gBufferHeight = (float)screenSize.y_;
+        float widthRange = 0.5f * (viewport.right_ - viewport.left_) / gBufferWidth;
+        float heightRange = 0.5f * (viewport.bottom_ - viewport.top_) / gBufferHeight;
+        
+        #ifdef USE_OPENGL
+        Vector4 bufferUVOffset(((float)viewport.left_) / gBufferWidth + widthRange,
+            ((float)viewport.top_) / gBufferHeight + heightRange, widthRange, heightRange);
+        #else
+        Vector4 bufferUVOffset((0.5f + (float)viewport.left_) / gBufferWidth + widthRange,
+            (0.5f + (float)viewport.top_) / gBufferHeight + heightRange, widthRange, heightRange);
+        #endif
+        
+        graphics->SetShaderParameter(VSP_GBUFFEROFFSETS, bufferUVOffset);
+    }
+    
     if (overrideView_)
     if (overrideView_)
     {
     {
         if (graphics->NeedParameterUpdate(VSP_VIEWPROJ, ((unsigned char*)camera_) + 4))
         if (graphics->NeedParameterUpdate(VSP_VIEWPROJ, ((unsigned char*)camera_) + 4))
@@ -195,6 +226,15 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
         }
         }
     }
     }
     
     
+    if (graphics->NeedParameterUpdate(PSP_DEPTHRECONSTRUCT, camera_))
+    {
+        float farClip = camera_->GetFarClip();
+        float nearClip = camera_->GetNearClip();
+        Vector4 depthReconstruct(farClip / (farClip - nearClip), -nearClip / (farClip - nearClip), 0.0f, 0.0f);
+        
+        graphics->SetShaderParameter(PSP_DEPTHRECONSTRUCT, depthReconstruct);
+    }
+    
     // Set light-related shader parameters
     // Set light-related shader parameters
     Light* light = 0;
     Light* light = 0;
     Texture2D* shadowMap = 0;
     Texture2D* shadowMap = 0;

+ 1 - 0
Engine/Graphics/GraphicsDefs.cpp

@@ -47,6 +47,7 @@ StringHash VSP_SKINMATRICES("SkinMatrices");
 StringHash VSP_VERTEXLIGHTS("VertexLights");
 StringHash VSP_VERTEXLIGHTS("VertexLights");
 StringHash PSP_AMBIENTSTARTCOLOR("AmbientStartColor");
 StringHash PSP_AMBIENTSTARTCOLOR("AmbientStartColor");
 StringHash PSP_AMBIENTENDCOLOR("AmbientEndColor");
 StringHash PSP_AMBIENTENDCOLOR("AmbientEndColor");
+StringHash PSP_DEPTHRECONSTRUCT("DepthReconstruct");
 StringHash PSP_FOGCOLOR("FogColor");
 StringHash PSP_FOGCOLOR("FogColor");
 StringHash PSP_FOGPARAMS("FogParams");
 StringHash PSP_FOGPARAMS("FogParams");
 StringHash PSP_LIGHTCOLOR("LightColor");
 StringHash PSP_LIGHTCOLOR("LightColor");

+ 1 - 0
Engine/Graphics/GraphicsDefs.h

@@ -217,6 +217,7 @@ extern StringHash VSP_SKINMATRICES;
 extern StringHash VSP_VERTEXLIGHTS;
 extern StringHash VSP_VERTEXLIGHTS;
 extern StringHash PSP_AMBIENTSTARTCOLOR;
 extern StringHash PSP_AMBIENTSTARTCOLOR;
 extern StringHash PSP_AMBIENTENDCOLOR;
 extern StringHash PSP_AMBIENTENDCOLOR;
+extern StringHash PSP_DEPTHRECONSTRUCT;
 extern StringHash PSP_FOGCOLOR;
 extern StringHash PSP_FOGCOLOR;
 extern StringHash PSP_FOGPARAMS;
 extern StringHash PSP_FOGPARAMS;
 extern StringHash PSP_LIGHTCOLOR;
 extern StringHash PSP_LIGHTCOLOR;

+ 21 - 0
Engine/Graphics/Light.cpp

@@ -358,6 +358,27 @@ Frustum Light::GetFrustum() const
     return ret;
     return ret;
 }
 }
 
 
+
+Matrix3x4 Light::GetDirLightTransform(Camera& camera, bool getNearQuad)
+{
+    Vector3 nearVector, farVector;
+    camera.GetFrustumSize(nearVector, farVector);
+    float nearClip = camera.GetNearClip();
+    float farClip = camera.GetFarClip();
+    
+    float distance = getNearQuad ? nearClip : farClip;
+    if (!camera.IsOrthographic())
+        farVector *= (distance / farClip);
+    else
+        farVector.z_ *= (distance / farClip);
+    
+    // Set an epsilon from clip planes due to possible inaccuracy
+    /// \todo Rather set an identity projection matrix
+    farVector.z_ = Clamp(farVector.z_, (1.0f + M_LARGE_EPSILON) * nearClip, (1.0f - M_LARGE_EPSILON) * farClip);
+    
+    return  Matrix3x4(Vector3(0.0f, 0.0f, farVector.z_), Quaternion::IDENTITY, Vector3(farVector.x_, farVector.y_, 1.0f));
+}
+
 const Matrix3x4& Light::GetVolumeTransform()
 const Matrix3x4& Light::GetVolumeTransform()
 {
 {
     const Matrix3x4& transform = GetWorldTransform();
     const Matrix3x4& transform = GetWorldTransform();

+ 3 - 0
Engine/Graphics/Light.h

@@ -241,8 +241,11 @@ public:
     void SetIntensitySortValue(float distance);
     void SetIntensitySortValue(float distance);
     /// %Set sort value based on overall intensity over a bounding box.
     /// %Set sort value based on overall intensity over a bounding box.
     void SetIntensitySortValue(const BoundingBox& box);
     void SetIntensitySortValue(const BoundingBox& box);
+    /// Return directional light quad transform for either near or far split.
+    Matrix3x4 GetDirLightTransform(Camera& camera, bool getNearQuad = false);
     /// Return light volume model transform.
     /// Return light volume model transform.
     const Matrix3x4& GetVolumeTransform();
     const Matrix3x4& GetVolumeTransform();
+
     
     
     /// %Set ramp texture attribute.
     /// %Set ramp texture attribute.
     void SetRampTextureAttr(ResourceRef value);
     void SetRampTextureAttr(ResourceRef value);

+ 223 - 32
Engine/Graphics/Renderer.cpp

@@ -51,6 +51,20 @@
 
 
 #include "DebugNew.h"
 #include "DebugNew.h"
 
 
+static const float dirLightVertexData[] =
+{
+    -1, 1, 0,
+    1, 1, 0,
+    1, -1, 0,
+    -1, -1, 0,
+};
+
+static const unsigned short dirLightIndexData[] =
+{
+    0, 1, 2,
+    2, 3, 0,
+};
+
 static const float pointLightVertexData[] =
 static const float pointLightVertexData[] =
 {
 {
     -0.423169f, -1.000000f, 0.423169f,
     -0.423169f, -1.000000f, 0.423169f,
@@ -129,14 +143,14 @@ static const unsigned short pointLightIndexData[] =
 
 
 static const float spotLightVertexData[] =
 static const float spotLightVertexData[] =
 {
 {
-    0.001f, 0.001f, 0.001f,
-    0.001f, -0.001f, 0.001f,
-    -0.001f, -0.001f, 0.001f,
-    -0.001f, 0.001f, 0.001f,
-    1.0f, 1.0f, 1.0f,
-    1.0f, -1.0f, 1.0f,
-    -1.0f,  -1.0f, 1.0f,
-    -1.0f, 1.0f, 1.0f,
+    0.00001f, 0.00001f, 0.00001f,
+    0.00001f, -0.00001f, 0.00001f,
+    -0.00001f, -0.00001f, 0.00001f,
+    -0.00001f, 0.00001f, 0.00001f,
+    1.00000f, 1.00000f, 0.99999f,
+    1.00000f, -1.00000f, 0.99999f,
+    -1.00000f,  -1.00000f, 0.99999f,
+    -1.00000f, 1.00000f, 0.99999f,
 };
 };
 
 
 static const unsigned short spotLightIndexData[] =
 static const unsigned short spotLightIndexData[] =
@@ -171,12 +185,24 @@ static const String shadowVariations[] =
     #endif
     #endif
 };
 };
 
 
+static const String linearVariations[] =
+{
+    "",
+    "Linear"
+};
+
 static const String fallbackVariations[] =
 static const String fallbackVariations[] =
 {
 {
     "",
     "",
     "FB"
     "FB"
 };
 };
 
 
+static const String hwVariations[] =
+{
+    "",
+    "HW"
+};
+
 static const String geometryVSVariations[] =
 static const String geometryVSVariations[] =
 {
 {
     "",
     "",
@@ -212,6 +238,14 @@ static const String vertexLightVSVariations[] =
     "6VL"
     "6VL"
 };
 };
 
 
+static const String deferredLightVSVariations[] =
+{
+    "",
+    "Dir",
+    "Ortho",
+    "OrthoDir"
+};
+
 static const String lightPSVariations[] = 
 static const String lightPSVariations[] = 
 {
 {
     "Dir",
     "Dir",
@@ -295,12 +329,55 @@ void Renderer::SetViewport(unsigned index, const Viewport& viewport)
 
 
 void Renderer::SetLightPrepass(bool enable)
 void Renderer::SetLightPrepass(bool enable)
 {
 {
-    // Light prepass is incompatible with hardware multisampling, so disable if enabled.
-    if (graphics_->GetMultiSample() > 1)
-        graphics_->SetMode(graphics_->GetWidth(), graphics_->GetHeight(), graphics_->GetFullscreen(), graphics_->GetVSync(),
-            graphics_->GetTripleBuffer(), 1);
+    if (!initialized_)
+    {
+        LOGERROR("Can not switch light pre-pass rendering before setting initial screen mode");
+        return;
+    }
     
     
-    lightPrepass_ = enable;
+    if (enable != lightPrepass_)
+    {
+        // Light prepass is incompatible with hardware multisampling, so set new screen mode with 1x sampling if in use
+        if (graphics_->GetMultiSample() > 1)
+            graphics_->SetMode(graphics_->GetWidth(), graphics_->GetHeight(), graphics_->GetFullscreen(), graphics_->GetVSync(),
+                graphics_->GetTripleBuffer(), 1);
+        
+        // Create the G-buffer textures if necessary
+        if (enable)
+        {
+            if (!normalBuffer_)
+            {
+                normalBuffer_ = new Texture2D(context_);
+                normalBuffer_->SetSize(0, 0, Graphics::GetRGBAFormat(), TEXTURE_RENDERTARGET);
+            }
+            
+            if (!depthBuffer_)
+            {
+                if (!graphics_->GetHardwareDepthSupport())
+                {
+                    depthBuffer_ = new Texture2D(context_);
+                    depthBuffer_->SetSize(0, 0, Graphics::GetDepthFormat(), TEXTURE_RENDERTARGET);
+                }
+                else
+                    depthBuffer_ = graphics_->GetDepthTexture();
+            }
+            
+            if (!lightBuffer_)
+            {
+                lightBuffer_ = new Texture2D(context_);
+                lightBuffer_->SetSize(0, 0, Graphics::GetRGBAFormat(), TEXTURE_RENDERTARGET);
+            }
+        }
+        else
+        {
+            normalBuffer_.Reset();
+            depthBuffer_.Reset();
+            lightBuffer_.Reset();
+        }
+        
+        lightPrepass_ = enable;
+        shadersDirty_ = true;
+    }
 }
 }
 
 
 void Renderer::SetSpecularLighting(bool enable)
 void Renderer::SetSpecularLighting(bool enable)
@@ -684,6 +761,8 @@ bool Renderer::AddView(RenderSurface* renderTarget, const Viewport& viewport)
 Geometry* Renderer::GetLightGeometry(Light* light)
 Geometry* Renderer::GetLightGeometry(Light* light)
 {
 {
     LightType type = light->GetLightType();
     LightType type = light->GetLightType();
+    if (type == LIGHT_DIRECTIONAL)
+        return dirLightGeometry_;
     if (type == LIGHT_SPOT)
     if (type == LIGHT_SPOT)
         return spotLightGeometry_;
         return spotLightGeometry_;
     else if (type == LIGHT_POINT)
     else if (type == LIGHT_POINT)
@@ -1002,7 +1081,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, b
         }
         }
         else
         else
         {
         {
-            if (type == PASS_BASE)
+            if (type == PASS_BASE || type == PASS_MATERIAL)
             {
             {
                 unsigned numVertexLights = 0;
                 unsigned numVertexLights = 0;
                 if (batch.lightQueue_)
                 if (batch.lightQueue_)
@@ -1038,6 +1117,50 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, b
     }
     }
 }
 }
 
 
+void Renderer::SetLightVolumeShaders(Batch& batch)
+{
+    bool fallback = graphics_->GetFallback();
+    
+    unsigned vsi = DLVS_NONE;
+    unsigned psi = DLPS_NONE;
+    Light* light = batch.lightQueue_->light_;
+    
+    switch (light->GetLightType())
+    {
+    case LIGHT_DIRECTIONAL:
+        vsi += DLVS_DIR;
+        break;
+        
+    case LIGHT_POINT:
+        if (light->GetShapeTexture())
+            psi += DLPS_POINTMASK;
+        else
+            psi += DLPS_POINT;
+        break;
+        
+    case LIGHT_SPOT:
+        psi += DLPS_SPOT;
+        break;
+    }
+    
+    if (batch.lightQueue_->shadowMap_)
+        psi += DLPS_SHADOW;
+    
+    if (!fallback && specularLighting_ && light->GetSpecularIntensity() > 0.0f)
+        psi += DLPS_SPEC;
+    
+    if (batch.camera_->IsOrthographic())
+    {
+        vsi += DLVS_ORTHO;
+        psi += DLPS_ORTHO;
+    }
+    
+    batch.material_ = 0;
+    batch.pass_ = 0;
+    batch.vertexShader_ = lightVS_[vsi];
+    batch.pixelShader_ = lightPS_[psi];
+}
+
 bool Renderer::ResizeInstancingBuffer(unsigned numInstances)
 bool Renderer::ResizeInstancingBuffer(unsigned numInstances)
 {
 {
     if (!instancingBuffer_ || !dynamicInstancing_)
     if (!instancingBuffer_ || !dynamicInstancing_)
@@ -1139,15 +1262,60 @@ void Renderer::LoadShaders()
     // Load inbuilt shaders
     // Load inbuilt shaders
     stencilVS_ = GetVertexShader("Stencil");
     stencilVS_ = GetVertexShader("Stencil");
     stencilPS_ = GetPixelShader("Stencil");
     stencilPS_ = GetPixelShader("Stencil");
+    lightVS_.Clear();
+    lightPS_.Clear();
+    
+    if (lightPrepass_)
+    {
+        lightVS_.Resize(MAX_DEFERRED_LIGHT_VS_VARIATIONS);
+        lightPS_.Resize(MAX_DEFERRED_LIGHT_PS_VARIATIONS);
+        
+        unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
+        unsigned fallback = graphics_->GetFallback() ? 1 : 0;
+        if (fallback)
+            shadows = SHADOWQUALITY_HIGH_16BIT;
+        
+        for (unsigned i = 0; i < MAX_DEFERRED_LIGHT_VS_VARIATIONS; ++i)
+            lightVS_[i] = GetVertexShader("LightVolume_" + deferredLightVSVariations[i]);
+        
+        for (unsigned i = 0; i < lightPS_.Size(); ++i)
+        {
+            // Specular variations do not exist for fallback shaders, so skip
+            if (fallback && i & DLPS_SPEC)
+                continue;
+            
+            /// \todo Allow specifying the light volume shader name for different lighting models
+            String linearDepth = linearVariations[(!fallback && !graphics_->GetHardwareDepthSupport() && i < DLPS_ORTHO) ? 1 : 0];
+            if (i & DLPS_SHADOW)
+            {
+                lightPS_[i] = GetPixelShader("LightVolume_" + linearDepth + lightPSVariations[i % DLPS_ORTHO] +
+                    shadowVariations[shadows] + fallbackVariations[fallback]);
+            }
+            else
+            {
+                lightPS_[i] = GetPixelShader("LightVolume_" + linearDepth + lightPSVariations[i % DLPS_ORTHO] +
+                    fallbackVariations[fallback]);
+            }
+        }
+    }
     
     
     shadersDirty_ = false;
     shadersDirty_ = false;
 }
 }
 
 
 void Renderer::LoadMaterialShaders(Technique* technique)
 void Renderer::LoadMaterialShaders(Technique* technique)
 {
 {
-    LoadPassShaders(technique, PASS_BASE);
-    LoadPassShaders(technique, PASS_LITBASE);
-    LoadPassShaders(technique, PASS_LIGHT);
+    if (lightPrepass_ && technique->HasPass(PASS_GBUFFER))
+    {
+        LoadPassShaders(technique, PASS_GBUFFER);
+        LoadPassShaders(technique, PASS_MATERIAL);
+    }
+    else
+    {
+        LoadPassShaders(technique, PASS_BASE);
+        LoadPassShaders(technique, PASS_LITBASE);
+        LoadPassShaders(technique, PASS_LIGHT);
+    }
+    
     LoadPassShaders(technique, PASS_PREALPHA);
     LoadPassShaders(technique, PASS_PREALPHA);
     LoadPassShaders(technique, PASS_POSTALPHA);
     LoadPassShaders(technique, PASS_POSTALPHA);
     LoadPassShaders(technique, PASS_SHADOW);
     LoadPassShaders(technique, PASS_SHADOW);
@@ -1159,6 +1327,11 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
     if (!pass)
     if (!pass)
         return;
         return;
     
     
+    unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
+    unsigned fallback = graphics_->GetFallback() ? 1 : 0;
+    if (fallback)
+        shadows = SHADOWQUALITY_HIGH_16BIT;
+    
     String vertexShaderName = pass->GetVertexShaderName();
     String vertexShaderName = pass->GetVertexShaderName();
     String pixelShaderName = pass->GetPixelShaderName();
     String pixelShaderName = pass->GetPixelShaderName();
     
     
@@ -1168,10 +1341,15 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
     if (pixelShaderName.Find('_') == String::NPOS)
     if (pixelShaderName.Find('_') == String::NPOS)
         pixelShaderName += "_";
         pixelShaderName += "_";
     
     
-    unsigned shadows = (graphics_->GetHardwareShadowSupport() ? 1 : 0) | (shadowQuality_ & SHADOWQUALITY_HIGH_16BIT);
-    unsigned fallback = graphics_->GetFallback() ? 1 : 0;
-    if (fallback)
-        shadows = SHADOWQUALITY_HIGH_16BIT;
+    // If hardware depth is used, do not write depth into a rendertarget in the G-buffer pass
+    // Also check for fallback G-buffer (different layout)
+    if (type == PASS_GBUFFER)
+    {
+        unsigned hwDepth = graphics_->GetHardwareDepthSupport() ? 1 : 0;
+        vertexShaderName += hwVariations[hwDepth];
+        pixelShaderName += hwVariations[hwDepth];
+        pixelShaderName += fallbackVariations[fallback];
+    }
     
     
     if (type == PASS_SHADOW)
     if (type == PASS_SHADOW)
     {
     {
@@ -1225,7 +1403,7 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
     }
     }
     else
     else
     {
     {
-        if (type == PASS_BASE)
+        if (type == PASS_BASE || type == PASS_MATERIAL)
         {
         {
             vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
             vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
             for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
             for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
@@ -1273,18 +1451,18 @@ void Renderer::ReloadTextures()
 
 
 void Renderer::CreateGeometries()
 void Renderer::CreateGeometries()
 {
 {
-    SharedPtr<VertexBuffer> plvb(new VertexBuffer(context_));
-    plvb->SetSize(24, MASK_POSITION);
-    plvb->SetData(pointLightVertexData);
+    SharedPtr<VertexBuffer> dlvb(new VertexBuffer(context_));
+    dlvb->SetSize(4, MASK_POSITION);
+    dlvb->SetData(dirLightVertexData);
     
     
-    SharedPtr<IndexBuffer> plib(new IndexBuffer(context_));
-    plib->SetSize(132, false);
-    plib->SetData(pointLightIndexData);
+    SharedPtr<IndexBuffer> dlib(new IndexBuffer(context_));
+    dlib->SetSize(6, false);
+    dlib->SetData(dirLightIndexData);
     
     
-    pointLightGeometry_ = new Geometry(context_);
-    pointLightGeometry_->SetVertexBuffer(0, plvb);
-    pointLightGeometry_->SetIndexBuffer(plib);
-    pointLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, plib->GetIndexCount());
+    dirLightGeometry_ = new Geometry(context_);
+    dirLightGeometry_->SetVertexBuffer(0, dlvb);
+    dirLightGeometry_->SetIndexBuffer(dlib);
+    dirLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, dlib->GetIndexCount());
     
     
     SharedPtr<VertexBuffer> slvb(new VertexBuffer(context_));
     SharedPtr<VertexBuffer> slvb(new VertexBuffer(context_));
     slvb->SetSize(8, MASK_POSITION);
     slvb->SetSize(8, MASK_POSITION);
@@ -1299,6 +1477,19 @@ void Renderer::CreateGeometries()
     spotLightGeometry_->SetIndexBuffer(slib);
     spotLightGeometry_->SetIndexBuffer(slib);
     spotLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, slib->GetIndexCount());
     spotLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, slib->GetIndexCount());
     
     
+    SharedPtr<VertexBuffer> plvb(new VertexBuffer(context_));
+    plvb->SetSize(24, MASK_POSITION);
+    plvb->SetData(pointLightVertexData);
+    
+    SharedPtr<IndexBuffer> plib(new IndexBuffer(context_));
+    plib->SetSize(132, false);
+    plib->SetData(pointLightIndexData);
+    
+    pointLightGeometry_ = new Geometry(context_);
+    pointLightGeometry_->SetVertexBuffer(0, plvb);
+    pointLightGeometry_->SetIndexBuffer(plib);
+    pointLightGeometry_->SetDrawRange(TRIANGLE_LIST, 0, plib->GetIndexCount());
+    
     faceSelectCubeMap_ = new TextureCube(context_);
     faceSelectCubeMap_ = new TextureCube(context_);
     faceSelectCubeMap_->SetNumLevels(1);
     faceSelectCubeMap_->SetNumLevels(1);
     faceSelectCubeMap_->SetSize(1, graphics_->GetRGBAFormat());
     faceSelectCubeMap_->SetSize(1, graphics_->GetRGBAFormat());

+ 98 - 3
Engine/Graphics/Renderer.h

@@ -106,6 +106,81 @@ enum LightPSVariation
     MAX_LIGHT_PS_VARIATIONS
     MAX_LIGHT_PS_VARIATIONS
 };
 };
 
 
+/// Deferred light volume vertex shader variations.
+enum DeferredLightVSVariation
+{
+    DLVS_NONE = 0,
+    DLVS_DIR,
+    DLVS_ORTHO,
+    DLVS_ORTHODIR,
+    MAX_DEFERRED_LIGHT_VS_VARIATIONS
+};
+
+/// Deferred light volume pixels shader variations.
+enum DeferredLightPSVariation
+{
+    DLPS_NONE = 0,
+    DLPS_SPOT,
+    DLPS_POINT,
+    DLPS_POINTMASK,
+    DLPS_SPEC,
+    DLPS_SPOTSPEC,
+    DLPS_POINTSPEC,
+    DLPS_POINTMASKSPEC,
+    DLPS_SHADOW,
+    DLPS_SPOTSHADOW,
+    DLPS_POINTSHADOW,
+    DLPS_POINTMASKSHADOW,
+    DLPS_SHADOWSPEC,
+    DLPS_SPOTSHADOWSPEC,
+    DLPS_POINTSHADOWSPEC,
+    DLPS_POINTMASKSHADOWSPEC,
+    DLPS_ORTHO,
+    DLPS_ORTHOSPOT,
+    DLPS_ORTHOPOINT,
+    DLPS_ORTHOPOINTMASK,
+    DLPS_ORTHOSPEC,
+    DLPS_ORTHOSPOTSPEC,
+    DLPS_ORTHOPOINTSPEC,
+    DLPS_ORTHOPOINTMASKSPEC,
+    DLPS_ORTHOSHADOW,
+    DLPS_ORTHOSPOTSHADOW,
+    DLPS_ORTHOPOINTSHADOW,
+    DLPS_ORTHOPOINTMASKSHADOW,
+    DLPS_ORTHOSHADOWSPEC,
+    DLPS_ORTHOSPOTSHADOWSPEC,
+    DLPS_ORTHOPOINTSHADOWSPEC,
+    DLPS_ORTHOPOINTMASKSHADOWSPEC,
+    MAX_DEFERRED_LIGHT_PS_VARIATIONS
+};
+
+/// Deferred rendering edge filter parameters.
+struct EdgeFilterParameters
+{
+    /// Construct undefined.
+    EdgeFilterParameters()
+    {
+    }
+    
+    /// Construct with initial values.
+    EdgeFilterParameters(float radius, float threshold, float strength) :
+        radius_(radius),
+        threshold_(threshold),
+        strength_(strength)
+    {
+    }
+    
+    //! Validate parameters.
+    void Validate();
+    
+    //! Radius for calculating luminance gradient.
+    float radius_;
+    //! Luminance difference threshold needed to pass pixel.
+    float threshold_;
+    //! Filter strength.
+    float strength_;
+};
+
 /// High-level rendering subsystem. Manages drawing of 3D views.
 /// High-level rendering subsystem. Manages drawing of 3D views.
 class Renderer : public Object
 class Renderer : public Object
 {
 {
@@ -223,6 +298,12 @@ public:
     TextureCube* GetFaceSelectCubeMap() const { return faceSelectCubeMap_; }
     TextureCube* GetFaceSelectCubeMap() const { return faceSelectCubeMap_; }
     /// Return the shadowed pointlight indirection cube map.
     /// Return the shadowed pointlight indirection cube map.
     TextureCube* GetIndirectionCubeMap() const { return indirectionCubeMap_; }
     TextureCube* GetIndirectionCubeMap() const { return indirectionCubeMap_; }
+    /// Return the normal buffer for light pre-pass rendering.
+    Texture2D* GetNormalBuffer() const { return normalBuffer_; }
+    /// Return the depth buffer for light pre-pass rendering.
+    Texture2D* GetDepthBuffer() const { return depthBuffer_; }
+    /// Return the light accumulation buffer for light pre-pass rendering.
+    Texture2D* GetLightBuffer() const { return lightBuffer_; }
     /// Return the instancing vertex buffer
     /// Return the instancing vertex buffer
     VertexBuffer* GetInstancingBuffer() const { return dynamicInstancing_ ? instancingBuffer_ : (VertexBuffer*)0; }
     VertexBuffer* GetInstancingBuffer() const { return dynamicInstancing_ ? instancingBuffer_ : (VertexBuffer*)0; }
     /// Return a vertex shader by name.
     /// Return a vertex shader by name.
@@ -254,8 +335,10 @@ public:
     Camera* GetShadowCamera();
     Camera* GetShadowCamera();
     /// Get a shader program.
     /// Get a shader program.
     ShaderVariation* GetShader(const String& name, const String& extension, bool checkExists) const;
     ShaderVariation* GetShader(const String& name, const String& extension, bool checkExists) const;
-    /// Choose shaders for a batch.
+    /// Choose shaders for a forward rendering batch.
     void SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, bool allowShadows = true);
     void SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, bool allowShadows = true);
+    /// Choose shaders for a light volume batch.
+    void SetLightVolumeShaders(Batch& batch);
     /// Ensure sufficient size of the instancing vertex buffer. Return true if successful.
     /// Ensure sufficient size of the instancing vertex buffer. Return true if successful.
     bool ResizeInstancingBuffer(unsigned numInstances);
     bool ResizeInstancingBuffer(unsigned numInstances);
     /// Reset shadow map allocation counts.
     /// Reset shadow map allocation counts.
@@ -295,10 +378,12 @@ private:
     WeakPtr<ResourceCache> cache_;
     WeakPtr<ResourceCache> cache_;
     /// Default zone.
     /// Default zone.
     SharedPtr<Zone> defaultZone_;
     SharedPtr<Zone> defaultZone_;
-    /// Point light volume geometry.
-    SharedPtr<Geometry> pointLightGeometry_;
+    /// Directional light quad geometry.
+    SharedPtr<Geometry> dirLightGeometry_;
     /// Spot light volume geometry.
     /// Spot light volume geometry.
     SharedPtr<Geometry> spotLightGeometry_;
     SharedPtr<Geometry> spotLightGeometry_;
+    /// Point light volume geometry.
+    SharedPtr<Geometry> pointLightGeometry_;
     /// Instance stream vertex buffer.
     /// Instance stream vertex buffer.
     SharedPtr<VertexBuffer> instancingBuffer_;
     SharedPtr<VertexBuffer> instancingBuffer_;
     /// Default material.
     /// Default material.
@@ -311,10 +396,20 @@ private:
     SharedPtr<TextureCube> faceSelectCubeMap_;
     SharedPtr<TextureCube> faceSelectCubeMap_;
     /// Indirection cube map for shadowed pointlights.
     /// Indirection cube map for shadowed pointlights.
     SharedPtr<TextureCube> indirectionCubeMap_;
     SharedPtr<TextureCube> indirectionCubeMap_;
+    /// Normal buffer for light pre-pass rendering.
+    SharedPtr<Texture2D> normalBuffer_;
+    /// Depth buffer for light pre-pass rendering.
+    SharedPtr<Texture2D> depthBuffer_;
+    /// Light accumulation buffer for light pre-pass rendering.
+    SharedPtr<Texture2D> lightBuffer_;
     /// Stencil rendering vertex shader.
     /// Stencil rendering vertex shader.
     SharedPtr<ShaderVariation> stencilVS_;
     SharedPtr<ShaderVariation> stencilVS_;
     /// Stencil rendering pixel shader.
     /// Stencil rendering pixel shader.
     SharedPtr<ShaderVariation> stencilPS_;
     SharedPtr<ShaderVariation> stencilPS_;
+    /// Light vertex shaders.
+    Vector<SharedPtr<ShaderVariation> > lightVS_;
+    /// Light pixel shaders.
+    Vector<SharedPtr<ShaderVariation> > lightPS_;
     /// Reusable scene nodes with shadow camera components.
     /// Reusable scene nodes with shadow camera components.
     Vector<SharedPtr<Node> > shadowCameraNodes_;
     Vector<SharedPtr<Node> > shadowCameraNodes_;
     /// Reusable occlusion buffers.
     /// Reusable occlusion buffers.

+ 209 - 9
Engine/Graphics/View.cpp

@@ -239,6 +239,7 @@ void View::Update(const FrameInfo& frame)
     occluders_.Clear();
     occluders_.Clear();
     baseQueue_.Clear();
     baseQueue_.Clear();
     preAlphaQueue_.Clear();
     preAlphaQueue_.Clear();
+    gbufferQueue_.Clear();
     alphaQueue_.Clear();
     alphaQueue_.Clear();
     postAlphaQueue_.Clear();
     postAlphaQueue_.Clear();
     lightQueues_.Clear();
     lightQueues_.Clear();
@@ -289,11 +290,11 @@ void View::Render()
     graphics_->SetTexture(TU_FACESELECT, renderer_->GetFaceSelectCubeMap());
     graphics_->SetTexture(TU_FACESELECT, renderer_->GetFaceSelectCubeMap());
     graphics_->SetTexture(TU_INDIRECTION, renderer_->GetIndirectionCubeMap());
     graphics_->SetTexture(TU_INDIRECTION, renderer_->GetIndirectionCubeMap());
     
     
-    // Reset the light optimization stencil reference value
-    lightStencilValue_ = 1;
-    
     // Render
     // Render
-    RenderBatches();
+    if (renderer_->GetLightPrepass())
+        RenderBatchesLightPrepass();
+    else
+        RenderBatchesForward();
     
     
     graphics_->SetScissorTest(false);
     graphics_->SetScissorTest(false);
     graphics_->SetStencilTest(false);
     graphics_->SetStencilTest(false);
@@ -481,6 +482,7 @@ void View::GetDrawables()
 void View::GetBatches()
 void View::GetBatches()
 {
 {
     WorkQueue* queue = GetSubsystem<WorkQueue>();
     WorkQueue* queue = GetSubsystem<WorkQueue>();
+    bool prepass = renderer_->GetLightPrepass();
     
     
     // Process lit geometries and shadow casters for each light
     // Process lit geometries and shadow casters for each light
     {
     {
@@ -666,10 +668,6 @@ void View::GetBatches()
                 if (!renderTarget_ && baseBatch.material_ && baseBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_)
                 if (!renderTarget_ && baseBatch.material_ && baseBatch.material_->GetAuxViewFrameNumber() != frame_.frameNumber_)
                     CheckMaterialForAuxView(baseBatch.material_);
                     CheckMaterialForAuxView(baseBatch.material_);
                 
                 
-                // If object already has a pixel lit base pass, can skip the unlit / vertex lit base pass
-                if (drawable->HasBasePass(j))
-                    continue;
-                
                 // Fill the rest of the batch
                 // Fill the rest of the batch
                 baseBatch.camera_ = camera_;
                 baseBatch.camera_ = camera_;
                 baseBatch.zone_ = GetZone(drawable);
                 baseBatch.zone_ = GetZone(drawable);
@@ -677,6 +675,29 @@ void View::GetBatches()
                 
                 
                 Pass* pass = 0;
                 Pass* pass = 0;
                 
                 
+                // In light prepass mode check for G-buffer and material passes first
+                if (prepass)
+                {
+                    Pass* pass = tech->GetPass(PASS_GBUFFER);
+                    if (pass)
+                    {
+                        FinalizeBatch(baseBatch, tech, pass);
+                        gbufferQueue_.AddBatch(baseBatch);
+                        
+                        pass = tech->GetPass(PASS_MATERIAL);
+                        if (pass)
+                        {
+                            FinalizeBatch(baseBatch, tech, pass);
+                            baseQueue_.AddBatch(baseBatch);
+                        }
+                        continue;
+                    }
+                }
+                
+                // If object already has a pixel lit base pass, can skip the unlit base pass
+                if (drawable->HasBasePass(j))
+                    continue;
+                
                 // Check for unlit or vertex lit base pass
                 // Check for unlit or vertex lit base pass
                 pass = tech->GetPass(PASS_BASE);
                 pass = tech->GetPass(PASS_BASE);
                 if (pass)
                 if (pass)
@@ -750,6 +771,11 @@ void View::UpdateGeometries()
         queue->AddWorkItem(item);
         queue->AddWorkItem(item);
         item.start_ = &preAlphaQueue_;
         item.start_ = &preAlphaQueue_;
         queue->AddWorkItem(item);
         queue->AddWorkItem(item);
+        if (renderer_->GetLightPrepass())
+        {
+            item.start_ = &gbufferQueue_;
+            queue->AddWorkItem(item);
+        }
         
         
         item.workFunction_ = SortBatchQueueBackToFrontWork;
         item.workFunction_ = SortBatchQueueBackToFrontWork;
         item.start_ = &alphaQueue_;
         item.start_ = &alphaQueue_;
@@ -812,6 +838,7 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
 {
 {
     Light* light = lightQueue.light_;
     Light* light = lightQueue.light_;
     Light* firstLight = drawable->GetFirstLight();
     Light* firstLight = drawable->GetFirstLight();
+    bool prepass = renderer_->GetLightPrepass();
     // Shadows on transparencies can only be rendered if shadow maps are not reused
     // Shadows on transparencies can only be rendered if shadow maps are not reused
     bool allowTransparentShadows = !renderer_->GetReuseShadowMaps();
     bool allowTransparentShadows = !renderer_->GetReuseShadowMaps();
     bool hasVertexLights = drawable->GetVertexLights().Size() > 0;
     bool hasVertexLights = drawable->GetVertexLights().Size() > 0;
@@ -828,6 +855,10 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
         
         
         Pass* pass = 0;
         Pass* pass = 0;
         
         
+        // Do not create pixel lit passes for materials that render into the G-buffer
+        if (prepass && tech->HasPass(PASS_GBUFFER))
+            continue;
+        
         // Check for lit base pass. Because it uses the replace blend mode, it must be ensured to be the first light
         // Check for lit base pass. Because it uses the replace blend mode, it must be ensured to be the first light
         // Also vertex lighting requires the non-lit base pass, so skip if any vertex lights
         // Also vertex lighting requires the non-lit base pass, so skip if any vertex lights
         if (light == firstLight && !hasVertexLights && !drawable->HasBasePass(i))
         if (light == firstLight && !hasVertexLights && !drawable->HasBasePass(i))
@@ -868,8 +899,11 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
     }
     }
 }
 }
 
 
-void View::RenderBatches()
+void View::RenderBatchesForward()
 {
 {
+    // Reset the light optimization stencil reference value
+    lightStencilValue_ = 1;
+    
     // If not reusing shadowmaps, render all of them first
     // If not reusing shadowmaps, render all of them first
     if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !lightQueues_.Empty())
     if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !lightQueues_.Empty())
     {
     {
@@ -946,6 +980,114 @@ void View::RenderBatches()
     }
     }
 }
 }
 
 
+void View::RenderBatchesLightPrepass()
+{
+    // If not reusing shadowmaps, render all of them first
+    if (!renderer_->GetReuseShadowMaps() && renderer_->GetDrawShadows() && !lightQueues_.Empty())
+    {
+        PROFILE(RenderShadowMaps);
+        
+        for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+        {
+            if (i->shadowMap_)
+                RenderShadowMap(*i);
+        }
+    }
+    
+    // Render the G-buffer
+    Texture2D* normalBuffer = renderer_->GetNormalBuffer();
+    Texture2D* depthBuffer = renderer_->GetDepthBuffer();
+    
+    if (graphics_->GetHardwareDepthSupport())
+    {
+        graphics_->SetRenderTarget(0, normalBuffer);
+        graphics_->SetDepthStencil(depthBuffer);
+        graphics_->SetViewport(screenRect_);
+        // Clear depth and stencil only
+        graphics_->Clear(CLEAR_DEPTH | CLEAR_STENCIL);
+    }
+    else
+    {
+        graphics_->SetRenderTarget(0, depthBuffer);
+        graphics_->ResetDepthStencil();
+        graphics_->SetViewport(screenRect_);
+        // Clear the depth render target to far depth
+        graphics_->Clear(CLEAR_COLOR | CLEAR_DEPTH | CLEAR_STENCIL, Color::WHITE);
+        graphics_->SetRenderTarget(1, normalBuffer);
+    }
+    
+    if (!gbufferQueue_.IsEmpty())
+    {
+        // Render G-buffer batches
+        PROFILE(RenderGBuffer);
+        
+        RenderBatchQueue(gbufferQueue_);
+    }
+    
+    graphics_->ResetRenderTarget(1);
+    
+    /*
+    if (!lightQueues_.Empty())
+    {
+        // Render shadow maps + opaque objects' shadowed additive lighting
+        PROFILE(RenderLights);
+        
+        for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
+        {
+            // If reusing shadowmaps, render each of them before the lit batches
+            if (renderer_->GetReuseShadowMaps() && i->shadowMap_)
+            {
+                RenderShadowMap(*i);
+                graphics_->SetRenderTarget(0, renderTarget_);
+                graphics_->SetDepthStencil(depthStencil_);
+                graphics_->SetViewport(screenRect_);
+            }
+            
+            RenderLightBatchQueue(i->litBatches_, i->light_);
+        }
+    }
+    */
+    
+    graphics_->SetScissorTest(false);
+    graphics_->SetStencilTest(false);
+    graphics_->SetRenderTarget(0, renderTarget_);
+    graphics_->SetDepthStencil(depthStencil_);
+    graphics_->SetViewport(screenRect_);
+    graphics_->Clear(CLEAR_COLOR, farClipZone_->GetFogColor());
+    
+    if (!baseQueue_.IsEmpty())
+    {
+        // Render opaque objects with deferred lighting result
+        PROFILE(RenderBase);
+        
+        RenderBatchQueue(baseQueue_);
+    }
+    
+    if (!preAlphaQueue_.IsEmpty())
+    {
+        // Render pre-alpha custom pass
+        PROFILE(RenderPreAlpha);
+        
+        RenderBatchQueue(preAlphaQueue_);
+    }
+    
+    if (!alphaQueue_.IsEmpty())
+    {
+        // Render transparent objects (both base passes & additive lighting)
+        PROFILE(RenderAlpha);
+        
+        RenderBatchQueue(alphaQueue_, true);
+    }
+    
+    if (!postAlphaQueue_.IsEmpty())
+    {
+        // Render pre-alpha custom pass
+        PROFILE(RenderPostAlpha);
+        
+        RenderBatchQueue(postAlphaQueue_);
+    }
+}
+
 void View::UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera)
 void View::UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera)
 {
 {
     float occluderSizeThreshold_ = renderer_->GetOccluderSizeThreshold();
     float occluderSizeThreshold_ = renderer_->GetOccluderSizeThreshold();
@@ -1776,9 +1918,12 @@ void View::PrepareInstancingBuffer()
     PROFILE(PrepareInstancingBuffer);
     PROFILE(PrepareInstancingBuffer);
     
     
     unsigned totalInstances = 0;
     unsigned totalInstances = 0;
+    bool prepass = renderer_->GetLightPrepass();
     
     
     totalInstances += baseQueue_.GetNumInstances(renderer_);
     totalInstances += baseQueue_.GetNumInstances(renderer_);
     totalInstances += preAlphaQueue_.GetNumInstances(renderer_);
     totalInstances += preAlphaQueue_.GetNumInstances(renderer_);
+    if (prepass)
+        totalInstances += gbufferQueue_.GetNumInstances(renderer_);
     
     
     for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
     for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
     {
     {
@@ -1797,6 +1942,8 @@ void View::PrepareInstancingBuffer()
         {
         {
             baseQueue_.SetTransforms(renderer_, lockedData, freeIndex);
             baseQueue_.SetTransforms(renderer_, lockedData, freeIndex);
             preAlphaQueue_.SetTransforms(renderer_, lockedData, freeIndex);
             preAlphaQueue_.SetTransforms(renderer_, lockedData, freeIndex);
+            if (prepass)
+                gbufferQueue_.SetTransforms(renderer_, lockedData, freeIndex);
             
             
             for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
             for (List<LightBatchQueue>::Iterator i = lightQueues_.Begin(); i != lightQueues_.End(); ++i)
             {
             {
@@ -1810,6 +1957,59 @@ void View::PrepareInstancingBuffer()
     }
     }
 }
 }
 
 
+void View::SetupLightBatch(Batch& batch)
+{
+    Light* light = batch.lightQueue_->light_;
+    LightType type = light->GetLightType();
+    float lightDist;
+    
+    graphics_->SetAlphaTest(false);
+    graphics_->SetBlendMode(BLEND_ADD);
+    graphics_->SetDepthWrite(false);
+    
+    if (type != LIGHT_DIRECTIONAL)
+    {
+        if (type == LIGHT_POINT)
+            lightDist = Sphere(light->GetWorldPosition(), light->GetRange() * 1.25f).DistanceFast(camera_->GetWorldPosition());
+        else
+            lightDist = light->GetFrustum().Distance(camera_->GetWorldPosition());
+        
+        // Draw front faces if not inside light volume
+        if (lightDist < camera_->GetNearClip() * 2.0f)
+        {
+            graphics_->SetCullMode(CULL_CW);
+            graphics_->SetDepthTest(CMP_GREATER);
+        }
+        else
+        {
+            graphics_->SetCullMode(CULL_CCW);
+            graphics_->SetDepthTest(CMP_LESSEQUAL);
+        }
+    }
+    else
+    {
+        graphics_->SetCullMode(CULL_NONE);
+        graphics_->SetDepthTest(CMP_LESSEQUAL);
+    }
+    
+    /// \todo Set stencil test to check for light masks
+    graphics_->SetScissorTest(false);
+    graphics_->SetStencilTest(false);
+}
+
+void View::DrawFullscreenQuad(Camera& camera, bool nearQuad)
+{
+    Light quadDirLight(context_);
+    Matrix3x4 model(quadDirLight.GetDirLightTransform(camera, nearQuad));
+    
+    graphics_->SetCullMode(CULL_NONE);
+    graphics_->SetShaderParameter(VSP_MODEL, model);
+    graphics_->SetShaderParameter(VSP_VIEWPROJ, camera.GetProjection());
+    graphics_->ClearTransformSources();
+    
+    renderer_->GetLightGeometry(&quadDirLight)->Draw(graphics_);
+}
+
 void View::RenderBatchQueue(const BatchQueue& queue, bool useScissor)
 void View::RenderBatchQueue(const BatchQueue& queue, bool useScissor)
 {
 {
     graphics_->SetScissorTest(false);
     graphics_->SetScissorTest(false);

+ 11 - 2
Engine/Graphics/View.h

@@ -123,8 +123,10 @@ private:
     void UpdateGeometries();
     void UpdateGeometries();
     /// Get pixel lit batches for a certain light and drawable.
     /// Get pixel lit batches for a certain light and drawable.
     void GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue);
     void GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue);
-    /// Render batches.
-    void RenderBatches();
+    /// Render batches using forward rendering.
+    void RenderBatchesForward();
+    /// Render batches using light pre-pass rendering.
+    void RenderBatchesLightPrepass();
     /// Query for occluders as seen from a camera.
     /// Query for occluders as seen from a camera.
     void UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera);
     void UpdateOccluders(PODVector<Drawable*>& occluders, Camera* camera);
     /// Draw occluders to occlusion buffer.
     /// Draw occluders to occlusion buffer.
@@ -171,10 +173,15 @@ private:
     void FinalizeBatch(Batch& batch, Technique* tech, Pass* pass, bool allowInstancing = true, bool allowShadows = true);
     void FinalizeBatch(Batch& batch, Technique* tech, Pass* pass, bool allowInstancing = true, bool allowShadows = true);
     /// Prepare instancing buffer by filling it with all instance transforms.
     /// Prepare instancing buffer by filling it with all instance transforms.
     void PrepareInstancingBuffer();
     void PrepareInstancingBuffer();
+    /// %Set up a light volume rendering batch.
+    void SetupLightBatch(Batch& batch);
+    /// Draw a full screen quad (either near or far.) Shaders must have been set beforehand.
+    void DrawFullscreenQuad(Camera& camera, bool nearQuad);
     /// Render everything in a batch queue, priority batches first.
     /// Render everything in a batch queue, priority batches first.
     void RenderBatchQueue(const BatchQueue& queue, bool useScissor = false);
     void RenderBatchQueue(const BatchQueue& queue, bool useScissor = false);
     /// Render batches lit by a specific light.
     /// Render batches lit by a specific light.
     void RenderLightBatchQueue(const BatchQueue& queue, Light* forwardQueueLight);
     void RenderLightBatchQueue(const BatchQueue& queue, Light* forwardQueueLight);
+
     /// Render a shadow map.
     /// Render a shadow map.
     void RenderShadowMap(const LightBatchQueue& queue);
     void RenderShadowMap(const LightBatchQueue& queue);
     
     
@@ -252,6 +259,8 @@ private:
     BatchQueue baseQueue_;
     BatchQueue baseQueue_;
     /// Pre-transparent pass batches.
     /// Pre-transparent pass batches.
     BatchQueue preAlphaQueue_;
     BatchQueue preAlphaQueue_;
+    /// Light pre-pass G-buffer batches.
+    BatchQueue gbufferQueue_;
     /// Transparent geometry batches.
     /// Transparent geometry batches.
     BatchQueue alphaQueue_;
     BatchQueue alphaQueue_;
     /// Post-transparent pass batches.
     /// Post-transparent pass batches.

+ 5 - 4
SourceAssets/HLSLShaders/LightVolume.xml

@@ -10,14 +10,15 @@
         <variation name="Spot" define="SPOTLIGHT" />
         <variation name="Spot" define="SPOTLIGHT" />
         <variation name="Point" define="POINTLIGHT" />
         <variation name="Point" define="POINTLIGHT" />
         <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
         <option name="Mask" define="CUBEMASK" require="POINTLIGHT" />
-        <option name="Shadow" define="SHADOW" />
         <option name="Spec" define="SPECULAR" />
         <option name="Spec" define="SPECULAR" />
+        <option name="Shadow" define="SHADOW" />
+        <option name="LQ" define="LQSHADOW" require="HWSHADOW" />
         <option name="HW" define="HWSHADOW" require="SHADOW" />
         <option name="HW" define="HWSHADOW" require="SHADOW" />
         <option name="FB" define="FALLBACK">
         <option name="FB" define="FALLBACK">
-            <exclude name="Linear" />
-            <exclude name="Shadow" />
-            <exclude name="Spec" />
+            <exclude name="LQ" />
             <exclude name="HW" />
             <exclude name="HW" />
+            <exclude name="Spec" />
+            <require name="SM2" />
         </option>
         </option>
     </shader>
     </shader>
 </shaders>
 </shaders>

+ 1 - 0
SourceAssets/HLSLShaders/Material.xml

@@ -1,5 +1,6 @@
 <shaders>
 <shaders>
     <shader name="Material" type="vs">
     <shader name="Material" type="vs">
+        <variation name="" />
         <variation name="1VL" define="NUMVERTEXLIGHTS=1" />
         <variation name="1VL" define="NUMVERTEXLIGHTS=1" />
         <variation name="2VL" define="NUMVERTEXLIGHTS=2" />
         <variation name="2VL" define="NUMVERTEXLIGHTS=2" />
         <variation name="3VL" define="NUMVERTEXLIGHTS=3" />
         <variation name="3VL" define="NUMVERTEXLIGHTS=3" />