Переглянути джерело

Restored the optional lit base pass optimization, but with the limitation of no ambient gradient.
Removed unneeded GLSL shader permutations.

Lasse Öörni 14 роки тому
батько
коміт
c4e569879d

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

@@ -1,5 +1,6 @@
 <technique>
     <pass name="base" vs="Ambient" ps="Ambient_Diff" />
+    <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" />

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

@@ -1,5 +1,6 @@
 <technique>
     <pass name="base" vs="Ambient" ps="Ambient_Diff" alphatest="true" />
+    <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" />

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

@@ -1,5 +1,6 @@
 <technique>
     <pass name="base" vs="Ambient" ps="Ambient_Diff" />
+    <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" />

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

@@ -1,5 +1,6 @@
 <technique>
     <pass name="base" vs="Ambient" ps="Ambient_Diff" alphatest="true" />
+    <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" />

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

@@ -1,5 +1,6 @@
 <technique>
     <pass name="base" vs="Ambient" ps="Ambient" />
+    <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" />

+ 7 - 4
Docs/Reference.dox

@@ -538,7 +538,7 @@ A technique definition looks like this:
 
 \code
 <technique>
-    <pass name="base|light|prealpha|postalpha|gbuffer|material|shadow" vs="VertexShaderName" ps="PixelShaderName"
+    <pass name="base|litbase|light|prealpha|postalpha|gbuffer|material|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 ... />
@@ -549,17 +549,20 @@ A technique definition looks like this:
 The passes are:
 
 - base: renders the ambient light, per-vertex lights and fog.
+- litbase: renders the first per-pixel light, ambient light and fog. This is an optional pass for optimization.
 - 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 prepare for opaque geometry light accumulation.
 - material: (light pre-pass only) renders the opaque geometry final color by combining ambient light, per-vertex lights and per-pixel light accumulation.
-- shadow: shadow map rendering pass. Renders depth only.
+- 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.
 
 Note that the technique does not need to enumerate shaders used for different geometry types (non-skinned, skinned, instanced, billboard) and different per-vertex and per-pixel light combinations. Instead specific hardcoded shader variations are assumed to exist. See the files Ambient.xml and ForwardLit.xml in either SourceAssets/HLSLShaders or SourceAssets/GLSLShaders to see which variations are required.
 
+The optional "litbase" pass reduces draw call count by combining ambient light rendering with the first per-pixel light. However, it has intentional limitations to not require too many shader permutations: there must be no vertex lights affecting the object, and the ambient lighting can not have a gradient. In case of excessive overdraw, it is likely better to not define it, but instead allow the computationally light ambient-only pass to initialize the Z buffer for later passes.
+
 
 \page Lights Lights and shadows
 
@@ -1129,11 +1132,11 @@ Like with ordinary events, in script event types are strings instead of name has
 
 Urho3D uses a task-based multithreading model. The WorkQueue subsystem can be supplied with tasks described by the WorkItem structure, by calling \ref WorkQueue::AddWorkItem "AddWorkItem()". These will be executed in background worker threads. The function \ref WorkQueue::Complete "Complete()" will complete all currently pending tasks, and execute them also in the main thread to make them finish faster.
 
-On single-core systems no worker threads will be created. In the presence of more cores, a worker thread will be created for each hardware core except one which is reserved for the main thread. Hyperthreaded cores are not included, as creating worker threads also for them leads to unpredictable extra synchronization overhead.
+On single-core systems no worker threads will be created, and tasks are immediately processed by the main thread instead. In the presence of more cores, a worker thread will be created for each hardware core except one which is reserved for the main thread. Hyperthreaded cores are not included, as creating worker threads also for them leads to unpredictable extra synchronization overhead.
 
 The work items include a function pointer to call, with the signature "void WorkFunction(const WorkItem* item, unsigned threadIndex)." The thread index ranges from 0 to n, where 0 represents the main thread and n is the number of worker threads created. Its function is to aid in splitting work into per-thread data structures that need no locking. The work item also contains three void pointers: start, end and aux, which can be used to describe a range of sub-work items, and an auxiliary data structure, which may for example be the object that originally queued the work.
 
-Multithreading is so far not exposed to scripts, and is currently used only in a limited manner: to speed up the preparation of rendering views, including lit object and shadow caster queries, occlusion tests, particle system updates and animation and skinning updates. Raycasts into the Octree are also threaded, but physics raycasts are not.
+Multithreading is so far not exposed to scripts, and is currently used only in a limited manner: to speed up the preparation of rendering views, including lit object and shadow caster queries, occlusion tests and particle system, animation and skinning updates. Raycasts into the Octree are also threaded, but physics raycasts are not.
 
 Note that as the Profiler currently manages only a single hierarchy tree, profiling blocks may only appear in main thread code, not in the work functions.
 

+ 5 - 2
Engine/Graphics/Batch.cpp

@@ -271,7 +271,7 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
     if (zone_)
     {
         if (graphics->NeedParameterUpdate(VSP_AMBIENTSTARTCOLOR, zone_))
-            graphics->SetShaderParameter(VSP_AMBIENTSTARTCOLOR, zone_->GetAmbientStartColor().ToVector4());
+            graphics->SetShaderParameter(VSP_AMBIENTSTARTCOLOR, zone_->GetAmbientStartColor());
         if (graphics->NeedParameterUpdate(VSP_AMBIENTENDCOLOR, zone_))
             graphics->SetShaderParameter(VSP_AMBIENTENDCOLOR, zone_->GetAmbientEndColor().ToVector4() - zone_->GetAmbientStartColor().ToVector4());
         
@@ -287,11 +287,14 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
             graphics->SetShaderParameter(VSP_ZONE, zoneTransform);
         }
         
+        if (graphics->NeedParameterUpdate(PSP_AMBIENTCOLOR, zone_))
+            graphics->SetShaderParameter(PSP_AMBIENTCOLOR, zone_->GetAmbientColor());
+        
         // If the pass is additive, override fog color to black so that shaders do not need a separate additive path
         BlendMode blend = pass_->GetBlendMode();
         Zone* fogColorZone = (blend == BLEND_ADD || blend == BLEND_ADDALPHA) ? renderer->GetDefaultZone() : zone_;
         if (graphics->NeedParameterUpdate(PSP_FOGCOLOR, fogColorZone))
-            graphics->SetShaderParameter(PSP_FOGCOLOR, fogColorZone->GetFogColor().ToVector4());
+            graphics->SetShaderParameter(PSP_FOGCOLOR, fogColorZone->GetFogColor());
         
         if (graphics->NeedParameterUpdate(PSP_FOGPARAMS, zone_))
         {

+ 6 - 0
Engine/Graphics/Drawable.cpp

@@ -42,6 +42,7 @@ Drawable::Drawable(Context* context) :
     octant_(0),
     viewFrame_(0),
     viewCamera_(0),
+    firstLight_(0),
     drawDistance_(0.0f),
     shadowDistance_(0.0f),
     lodBias_(1.0f),
@@ -54,6 +55,7 @@ Drawable::Drawable(Context* context) :
     lodDistance_(0.0f),
     sortValue_(0.0f),
     viewFrameNumber_(0),
+    basePassFlags_(0),
     drawableFlags_(0),
     visible_(true),
     castShadows_(false),
@@ -214,12 +216,16 @@ void Drawable::MarkInView(const FrameInfo& frame, bool mainView)
 
 void Drawable::ClearLights()
 {
+    basePassFlags_ = 0;
+    firstLight_ = 0;
     lights_.Clear();
     vertexLights_.Clear();
 }
 
 void Drawable::AddLight(Light* light)
 {
+    if (lights_.Empty())
+        firstLight_ = light;
     lights_.Push(light);
 }
 

+ 9 - 1
Engine/Graphics/Drawable.h

@@ -180,7 +180,7 @@ public:
     /// Sort and limit per-vertex lights to maximum allowed.
     void LimitVertexLights();
     /// %Set base pass flag for a batch.
-    void SetBasePass(unsigned batchIndex);
+    void SetBasePass(unsigned batchIndex) { basePassFlags_ |= (1 << batchIndex); }
     /// Return octree octant.
     Octant* GetOctant() const { return octant_; }
     /// Return current zone.
@@ -197,10 +197,14 @@ public:
     bool IsInView(unsigned frameNumber) const { return viewFrameNumber_ == frameNumber; }
     /// Return whether is visible in a specific view this frame.
     bool IsInView(const FrameInfo& frame, bool mainView = true) const { return viewFrameNumber_ == frame.frameNumber_ && viewFrame_ == &frame && (!mainView || viewCamera_ == frame.camera_); }
+    /// Return whether has a base pass.
+    bool HasBasePass(unsigned batchIndex) const { return (basePassFlags_ & (1 << batchIndex)) != 0; }
     /// Return per-pixel lights.
     const PODVector<Light*>& GetLights() const { return lights_; }
     /// Return per-vertex lights.
     const PODVector<Light*>& GetVertexLights() const { return vertexLights_; }
+    /// Return the first added per-pixel light.
+    Light* GetFirstLight() const { return firstLight_; }
     
 protected:
     /// Handle node being assigned.
@@ -228,6 +232,8 @@ protected:
     PODVector<Light*> lights_;
     /// Per-vertex lights affecting this drawable.
     PODVector<Light*> vertexLights_;
+    /// First per-pixel light added this frame.
+    Light* firstLight_;
     /// Current zone.
     WeakPtr<Zone> zone_;
     /// Previous zone.
@@ -256,6 +262,8 @@ protected:
     float sortValue_;
     /// Last visible frame number.
     unsigned viewFrameNumber_;
+    /// Base pass flags.
+    unsigned basePassFlags_;
     /// Drawable flags.
     unsigned char drawableFlags_;
     /// Visible flag.

+ 1 - 0
Engine/Graphics/GraphicsDefs.cpp

@@ -45,6 +45,7 @@ StringHash VSP_ZONE("Zone");
 StringHash VSP_LIGHTMATRICES("LightMatrices");
 StringHash VSP_SKINMATRICES("SkinMatrices");
 StringHash VSP_VERTEXLIGHTS("VertexLights");
+StringHash PSP_AMBIENTCOLOR("AmbientColor");
 StringHash PSP_DEPTHRECONSTRUCT("DepthReconstruct");
 StringHash PSP_FOGCOLOR("FogColor");
 StringHash PSP_FOGPARAMS("FogParams");

+ 2 - 0
Engine/Graphics/GraphicsDefs.h

@@ -166,6 +166,7 @@ enum TextureUsage
 enum PassType
 {
     PASS_BASE,
+    PASS_LITBASE,
     PASS_LIGHT,
     PASS_PREALPHA,
     PASS_POSTALPHA,
@@ -214,6 +215,7 @@ extern StringHash VSP_ZONE;
 extern StringHash VSP_LIGHTMATRICES;
 extern StringHash VSP_SKINMATRICES;
 extern StringHash VSP_VERTEXLIGHTS;
+extern StringHash PSP_AMBIENTCOLOR;
 extern StringHash PSP_DEPTHRECONSTRUCT;
 extern StringHash PSP_FOGCOLOR;
 extern StringHash PSP_FOGPARAMS;

+ 7 - 2
Engine/Graphics/Light.cpp

@@ -456,10 +456,15 @@ void Light::OnWorldBoundingBoxUpdate()
 
 void Light::SetIntensitySortValue(float distance)
 {
+    // When sorting lights globally, give priority to directional lights so that they will be combined into the ambient pass
     if (lightType_ != LIGHT_DIRECTIONAL)
-        sortValue_ = M_EPSILON / (color_.Intensity() + M_EPSILON);
+        sortValue_ = Max(distance, M_MIN_NEARCLIP) / (color_.Intensity() + M_EPSILON);
     else
-        sortValue_ = distance / (color_.Intensity() + M_EPSILON);
+        sortValue_ = M_EPSILON / (color_.Intensity() + M_EPSILON);
+    
+    // Additionally, give priority to vertex lights so that vertex light base passes can be determined before per pixel lights
+    if (perVertex_)
+        sortValue_ -= M_LARGE_VALUE;
 }
 
 void Light::SetIntensitySortValue(const BoundingBox& box)

+ 3 - 2
Engine/Graphics/Renderer.cpp

@@ -1014,7 +1014,7 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* technique, Pass* pass, b
         
         //  Check whether is a pixel lit forward pass. If not, there is only one pixel shader
         PassType type = pass->GetType();
-        if (type == PASS_LIGHT)
+        if (type == PASS_LIGHT || type == PASS_LITBASE)
         {
             LightBatchQueue* lightQueue = batch.lightQueue_;
             if (!lightQueue)
@@ -1309,6 +1309,7 @@ void Renderer::LoadMaterialShaders(Technique* technique)
     else
     {
         LoadPassShaders(technique, PASS_BASE);
+        LoadPassShaders(technique, PASS_LITBASE);
         LoadPassShaders(technique, PASS_LIGHT);
     }
     
@@ -1359,7 +1360,7 @@ void Renderer::LoadPassShaders(Technique* technique, PassType type, bool allowSh
     vertexShaders.Clear();
     pixelShaders.Clear();
     
-    if (type == PASS_LIGHT)
+    if (type == PASS_LIGHT || type == PASS_LITBASE)
     {
         // Load forward pixel lit variations. If material is transparent, and shadow maps are reused,
         // do not load shadowed variations

+ 1 - 0
Engine/Graphics/Technique.cpp

@@ -34,6 +34,7 @@
 static const String passNames[] =
 {
     "base",
+    "litbase",
     "light",
     "prealpha",
     "postalpha",

+ 25 - 1
Engine/Graphics/View.cpp

@@ -726,7 +726,12 @@ void View::GetBatches()
                 
                 // Next check for forward base pass
                 if (!pass)
+                {
+                    // Skip if a lit base pass already exists
+                    if (j < 32 && drawable->HasBasePass(j))
+                        continue;
                     pass = tech->GetPass(PASS_BASE);
+                }
                 
                 if (pass)
                 {
@@ -867,9 +872,12 @@ void View::UpdateGeometries()
 void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
 {
     Light* light = lightQueue.light_;
+    Light* firstLight = drawable->GetFirstLight();
+    Zone* zone = GetZone(drawable);
     // Shadows on transparencies can only be rendered if shadow maps are not reused
     bool allowTransparentShadows = !renderer_->GetReuseShadowMaps();
     bool hasVertexLights = drawable->GetVertexLights().Size() > 0;
+    bool hasAmbientGradient = zone->GetAmbientGradient() && zone->GetAmbientStartColor() != zone->GetAmbientEndColor();
     bool prepass = renderer_->GetLightPrepass();
     unsigned numBatches = drawable->GetNumBatches();
     
@@ -886,7 +894,23 @@ void View::GetLitBatches(Drawable* drawable, LightBatchQueue& lightQueue)
         if (prepass && tech->HasPass(PASS_GBUFFER))
             continue;
         
-        Pass* pass = tech->GetPass(PASS_LIGHT);
+        Pass* pass = 0;
+        
+        // Check for lit base pass. Because it uses the replace blend mode, it must be ensured to be the first light
+        // Also vertex lighting or ambient gradient require the non-lit base pass, so skip in those cases
+        if (i < 32 && light == firstLight && !hasVertexLights && !hasAmbientGradient && !drawable->HasBasePass(i))
+        {
+            pass = tech->GetPass(PASS_LITBASE);
+            if (pass)
+            {
+                litBatch.isBase_ = true;
+                drawable->SetBasePass(i);
+            }
+        }
+        
+        // If no lit base pass, get ordinary light pass
+        if (!pass)
+            pass = tech->GetPass(PASS_LIGHT);
         // Skip if material does not receive light at all
         if (!pass)
             continue;

+ 0 - 1
SourceAssets/GLSLShaders/Ambient.xml

@@ -11,7 +11,6 @@
         <option name="" /> <!-- Dummy option to separate the two variation groups -->
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" require="SM3" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     <shader name="Ambient" type="ps">

+ 6 - 1
SourceAssets/GLSLShaders/ForwardLit.frag

@@ -95,5 +95,10 @@ void main()
         finalColor = diff * lightColor * diffColor.rgb;
     #endif
 
-    gl_FragColor = vec4(GetLitFog(finalColor, vTexCoord.z), diffColor.a);
+    #ifdef AMBIENT
+        finalColor += cAmbientColor * diffColor.rgb;
+        gl_FragColor = vec4(GetFog(finalColor, vTexCoord.z), diffColor.a);
+    #else
+        gl_FragColor = vec4(GetLitFog(finalColor, vTexCoord.z), diffColor.a);
+    #endif
 }

+ 1 - 1
SourceAssets/GLSLShaders/ForwardLit.xml

@@ -9,7 +9,6 @@
         <option name="Shadow" define="SHADOW" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     <shader name="ForwardLit" type="ps">
@@ -17,6 +16,7 @@
         <option name="Normal" define="NORMALMAP" require="DIFFMAP" />
         <option name="SpecMap" define="SPECMAP" require="DIFFMAP" />
         <option name="VCol" define="VERTEXCOLOR" />
+        <option name="Ambient" define="AMBIENT" />
         <variation name="Dir" define="DIRLIGHT" />
         <variation name="Spot" define="SPOTLIGHT" />
         <variation name="Point" define="POINTLIGHT" />

+ 0 - 1
SourceAssets/GLSLShaders/GBuffer.xml

@@ -4,7 +4,6 @@
         <option name="HW" define="HWDEPTH" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" />
     </shader>
     <shader name="GBuffer" type="ps">
         <option name="Normal" define="NORMALMAP" />

+ 1 - 1
SourceAssets/GLSLShaders/LitParticle.frag

@@ -50,7 +50,7 @@ void main()
     finalColor = diff * lightColor * diffColor.rgb;
     
     #ifdef AMBIENT
-        finalColor += GetAmbient(vZonePosDepth.x) * diffColor.rgb;
+        finalColor += cAmbientColor * diffColor.rgb;
         gl_FragColor = vec4(GetFog(finalColor, vZonePosDepth.y), diffColor.a);
     #else
         gl_FragColor = vec4(GetLitFog(finalColor, vZonePosDepth.y), diffColor.a);

+ 0 - 1
SourceAssets/GLSLShaders/LitParticle.xml

@@ -7,7 +7,6 @@
         <option name="" /> <!-- Dummy option to separate the two variation groups -->
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     <shader name="LitParticle" type="ps">

+ 0 - 1
SourceAssets/GLSLShaders/Material.xml

@@ -10,7 +10,6 @@
         <option name="" /> <!-- Dummy option to separate the two variation groups -->
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" />
     </shader>
     <shader name="Material" type="ps">
         <option name="Diff" define="DIFFMAP" />

+ 1 - 0
SourceAssets/GLSLShaders/Uniforms.frag

@@ -1,3 +1,4 @@
+uniform vec3 cAmbientColor;
 uniform vec2 cDepthReconstruct;
 uniform vec4 cFogParams;
 uniform vec3 cFogColor;

+ 0 - 1
SourceAssets/GLSLShaders/Unlit.xml

@@ -3,7 +3,6 @@
         <option name="VCol" define="VERTEXCOLOR" />
         <variation name="" />
         <variation name="Skinned" define="SKINNED" />
-        <variation name="Instanced" define="INSTANCED" />
         <variation name="Billboard" define="BILLBOARD" />
     </shader>
     <shader name="Unlit" type="ps">

+ 6 - 1
SourceAssets/HLSLShaders/ForwardLit.hlsl

@@ -202,5 +202,10 @@ void PS(float3 iTexCoord : TEXCOORD0,
         finalColor = diff * lightColor * diffColor.rgb;
     #endif
 
-    oColor = float4(GetLitFog(finalColor, iTexCoord.z), diffColor.a);
+    #ifdef AMBIENT
+        finalColor += cAmbientColor * diffColor.rgb;
+        oColor = float4(GetFog(finalColor, iTexCoord.z), diffColor.a);
+    #else
+        oColor = float4(GetLitFog(finalColor, iTexCoord.z), diffColor.a);
+    #endif
 }

+ 1 - 0
SourceAssets/HLSLShaders/ForwardLit.xml

@@ -17,6 +17,7 @@
         <option name="Normal" define="NORMALMAP" require="DIFFMAP" />
         <option name="SpecMap" define="SPECMAP" require="DIFFMAP" />
         <option name="VCol" define="VERTEXCOLOR" />
+        <option name="Ambient" define="AMBIENT" />
         <variation name="Dir" define="DIRLIGHT" />
         <variation name="Spot" define="SPOTLIGHT" />
         <variation name="Point" define="POINTLIGHT" />

+ 7 - 1
SourceAssets/HLSLShaders/LitParticle.hlsl

@@ -102,5 +102,11 @@ void PS(float3 iTexCoord : TEXCOORD0,
     #endif
 
     finalColor = diff * lightColor * diffColor.rgb;
-    oColor = float4(GetLitFog(finalColor, iTexCoord.z), diffColor.a);
+    
+    #ifdef AMBIENT
+        finalColor += cAmbientColor * diffColor.rgb;
+        oColor = float4(GetFog(finalColor, iTexCoord.z), diffColor.a);
+    #else
+        oColor = float4(GetLitFog(finalColor, iTexCoord.z), diffColor.a);
+    #endif
 }

+ 1 - 0
SourceAssets/HLSLShaders/LitParticle.xml

@@ -13,6 +13,7 @@
     <shader name="LitParticle" type="ps">
         <option name="Diff" define="DIFFMAP" />
         <option name="VCol" define="VERTEXCOLOR" />
+        <option name="Ambient" define="AMBIENT" />
         <variation name="Dir" define="DIRLIGHT" />
         <variation name="Spot" define="SPOTLIGHT" />
         <variation name="Point" define="POINTLIGHT" />

+ 16 - 15
SourceAssets/HLSLShaders/Uniforms.hlsl

@@ -20,18 +20,19 @@ uniform float4x3 cSkinMatrices[64] : register(C41);
 uniform float4 cVertexLights[6*3] : register(C233);
 
 // Pixel shader parameters
-uniform float2 cDepthReconstruct : register(C0);
-uniform float4 cFogParams : register(C1);
-uniform float3 cFogColor : register(C2);
-uniform float4 cLightColor : register(C3);
-uniform float4 cLightPosPS : register(C4);
-uniform float3 cLightDirPS : register(C5);
-uniform float4 cMatDiffColor : register(C6);
-uniform float3 cMatEmissiveColor : register(C7);
-uniform float2 cMatSpecProperties : register(C8);
-uniform float2 cSampleOffsets : register(C9);
-uniform float4 cShadowCubeAdjust : register(C10);
-uniform float4 cShadowDepthFade : register(C11);
-uniform float4 cShadowIntensity : register(C12);
-uniform float4 cShadowSplits : register(C13);
-uniform float4x4 cLightMatricesPS[4] :  register(C14);
+uniform float3 cAmbientColor : register(C0);
+uniform float2 cDepthReconstruct : register(C1);
+uniform float4 cFogParams : register(C2);
+uniform float3 cFogColor : register(C3);
+uniform float4 cLightColor : register(C4);
+uniform float4 cLightPosPS : register(C5);
+uniform float3 cLightDirPS : register(C6);
+uniform float4 cMatDiffColor : register(C7);
+uniform float3 cMatEmissiveColor : register(C8);
+uniform float2 cMatSpecProperties : register(C9);
+uniform float2 cSampleOffsets : register(C10);
+uniform float4 cShadowCubeAdjust : register(C11);
+uniform float4 cShadowDepthFade : register(C12);
+uniform float4 cShadowIntensity : register(C13);
+uniform float4 cShadowSplits : register(C14);
+uniform float4x4 cLightMatricesPS[4] :  register(C15);