Browse Source

Fixed the "lightvolumes" render path command not allowing custom shader defines. Added hardware depth utilizing deferred & prepass renderpaths as an example. These use less bandwidth and therefore perform faster, but the deferred path will cause far plane artifacts on OpenGL due to stencil buffer not being available when a readable depth format is used.

Lasse Öörni 11 years ago
parent
commit
4d320ad198

+ 22 - 0
Bin/CoreData/RenderPaths/DeferredHWDepth.xml

@@ -0,0 +1,22 @@
+<renderpath>
+    <rendertarget name="albedo" sizedivisor="1 1" format="rgba" />
+    <rendertarget name="normal" sizedivisor="1 1" format="rgba" />
+    <rendertarget name="depth" sizedivisor="1 1" format="readabledepth" />
+    <command type="clear" color="fog" depth="1.0" stencil="0" depthstencil="depth" />
+    <command type="scenepass" pass="deferred" marktostencil="true" vertexlights="true" metadata="gbuffer" depthstencil="depth">
+        <output index="0" name="viewport" />
+        <output index="1" name="albedo" />
+        <output index="2" name="normal" />
+    </command>
+    <command type="lightvolumes" vs="DeferredLight" ps="DeferredLight" psdefines="HWDEPTH" depthstencil="depth">
+        <texture unit="albedo" name="albedo" />
+        <texture unit="normal" name="normal" />
+        <texture unit="depth" name="depth" />
+    </command>
+    <command type="scenepass" pass="postopaque" depthstencil="depth" />
+    <command type="scenepass" pass="refract" depthstencil="depth">
+        <texture unit="environment" name="viewport" />
+    </command>
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" />
+    <command type="scenepass" pass="postalpha" sort="backtofront" depthstencil="depth" />
+</renderpath>

+ 21 - 0
Bin/CoreData/RenderPaths/PrepassHWDepth.xml

@@ -0,0 +1,21 @@
+<renderpath>
+    <rendertarget name="light" sizedivisor="1 1"  format="rgba" />
+    <rendertarget name="normal" sizedivisor="1 1" format="rgba" />
+    <rendertarget name="depth" sizedivisor="1 1"  format="readabledepth" />
+    <command type="clear" color="fog" depth="1.0" stencil="0" depthstencil="depth" />
+    <command type="scenepass" pass="prepass" marktostencil="true" metadata="gbuffer" output="normal" depthstencil="depth" />
+    <command type="clear" color="0 0 0 0" output="light" depthstencil="depth" />
+    <command type="lightvolumes" vs="PrepassLight" ps="PrepassLight" psdefines="HWDEPTH" output="light" depthstencil="depth" >
+        <texture unit="normal" name="normal" />
+        <texture unit="depth" name="depth" />
+    </command>
+    <command type="scenepass" pass="material" vertexlights="true" depthstencil="depth">
+        <texture unit="light" name="light" />
+    </command>
+    <command type="scenepass" pass="postopaque" depthstencil="depth" />
+    <command type="scenepass" pass="refract" depthstencil="depth">
+        <texture unit="environment" name="viewport" />
+    </command>
+    <command type="scenepass" pass="alpha" vertexlights="true" sort="backtofront" metadata="alpha" depthstencil="depth" />
+    <command type="scenepass" pass="postalpha" sort="backtofront" depthstencil="depth" />
+</renderpath>

+ 10 - 2
Bin/CoreData/Shaders/GLSL/DeferredLight.glsl

@@ -39,7 +39,11 @@ void PS()
 {
     // If rendering a directional light quad, optimize out the w divide
     #ifdef DIRLIGHT
-        float depth = DecodeDepth(texture2D(sDepthBuffer, vScreenPos).rgb);
+        #ifdef HWDEPTH
+            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
@@ -48,7 +52,11 @@ void PS()
         vec4 albedoInput = texture2D(sAlbedoBuffer, vScreenPos);
         vec4 normalInput = texture2D(sNormalBuffer, vScreenPos);
     #else
-        float depth = DecodeDepth(texture2DProj(sDepthBuffer, vScreenPos).rgb);
+        #ifdef HWDEPTH
+            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

+ 10 - 2
Bin/CoreData/Shaders/GLSL/PrepassLight.glsl

@@ -38,7 +38,11 @@ void PS()
 {
     // If rendering a directional light quad, optimize out the w divide
     #ifdef DIRLIGHT
-        float depth = DecodeDepth(texture2D(sDepthBuffer, vScreenPos).rgb);
+        #ifdef HWDEPTH
+            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
@@ -46,7 +50,11 @@ void PS()
         #endif
         vec4 normalInput = texture2D(sNormalBuffer, vScreenPos);
     #else
-        float depth = DecodeDepth(texture2DProj(sDepthBuffer, vScreenPos).rgb);
+        #ifdef HWDEPTH
+            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

+ 9 - 5
Bin/CoreData/Shaders/HLSL/DeferredLight.hlsl

@@ -48,27 +48,31 @@ void PS(
 {
     // If rendering a directional light quad, optimize out the w divide
     #ifdef DIRLIGHT
+        float depth = Sample(sDepthBuffer, iScreenPos).r;
+        #ifdef HWDEPTH
+            depth = ReconstructDepth(depth);
+        #endif
         #ifdef ORTHO
-            float depth = Sample(sDepthBuffer, iScreenPos).r;
             float3 worldPos = lerp(iNearRay, iFarRay, depth);
         #else
-            float depth = Sample(sDepthBuffer, iScreenPos).r;
             float3 worldPos = iFarRay * depth;
         #endif
         float4 albedoInput = Sample(sAlbedoBuffer, iScreenPos);
         float4 normalInput = Sample(sNormalBuffer, iScreenPos);
     #else
+        float depth = tex2Dproj(sDepthBuffer, iScreenPos).r;
+        #ifdef HWDEPTH
+            depth = ReconstructDepth(depth);
+        #endif
         #ifdef ORTHO
-            float depth = tex2Dproj(sDepthBuffer, iScreenPos).r;
             float3 worldPos = lerp(iNearRay, iFarRay, depth) / iScreenPos.w;
         #else
-            float depth = tex2Dproj(sDepthBuffer, iScreenPos).r;
             float3 worldPos = iFarRay * depth / iScreenPos.w;
         #endif
         float4 albedoInput = tex2Dproj(sAlbedoBuffer, iScreenPos);
         float4 normalInput = tex2Dproj(sNormalBuffer, iScreenPos);
     #endif
-
+    
     float3 normal = normalize(normalInput.rgb * 2.0 - 1.0);
     float4 projWorldPos = float4(worldPos, 1.0);
     float3 lightColor;

+ 8 - 4
Bin/CoreData/Shaders/HLSL/PrepassLight.hlsl

@@ -48,20 +48,24 @@ void PS(
 {
     // If rendering a directional light quad, optimize out the w divide
     #ifdef DIRLIGHT
+        float depth = Sample(sDepthBuffer, iScreenPos).r;
+        #ifdef HWDEPTH
+            depth = ReconstructDepth(depth);
+        #endif
         #ifdef ORTHO
-            float depth = Sample(sDepthBuffer, iScreenPos).r;
             float3 worldPos = lerp(iNearRay, iFarRay, depth);
         #else
-            float depth = Sample(sDepthBuffer, iScreenPos).r;
             float3 worldPos = iFarRay * depth;
         #endif
         float4 normalInput = Sample(sNormalBuffer, iScreenPos);
     #else
+        float depth = tex2Dproj(sDepthBuffer, iScreenPos).r;
+        #ifdef HWDEPTH
+            depth = ReconstructDepth(depth);
+        #endif
         #ifdef ORTHO
-            float depth = tex2Dproj(sDepthBuffer, iScreenPos).r;
             float3 worldPos = lerp(iNearRay, iFarRay, depth) / iScreenPos.w;
         #else
-            float depth = tex2Dproj(sDepthBuffer, iScreenPos).r;
             float3 worldPos = iFarRay * depth / iScreenPos.w;
         #endif
         float4 normalInput = tex2Dproj(sNormalBuffer, iScreenPos);

+ 14 - 9
Source/Urho3D/Graphics/Renderer.cpp

@@ -179,9 +179,9 @@ static const char* shadowVariations[] =
     "",
     ""
     #else
-    "LQSHADOW SHADOWCMP",
-    "LQSHADOW",
-    "SHADOWCMP",
+    "LQSHADOW SHADOWCMP ",
+    "LQSHADOW ",
+    "SHADOWCMP ",
     ""
     #endif
 };
@@ -1164,7 +1164,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     }
 }
 
-void Renderer::SetLightVolumeBatchShaders(Batch& batch, const String& vsName, const String& psName)
+void Renderer::SetLightVolumeBatchShaders(Batch& batch, const String& vsName, const String& psName, const String& vsDefines, const String& psDefines)
 {
     assert(deferredLightPSVariations_.Size());
     
@@ -1202,8 +1202,15 @@ void Renderer::SetLightVolumeBatchShaders(Batch& batch, const String& vsName, co
         psi += DLPS_ORTHO;
     }
     
-    batch.vertexShader_ = graphics_->GetShader(VS, vsName, deferredLightVSVariations[vsi]);
-    batch.pixelShader_ = graphics_->GetShader(PS, psName, deferredLightPSVariations_[psi]);
+    if (vsDefines.Length())
+        batch.vertexShader_ = graphics_->GetShader(VS, vsName, deferredLightVSVariations[vsi] + vsDefines);
+    else
+        batch.vertexShader_ = graphics_->GetShader(VS, vsName, deferredLightVSVariations[vsi]);
+
+    if (psDefines.Length())
+        batch.pixelShader_ = graphics_->GetShader(PS, psName, deferredLightPSVariations_[psi] + psDefines);
+    else
+        batch.pixelShader_ = graphics_->GetShader(PS, psName, deferredLightPSVariations_[psi]);
 }
 
 void Renderer::SetCullMode(CullMode mode, Camera* camera)
@@ -1266,7 +1273,6 @@ void Renderer::OptimizeLightByScissor(Light* light, Camera* camera)
 
 void Renderer::OptimizeLightByStencil(Light* light, Camera* camera)
 {
-    #ifndef GL_ES_VERSION_2_0
     if (light)
     {
         LightType type = light->GetLightType();
@@ -1332,7 +1338,6 @@ void Renderer::OptimizeLightByStencil(Light* light, Camera* camera)
     }
     else
         graphics_->SetStencilTest(false);
-    #endif
 }
 
 const Rect& Renderer::GetLightScissor(Light* light, Camera* camera)
@@ -1466,7 +1471,7 @@ void Renderer::LoadShaders()
         if (i & DLPS_SHADOW)
             deferredLightPSVariations_[i] += shadowVariations[shadows];
         if (i & DLPS_ORTHO)
-            deferredLightPSVariations_[i] += "ORTHO";
+            deferredLightPSVariations_[i] += "ORTHO ";
     }
     
     shadersDirty_ = false;

+ 1 - 1
Source/Urho3D/Graphics/Renderer.h

@@ -313,7 +313,7 @@ public:
     /// Choose shaders for a forward rendering batch.
     void SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows = true);
     /// Choose shaders for a deferred light volume batch.
-    void SetLightVolumeBatchShaders(Batch& batch, const String& vsName, const String& psName);
+    void SetLightVolumeBatchShaders(Batch& batch, const String& vsName, const String& psName, const String& vsDefines, const String& psDefines);
     /// Set cull mode while taking possible projection flipping into account.
     void SetCullMode(CullMode mode, Camera* camera);
     /// Ensure sufficient size of the instancing vertex buffer. Return true if successful.

+ 20 - 9
Source/Urho3D/Graphics/View.cpp

@@ -319,11 +319,18 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
     
     drawDebug_ = viewport->GetDrawDebug();
     hasScenePasses_ = false;
+    lightVolumeCommand_ = 0;
     
     // Make sure that all necessary batch queues exist
     scenePasses_.Clear();
 
-    usingCustomDepth_ = false;
+    noStencil_ = false;
+    #ifdef URHO3D_OPENGL
+    #ifdef GL_ES_VERSION_2_0
+    // On OpenGL ES we assume a stencil is not available or would not give a good performance, and disable light stencil
+    // optimizations in any case
+    noStencil_ = true;
+    #else
     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
     {
         const RenderPathCommand& command = renderPath_->commands_[i];
@@ -331,11 +338,14 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
             continue;
         if (command.depthStencilName_.Length())
         {
-            // Using a readable depth texture will disable automatic stencil use, as can't guarantee a stencil channel is available
-            usingCustomDepth_ = true;
+            // Using a readable depth texture will disable light stencil optimizations on OpenGL, as for compatibility reasons
+            // we are using a depth format without stencil channel
+            noStencil_ = true;
             break;
         }
     }
+    #endif
+    #endif
 
     for (unsigned i = 0; i < renderPath_->commands_.Size(); ++i)
     {
@@ -350,7 +360,7 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
             ScenePassInfo info;
             info.pass_ = command.pass_;
             info.allowInstancing_ = command.sortMode_ != SORT_BACKTOFRONT;
-            info.markToStencil_ = !usingCustomDepth_ && command.markToStencil_;
+            info.markToStencil_ = !noStencil_ && command.markToStencil_;
             info.vertexLights_ = command.vertexLights_;
             
             // Check scenepass metadata for defining custom passes which interact with lighting
@@ -437,8 +447,7 @@ bool View::Define(RenderSurface* renderTarget, Viewport* viewport)
         }
         else if (command.type_ == CMD_LIGHTVOLUMES)
         {
-            lightVolumeVSName_ = command.vertexShaderName_;
-            lightVolumePSName_ = command.pixelShaderName_;
+            lightVolumeCommand_ = &command;
             deferred_ = true;
         }
         else if (command.type_ == CMD_FORWARDLIGHTS)
@@ -1061,7 +1070,9 @@ void View::GetBatches()
                     volumeBatch.material_ = 0;
                     volumeBatch.pass_ = 0;
                     volumeBatch.zone_ = 0;
-                    renderer_->SetLightVolumeBatchShaders(volumeBatch, lightVolumeVSName_, lightVolumePSName_);
+                    renderer_->SetLightVolumeBatchShaders(volumeBatch, lightVolumeCommand_->vertexShaderName_,
+                        lightVolumeCommand_->pixelShaderName_, lightVolumeCommand_->vertexShaderDefines_,
+                        lightVolumeCommand_->pixelShaderDefines_);
                     lightQueue.volumeBatches_.Push(volumeBatch);
                 }
             }
@@ -1532,7 +1543,7 @@ void View::ExecuteRenderPathCommands()
                         if (!i->litBatches_.IsEmpty())
                         {
                             renderer_->OptimizeLightByScissor(i->light_, camera_);
-                            if (!usingCustomDepth_)
+                            if (!noStencil_)
                                 renderer_->OptimizeLightByStencil(i->light_, camera_);
                             i->litBatches_.Draw(this, false, true, allowDepthWrite);
                         }
@@ -2891,7 +2902,7 @@ void View::SetupLightVolumeBatch(Batch& batch)
     }
     
     graphics_->SetScissorTest(false);
-    if (!usingCustomDepth_)
+    if (!noStencil_)
         graphics_->SetStencilTest(true, CMP_NOTEQUAL, OP_KEEP, OP_KEEP, OP_KEEP, 0, light->GetLightMask());
     else
         graphics_->SetStencilTest(false);

+ 4 - 6
Source/Urho3D/Graphics/View.h

@@ -317,8 +317,8 @@ private:
     bool useLitBase_;
     /// Has scene passes flag. If no scene passes, view can be defined without a valid scene or camera to only perform quad rendering.
     bool hasScenePasses_;
-    /// Whether is using a custom readable depth texture.
-    bool usingCustomDepth_;
+    /// Whether is using a custom readable depth texture without a stencil channel.
+    bool noStencil_;
     /// Draw debug geometry flag. Copied from the viewport.
     bool drawDebug_;
     /// Renderpath.
@@ -365,10 +365,8 @@ private:
     StringHash litBasePassName_;
     /// Hash of the litalpha pass.
     StringHash litAlphaPassName_;
-    /// Name of light volume vertex shader.
-    String lightVolumeVSName_;
-    /// Name of light volume pixel shader.
-    String lightVolumePSName_;
+    /// Pointer to the light volume command if any.
+    const RenderPathCommand* lightVolumeCommand_;
 };
 
 }