浏览代码

Added height fog mode boolean to Zone. Apply height fog shaders automatically. Changed height fog parameters to be relative to the zone node.

Lasse Öörni 12 年之前
父节点
当前提交
b76823a317

+ 13 - 12
Docs/Reference.dox

@@ -909,7 +909,7 @@ To control whether the backbuffer should use sRGB conversion on write, call \ref
 A technique definition looks like this:
 
 \code
-<technique vs="VertexShaderName" ps="VertexShaderName" vsdefines="DEFINE1 DEFINE2" psdefines="DEFINE3 DEFINE4" sm3="false|true" >
+<technique vs="VertexShaderName" ps="PixelShaderName" vsdefines="DEFINE1 DEFINE2" psdefines="DEFINE3 DEFINE4" sm3="false|true" >
     <pass name="base|litbase|light|alpha|litalpha|postopaque|refract|postalpha|prepass|material|deferred|depth|shadow"
         vs="VertexShaderName" ps="PixelShaderName" vsdefines="DEFINE1 DEFINE2" psdefines="DEFINE3 DEFINE4"
         lighting="unlit|pervertex|perpixel"
@@ -922,7 +922,13 @@ A technique definition looks like this:
 </technique>
 \endcode
 
-The "sm3" attribute in the technique root element determines whether the technique requires %Shader %Model 3 hardware.
+The sm3 attribute in the technique root element allows the technique to specify it requires %Shader %Model 3 hardware. Omitting it is same as specifying false (works on both SM2 & 3.)
+
+Shaders are referred to by giving the name of a shader without path and file extension. For example "Basic" or "LitSolid". The engine will add the correct path and file extension (Shaders/HLSL/LitSolid.hlsl for Direct3D9, and Shaders/GLSL/LitSolid.glsl for OpenGL) automatically. The same shader source file contains both the vertex and pixel shader. In addition, compilation defines can be specified, which are passed to the shader compiler. For example the define "DIFFMAP" typically enables diffuse mapping in the pixel shader.
+
+Shaders and their compilation defines can be specified on both the technique and pass level. If a pass does not override the default shaders specified on the technique level, it still can specify additional compilation defines to be used. However, if a pass overrides the shaders, then the technique-level defines are not used.
+
+The technique definition 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 the engine will add certain hardcoded compilation defines for these. See \ref Shaders "Shaders" for details.
 
 The purposes of the different passes are:
 
@@ -948,15 +954,6 @@ The optional "litbase" pass reduces draw call count by combining ambient lightin
 
 The refract pass requires pingponging the scene rendertarget to a texture, but this will not be performed if there is no refractive geometry to render, so there is no unnecessary cost to it.
 
-\section Materials_TechniqueShaders Specifying shaders
-
-Shaders are referred to by giving the name of a shader without path and file extension. For example "Basic" or "LitSolid". The engine will add the correct path and file extension (Shaders/HLSL/LitSolid.hlsl for Direct3D9, and Shaders/GLSL/LitSolid.glsl for OpenGL) automatically. The same shader source file contains both the vertex and pixel shader. In addition, compilation defines can be specified, which are passed to the shader compiler. For example the define "DIFFMAP" typically enables diffuse mapping in the pixel shader.
-
-Shaders and their compilation defines can be specified on both the technique and pass level. If a pass does not override the default shaders specified on the technique level, it still can specify additional compilation defines to be used. However, if a pass overrides the shaders, then the technique-level defines are not used.
-
-The technique definition 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 the engine will add certain hardcoded compilation defines for these. See \ref Shaders "Shaders" for details.
-
-
 \page Shaders Shaders
 
 Urho3D uses an ubershader-like approach: permutations of each shader will be built with different compilation defines, to produce eg. static or skinned, deferred or forward or shadowed/unshadowed rendering.
@@ -1254,7 +1251,11 @@ A Zone controls ambient lighting and fogging. Each geometry object determines th
 
 The viewport will be initially cleared to the fog color of the zone found at the camera's far clip distance. If no zone is found either for the far clip or an object, a default zone with black ambient and fog color will be used.
 
-Zones have two special flags: override mode and ambient gradient. If the camera is inside a zone with override mode enabled, all rendered objects will use that zone's ambient and fog settings, instead of the zone they belong to. This can be used for example to implement an underwater effect. When ambient gradient mode is enabled, the zone's own ambient color value is not used, but instead it will look for two highest-priority neighbor zones that touch it at the minimum and maximum Z face of its oriented bounding box: any objects inside will then get a per-vertex ambient color fade between the neighbor zones' ambient colors.
+Zones have three special flags: height fog mode, override mode and ambient gradient. 
+
+- When height fog mode is enabled, objects inside the zone receive height fog in addition of distance fog. The fog's \ref Zone::SetFogHeight "height level" is specified relative to the zone's world position. The width of the fog band on the Y-axis is specified by the \ref Zone::SetFogHeightScale "fog height scale" parameter.
+- If the camera is inside a zone with override mode enabled, all rendered objects will use that zone's ambient and fog settings, instead of the zone they belong to. This can be used for example to implement an underwater effect.
+- When ambient gradient mode is enabled, the zone's own ambient color value is not used, but instead it will look for two highest-priority neighbor zones that touch it at the minimum and maximum Z face of its oriented bounding box: any objects inside will then get a per-vertex ambient color fade between the neighbor zones' ambient colors.
 
 Zones also define a lightmask and a shadowmask (with all bits set by default.) An object's final lightmask for light culling is determined by ANDing the object lightmask and the zone lightmask. The final shadowmask is also calculated in the same way.
 

+ 9 - 8
Source/Engine/Graphics/Batch.cpp

@@ -338,18 +338,19 @@ void Batch::Prepare(View* view, bool setModelTransform) const
         float farClip = camera_->GetFarClip();
         float fogStart = Min(zone_->GetFogStart(), farClip);
         float fogEnd = Min(zone_->GetFogEnd(), farClip);
-        float fogHeight = zone_->GetFogHeight();
-        float fogHeightScale = zone_->GetFogHeightScale();
-
         if (fogStart >= fogEnd * (1.0f - M_LARGE_EPSILON))
             fogStart = fogEnd * (1.0f - M_LARGE_EPSILON);
         float fogRange = Max(fogEnd - fogStart, M_EPSILON);
-
-        float fogHeightY = box.min_.y_;
-        if (fogHeight < 0.0f)
-            fogHeightY = box.max_.y_;
+        Vector4 fogParams(fogEnd / farClip, farClip / fogRange, 0.0f, 0.0f);
+        
+        Node* zoneNode = zone_->GetNode();
+        if (zone_->GetHeightFog() && zoneNode)
+        {
+            Vector3 worldFogHeightVec = zoneNode->GetWorldTransform() * Vector3(0.0f, zone_->GetFogHeight(), 0.0f);
+            fogParams.z_ = worldFogHeightVec.y_;
+            fogParams.w_ = zone_->GetFogHeightScale() / zoneNode->GetWorldScale().y_;
+        }
         
-        Vector4 fogParams(fogEnd / farClip, farClip / fogRange, fogHeightY + fogHeight, fogHeightScale);
         graphics->SetShaderParameter(PSP_FOGPARAMS, fogParams);
     }
     

+ 54 - 43
Source/Engine/Graphics/Renderer.cpp

@@ -249,6 +249,12 @@ static const char* lightPSVariations[] =
     "PERPIXEL POINTLIGHT CUBEMASK SPECULAR SHADOW "
 };
 
+static const char* heightFogVariations[] =
+{
+    "",
+    "HEIGHTFOG "
+};
+
 static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3;
 static const unsigned MAX_BUFFER_AGE = 1000;
 
@@ -1065,6 +1071,8 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
     // Make sure shaders are loaded now
     if (vertexShaders.Size() && pixelShaders.Size())
     {
+        bool heightFog = graphics_->GetSM3Support() && batch.zone_ && batch.zone_->GetHeightFog();
+        
         // If instancing is not supported, but was requested, or the object is too large to be instanced,
         // choose static geometry vertex shader instead
         if (batch.geometryType_ == GEOM_INSTANCED && (!GetDynamicInstancing() || batch.geometry_->GetIndexCount() >
@@ -1123,24 +1131,14 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
                 break;
             }
             
-            batch.vertexShader_ = vertexShaders[vsi];
-            batch.pixelShader_ = pixelShaders[psi];
-            
-            // If shadow or specular variations do not exist, try without them
-            if ((!batch.vertexShader_ || !batch.pixelShader_) && ((vsi % MAX_LIGHT_VS_VARIATIONS) >= LVS_SHADOW))
-            {
-                vsi -= LVS_SHADOW;
-                psi -= LPS_SHADOW;
-                batch.vertexShader_ = vertexShaders[vsi];
-                batch.pixelShader_ = pixelShaders[psi];
-            }
-            if ((!batch.vertexShader_ || !batch.pixelShader_) && ((vsi % MAX_LIGHT_VS_VARIATIONS) >= LVS_SPEC))
+            if (heightFog)
             {
-                vsi -= LVS_SPEC;
-                psi -= LPS_SPEC;
-                batch.vertexShader_ = vertexShaders[vsi];
-                batch.pixelShader_ = pixelShaders[psi];
+                vsi += MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS;
+                psi += MAX_LIGHT_PS_VARIATIONS;
             }
+            
+            batch.vertexShader_ = vertexShaders[vsi];
+            batch.pixelShader_ = pixelShaders[psi];
         }
         else
         {
@@ -1152,21 +1150,21 @@ void Renderer::SetBatchShaders(Batch& batch, Technique* tech, bool allowShadows)
                     numVertexLights = batch.lightQueue_->vertexLights_.Size();
                 
                 unsigned vsi = batch.geometryType_ * MAX_VERTEXLIGHT_VS_VARIATIONS + numVertexLights;
+                if (heightFog)
+                    vsi += MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS;
+                
                 batch.vertexShader_ = vertexShaders[vsi];
-                // If vertex lights variations do not exist, try without them
-                if (!batch.vertexShader_)
-                {
-                    unsigned vsi = batch.geometryType_ * MAX_VERTEXLIGHT_VS_VARIATIONS;
-                    batch.vertexShader_ = vertexShaders[vsi];
-                }
             }
             else
             {
                 unsigned vsi = batch.geometryType_;
+                if (heightFog)
+                    vsi += MAX_GEOMETRYTYPES;
+                
                 batch.vertexShader_ = vertexShaders[vsi];
             }
             
-            batch.pixelShader_ = pixelShaders[0];
+            batch.pixelShader_ = pixelShaders[heightFog ? 1 : 0];
         }
     }
     
@@ -1518,26 +1516,31 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
     if (pass->GetLightingMode() == LIGHTING_PERPIXEL)
     {
         // Load forward pixel lit variations
-        vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
-        pixelShaders.Resize(MAX_LIGHT_PS_VARIATIONS);
+        vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS * 2);
+        pixelShaders.Resize(MAX_LIGHT_PS_VARIATIONS * 2);
         
-        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS; ++j)
+        for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS * 2; ++j)
         {
-            unsigned g = j / MAX_LIGHT_VS_VARIATIONS;
-            unsigned l = j % MAX_LIGHT_VS_VARIATIONS;
+            unsigned k = j % (MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
+            unsigned h = j / (MAX_GEOMETRYTYPES * MAX_LIGHT_VS_VARIATIONS);
+            unsigned g = k / MAX_LIGHT_VS_VARIATIONS;
+            unsigned l = k % MAX_LIGHT_VS_VARIATIONS;
+            
             vertexShaders[j] = GetShader(VS, pass->GetVertexShader(), Shader::SanitateDefines(pass->GetVertexShaderDefines() + " " +
-                lightVSVariations[l] + geometryVSVariations[g]));
+                lightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]));
         }
-        for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS; ++j)
+        for (unsigned j = 0; j < MAX_LIGHT_PS_VARIATIONS * 2; ++j)
         {
-            if (j & LPS_SHADOW)
+            unsigned k = j % MAX_LIGHT_PS_VARIATIONS;
+            unsigned h = j / MAX_LIGHT_PS_VARIATIONS;
+            if (k & LPS_SHADOW)
             {
                 pixelShaders[j] = GetShader(PS, pass->GetPixelShader(), Shader::SanitateDefines(pass->GetPixelShaderDefines() +
-                    " " + lightPSVariations[j] + shadowVariations[shadows]));
+                    " " + lightPSVariations[k] + shadowVariations[shadows] + heightFogVariations[h]));
             }
             else
                 pixelShaders[j] = GetShader(PS, pass->GetPixelShader(), Shader::SanitateDefines(pass->GetPixelShaderDefines() +
-                    " " + lightPSVariations[j]));
+                    " " + lightPSVariations[k] + heightFogVariations[h]));
         }
     }
     else
@@ -1545,27 +1548,35 @@ void Renderer::LoadPassShaders(Technique* tech, StringHash type)
         // Load vertex light variations
         if (pass->GetLightingMode() == LIGHTING_PERVERTEX)
         {
-            vertexShaders.Resize(MAX_VERTEXLIGHT_VS_VARIATIONS * MAX_GEOMETRYTYPES);
-            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS; ++j)
+            vertexShaders.Resize(MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS * 2);
+            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS * 2; ++j)
             {
-                unsigned g = j / MAX_VERTEXLIGHT_VS_VARIATIONS;
-                unsigned l = j % MAX_VERTEXLIGHT_VS_VARIATIONS;
+                unsigned k = j % (MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
+                unsigned h = j / (MAX_GEOMETRYTYPES * MAX_VERTEXLIGHT_VS_VARIATIONS);
+                unsigned g = k / MAX_VERTEXLIGHT_VS_VARIATIONS;
+                unsigned l = k % MAX_VERTEXLIGHT_VS_VARIATIONS;
                 vertexShaders[j] = GetShader(VS, pass->GetVertexShader(), Shader::SanitateDefines(pass->GetVertexShaderDefines() +
-                    " " + vertexLightVSVariations[l] + geometryVSVariations[g]));
+                    " " + vertexLightVSVariations[l] + geometryVSVariations[g] + heightFogVariations[h]));
             }
         }
         else
         {
-            vertexShaders.Resize(MAX_GEOMETRYTYPES);
-            for (unsigned j = 0; j < MAX_GEOMETRYTYPES; ++j)
+            vertexShaders.Resize(MAX_GEOMETRYTYPES * 2);
+            for (unsigned j = 0; j < MAX_GEOMETRYTYPES * 2; ++j)
             {
+                unsigned k = j % MAX_GEOMETRYTYPES;
+                unsigned h = j / MAX_GEOMETRYTYPES;
                 vertexShaders[j] = GetShader(VS, pass->GetVertexShader(), Shader::SanitateDefines(pass->GetVertexShaderDefines() +
-                    " " + geometryVSVariations[j]));
+                    " " + geometryVSVariations[k] + heightFogVariations[h]));
             }
         }
         
-        pixelShaders.Resize(1);
-        pixelShaders[0] = GetShader(PS, pass->GetPixelShader(), Shader::SanitateDefines(pass->GetPixelShaderDefines()));
+        pixelShaders.Resize(2);
+        for (unsigned j = 0; j < 2; ++j)
+        {
+            pixelShaders[j] = GetShader(PS, pass->GetPixelShader(), Shader::SanitateDefines(pass->GetPixelShaderDefines() + " " +
+                heightFogVariations[j]));
+        }
     }
     
     pass->MarkShadersLoaded(shadersChangedFrameNumber_);

+ 8 - 0
Source/Engine/Graphics/Zone.cpp

@@ -47,6 +47,7 @@ extern const char* SCENE_CATEGORY;
 Zone::Zone(Context* context) :
     Drawable(context, DRAWABLE_ZONE),
     inverseWorldDirty_(true),
+    heightFog_(false),
     override_(false),
     ambientGradient_(false),
     ambientColor_(DEFAULT_AMBIENT_COLOR),
@@ -78,6 +79,7 @@ void Zone::RegisterObject(Context* context)
     ATTRIBUTE(Zone, VAR_FLOAT, "Fog End", fogEnd_, DEFAULT_FOG_END, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_FLOAT, "Fog Height", fogHeight_, DEFAULT_FOG_HEIGHT, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_FLOAT, "Fog Height Scale", fogHeightScale_, DEFAULT_FOG_HEIGHT_SCALE, AM_DEFAULT);
+    ATTRIBUTE(Zone, VAR_BOOL, "Height Fog Mode", heightFog_, false, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_BOOL, "Override Mode", override_, false, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_BOOL, "Ambient Gradient", ambientGradient_, false, AM_DEFAULT);
     ATTRIBUTE(Zone, VAR_INT, "Priority", priority_, 0, AM_DEFAULT);
@@ -166,6 +168,12 @@ void Zone::SetPriority(int priority)
     MarkNetworkUpdate();
 }
 
+void Zone::SetHeightFog(bool enable)
+{
+    heightFog_ = enable;
+    MarkNetworkUpdate();
+}
+
 void Zone::SetOverride(bool enable)
 {
     override_ = enable;

+ 10 - 4
Source/Engine/Graphics/Zone.h

@@ -58,12 +58,14 @@ public:
     void SetFogStart(float start);
     /// Set fog end distance.
     void SetFogEnd(float end);
-    /// Set fog height distance.
+    /// Set fog height distance relative to the scene node's world position. Effective only in height fog mode.
     void SetFogHeight(float height);
-    /// Set fog height scale.
+    /// Set fog height scale. Effective only in height fog mode.
     void SetFogHeightScale(float scale);
     /// Set zone priority. If an object or camera is inside several zones, the one with highest priority is used.
     void SetPriority(int priority);
+    /// Set height fog mode.
+    void SetHeightFog(bool enable);
     /// Set override mode. If camera is inside an override zone, it will also be used for all drawables.
     void SetOverride(bool enable);
     /// Set ambient gradient mode. In gradient mode ambient color is interpolated from neighbor zones.
@@ -83,13 +85,15 @@ public:
     float GetFogStart() const { return fogStart_; }
     /// Return fog end distance.
     float GetFogEnd() const { return fogEnd_; }
-    /// Return fog height distance.
+    /// Return fog height distance relative to the scene node's world position.
     float GetFogHeight() const { return fogHeight_; }
     /// Return fog height scale.
     float GetFogHeightScale() const { return fogHeightScale_; }
     /// Return zone priority.
     int GetPriority() const { return priority_; }
-    /// Return override mode.
+    /// Return whether height fog mode is enabled.
+    bool GetHeightFog() const { return heightFog_; }
+    /// Return whether override mode is enabled.
     bool GetOverride() const { return override_; }
     /// Return whether ambient gradient mode is enabled.
     bool GetAmbientGradient() const { return ambientGradient_; }
@@ -109,6 +113,8 @@ protected:
     mutable Matrix3x4 inverseWorld_;
     /// Inverse transform dirty flag.
     mutable bool inverseWorldDirty_;
+    /// Height fog mode flag.
+    bool heightFog_;
     /// Override mode flag.
     bool override_;
     /// Ambient gradient mode flag.

+ 9 - 0
Source/Engine/LuaScript/pkgs/Graphics/Zone.pkg

@@ -7,7 +7,10 @@ class Zone : public Drawable
     void SetFogColor(const Color& color);
     void SetFogStart(float start);
     void SetFogEnd(float end);
+    void SetFogHeight(float height);
+    void SetFogHeightScale(float scale);
     void SetPriority(int priority);
+    void SetHeightFog(bool enable);
     void SetOverride(bool enable);
     void SetAmbientGradient(bool enable);
     
@@ -18,7 +21,10 @@ class Zone : public Drawable
     const Color& GetFogColor() const;
     float GetFogStart() const;
     float GetFogEnd() const;
+    float GetFogHeight() const;
+    float GetFogHeightScale() const;
     int GetPriority() const;
+    bool GetHeightFog() const;
     bool GetOverride() const;
     bool GetAmbientGradient() const;
     
@@ -32,7 +38,10 @@ class Zone : public Drawable
     tolua_property__get_set Color& fogColor;
     tolua_property__get_set float fogStart;
     tolua_property__get_set float fogEnd;
+    tolua_property__get_set float fogHeight;
+    tolua_property__get_set float fogHeightScale;
     tolua_property__get_set int priority;
+    tolua_property__get_set bool heightFog;
     tolua_property__get_set bool override;
     tolua_property__get_set bool ambientGradient;
 };

+ 2 - 0
Source/Engine/Script/GraphicsAPI.cpp

@@ -825,6 +825,8 @@ static void RegisterZone(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Zone", "float get_fogHeightScale() const", asMETHOD(Zone, GetFogHeightScale), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_priority(int)", asMETHOD(Zone, SetPriority), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "int get_priority() const", asMETHOD(Zone, GetPriority), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "void set_heightFog(bool)", asMETHOD(Zone, SetHeightFog), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Zone", "bool get_heightFog() const", asMETHOD(Zone, GetHeightFog), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_override(bool)", asMETHOD(Zone, SetOverride), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "bool get_override() const", asMETHOD(Zone, GetOverride), asCALL_THISCALL);
     engine->RegisterObjectMethod("Zone", "void set_ambientGradient(bool)", asMETHOD(Zone, SetAmbientGradient), asCALL_THISCALL);