瀏覽代碼

Switched back to a edge filter for deferred antialiasing, as temporal antialiasing causes problems when both the camera and objects move.
Fixed infinite loop in HashMap copy.

Lasse Öörni 14 年之前
父節點
當前提交
87f9bbd846

+ 1 - 1
Docs/GettingStarted.dox

@@ -89,7 +89,7 @@ Urho3D.exe understands the following command line options:
 -sm2        Force SM2.0 rendering
 \endverbatim
 
-(*) Only forward rendering supports hardware multisampling. In light pre-pass and deferred rendering modes temporal antialiasing will be used instead.
+(*) Only forward rendering supports hardware multisampling. In deferred rendering a post-process edge filter will be used instead.
 
 
 \page Structure Overall structure

+ 3 - 3
Docs/Reference.dox

@@ -350,7 +350,7 @@ Graphics implements the low-level functionality:
 
 It also provides a low-performance, immediate-like interface for manually defining small amounts of geometry to be rendered. This interface is used for rendering the debug geometry and the user interface.
 
-Screen resolution, fullscreen/windowed, vertical sync, forward/deferred mode, and hardware multisampling level are all set at once by calling Graphics's \ref Graphics::SetMode "SetMode()" function. Hardware multisampling will be disabled in deferred rendering modes as incompatible, instead it is replaced by manual temporal antialiasing.
+Screen resolution, fullscreen/windowed, vertical sync, forward/deferred mode, and hardware multisampling level are all set at once by calling Graphics's \ref Graphics::SetMode "SetMode()" function. Hardware multisampling will be disabled as incompatible in deferred rendering mode; instead it is replaced by a post-process edge filter.
 
 When setting the initial screen mode, Graphics does a few checks:
 
@@ -619,9 +619,9 @@ Opaque objects are rendered to the G-buffer first, then an initial full-screen q
 
 After light accumulation, emissive properties of materials as well as effects like environment mapping need to be rendered in an additional pass for objects that need them.
 
-\section ForwardDeferred_TemporalAA Temporal antialiasing
+\section ForwardDeferred_DeferredAntiAlias Deferred antialiasing
 
-When multisampling is enabled, deferred rendering uses subpixel projection jittering to render temporal antialiasing that should look similar to 2x MSAA. Reprojection based on the previous frame's camera view transform is used to eliminate "ghosting" effects when the camera rotates, and additional ghosting prevention is done by analyzing the depth of each pixel compared to the previous frame. Depending on the GPU speed, this form of antialiasing has a low-to-moderate performance impact.
+As actual hardware multisampling is incompatible with deferred rendering, a post-process edge filter will be used instead, if multisampling is requested, to detect intensity change gradients and smooth them. Depending on the GPU speed, this has a low-to-moderate performance impact.
 
 \section ForwardDeferred_Conclusion Conclusion
 

+ 3 - 0
Engine/Container/HashMap.h

@@ -216,7 +216,10 @@ public:
         ConstIterator it = map.Begin();
         ConstIterator end = map.End();
         while (it != end)
+        {
             InsertNode(it->first_, it->second_);
+            ++it;
+        }
     }
     
     /// Insert a pair by iterator. Return iterator to the value

+ 24 - 6
Engine/Engine/GraphicsAPI.cpp

@@ -736,18 +736,34 @@ static Renderer* GetRenderer()
     return GetScriptContext()->GetSubsystem<Renderer>();
 }
 
+static void ConstructEdgeFilterParameters(EdgeFilterParameters* ptr)
+{
+    new(ptr) EdgeFilterParameters(0.0f, 0.0f, 0.0f);
+}
+
+static void ConstructEdgeFilterParametersCopy(EdgeFilterParameters& parameters, EdgeFilterParameters* ptr)
+{
+    new(ptr) EdgeFilterParameters(parameters);
+}
+
+static void ConstructEdgeFilterParametersInit(float radius, float threshold, float strength, BiasParameters* ptr)
+{
+    new(ptr) EdgeFilterParameters(radius, threshold, strength);
+}
+
 static void RegisterRenderer(asIScriptEngine* engine)
 {
     engine->RegisterGlobalProperty("const int QUALITY_LOW", (void*)&QUALITY_LOW);
     engine->RegisterGlobalProperty("const int QUALITY_MEDIUM", (void*)&QUALITY_MEDIUM);
     engine->RegisterGlobalProperty("const int QUALITY_HIGH", (void*)&QUALITY_HIGH);
     
-    /// Set reuse of shadowmaps. Default is true, disabling allows transparent geometry shadowing
-    void SetReuseShadowMaps(bool enable);
-    /// Set number of full, half and quarter size shadowmaps. Only has effect if reuse of shadowmaps is disabled first
-    void SetNumShadowMaps(unsigned full, unsigned half, unsigned quarter);
-    /// Set dynamic instancing on/off
-    void SetDynamicInstancing(bool enable);
+    engine->RegisterObjectType("EdgeFilterParameters", sizeof(EdgeFilterParameters), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
+    engine->RegisterObjectBehaviour("EdgeFilterParameters", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructEdgeFilterParameters), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("EdgeFilterParameters", asBEHAVE_CONSTRUCT, "void f(const EdgeFilterParameters&in)", asFUNCTION(ConstructEdgeFilterParametersCopy), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("EdgeFilterParameters", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION(ConstructEdgeFilterParametersInit), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectProperty("EdgeFilterParameters", "float radius", offsetof(EdgeFilterParameters, radius_));
+    engine->RegisterObjectProperty("EdgeFilterParameters", "float threshold", offsetof(EdgeFilterParameters, threshold_));
+    engine->RegisterObjectProperty("EdgeFilterParameters", "float strength", offsetof(EdgeFilterParameters, strength_));
     
     RegisterObject<Renderer>(engine, "Renderer");
     engine->RegisterObjectMethod("Renderer", "void SetNumShadowMaps(uint, uint, uint)", asMETHOD(Renderer, SetNumShadowMaps), asCALL_THISCALL);
@@ -776,6 +792,8 @@ static void RegisterRenderer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Renderer", "bool get_reuseShadowMaps() const", asMETHOD(Renderer, GetReuseShadowMaps), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_dynamicInstancing(bool)", asMETHOD(Renderer, SetDynamicInstancing), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "bool get_dynamicInstancing() const", asMETHOD(Renderer, GetDynamicInstancing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "const EdgeFilterParameters& get_edgeFilter() const", asMETHOD(Renderer, GetEdgeFilter), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Renderer", "void set_edgeFilter(const EdgeFilterParameters& in)", asMETHOD(Renderer, SetEdgeFilter), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_maxOccluderTriangles(int)", asMETHOD(Renderer, SetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "int get_maxOccluderTriangles() const", asMETHOD(Renderer, GetMaxOccluderTriangles), asCALL_THISCALL);
     engine->RegisterObjectMethod("Renderer", "void set_occlusionBufferSize(int)", asMETHOD(Renderer, SetOcclusionBufferSize), asCALL_THISCALL);

+ 1 - 1
Engine/Engine/PhysicsAPI.cpp

@@ -62,7 +62,7 @@ static CScriptArray* PhysicsWorldRaycast(const Ray& ray, float maxDistance, unsi
 
 static void RegisterPhysicsWorld(asIScriptEngine* engine)
 {
-    engine->RegisterObjectType("PhysicsRaycastResult", sizeof(PhysicsRaycastResult), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CK);
+    engine->RegisterObjectType("PhysicsRaycastResult", sizeof(PhysicsRaycastResult), asOBJ_VALUE | asOBJ_POD);
     engine->RegisterObjectBehaviour("PhysicsRaycastResult", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructPhysicsRaycastResult), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectProperty("PhysicsRaycastResult", "Vector3 position", offsetof(PhysicsRaycastResult, position_));
     engine->RegisterObjectProperty("PhysicsRaycastResult", "Vector3 normal", offsetof(PhysicsRaycastResult, normal_));

+ 7 - 14
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -2231,30 +2231,24 @@ void Graphics::CreateRenderTargets()
             depthBuffer_->SetSize(0, 0, GetDepthFormat(), TEXTURE_RENDERTARGET);
         }
         
-        // If deferred mode temporal AA is used, reserve screen buffers
+        // If deferred antialiasing is used, reserve screen buffer
         // (later we will probably want the screen buffer reserved in any case, to do for example distortion effects,
         // which will also be useful in forward rendering)
         if (multiSample_)
         {
-            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
-            {
-                screenBuffers_[i] = new Texture2D(context_);
-                screenBuffers_[i]->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
-            }
+            screenBuffer_ = new Texture2D(context_);
+            screenBuffer_->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
+            screenBuffer_->SetFilterMode(FILTER_BILINEAR);
         }
         else
-        {
-            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
-                screenBuffers_[i].Reset();
-        }
+            screenBuffer_.Reset();
     }
     else
     {
         diffBuffer_.Reset();
         normalBuffer_.Reset();
         depthBuffer_.Reset();
-        for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
-            screenBuffers_[i].Reset();
+        screenBuffer_.Reset();
     }
 }
 
@@ -2399,8 +2393,8 @@ void Graphics::InitializeShaderParameters()
     shaderParameters_["SkinMatrices"] = VSP_SKINMATRICES;
     
     shaderParameters_["AmbientColor"] = PSP_AMBIENTCOLOR;
-    shaderParameters_["AntiAliasWeights"] = PSP_ANTIALIASWEIGHTS;
     shaderParameters_["CameraPosPS"] = PSP_CAMERAPOS;
+    shaderParameters_["EdgeFilterParams"] = PSP_EDGEFILTERPARAMS;
     shaderParameters_["ElapsedTimePS"] = PSP_ELAPSEDTIME;
     shaderParameters_["FogColor"] = PSP_FOGCOLOR;
     shaderParameters_["FogParams"] = PSP_FOGPARAMS;
@@ -2419,7 +2413,6 @@ void Graphics::InitializeShaderParameters()
     shaderParameters_["ShadowIntensity"] = PSP_SHADOWINTENSITY;
     shaderParameters_["ShadowProjPS"] = PSP_SHADOWPROJ;
     shaderParameters_["SpotProjPS"] = PSP_SPOTPROJ;
-    shaderParameters_["ViewProjPS"] = PSP_VIEWPROJ;
     
     // Map texture units
     textureUnits_["NormalMap"] = TU_NORMAL;

+ 3 - 4
Engine/Graphics/Direct3D9/D3D9Graphics.h

@@ -46,7 +46,6 @@ class VertexBuffer;
 class VertexDeclaration;
 
 static const int IMMEDIATE_BUFFER_DEFAULT_SIZE = 1024;
-static const int NUM_SCREEN_BUFFERS = 2;
 
 /// Graphics subsystem. Manages the Direct3D9 device, application window, rendering state and GPU resources
 class Graphics : public Object
@@ -332,7 +331,7 @@ public:
     /// Return depth buffer for deferred rendering. If reading hardware depth is supported, return a depth texture
     Texture2D* GetDepthBuffer() const { return depthBuffer_; }
     /// Return screen buffer for post-processing
-    Texture2D* GetScreenBuffer(unsigned index) const { return screenBuffers_[index & (NUM_SCREEN_BUFFERS - 1)]; }
+    Texture2D* GetScreenBuffer() const { return screenBuffer_; }
     
     /// Add a GPU object to keep track of. Called by GPUObject.
     void AddGPUObject(GPUObject* object);
@@ -446,8 +445,8 @@ private:
     SharedPtr<Texture2D> normalBuffer_;
     /// Deferred rendering depth buffer
     SharedPtr<Texture2D> depthBuffer_;
-    /// Screen buffers for post processing
-    SharedPtr<Texture2D> screenBuffers_[NUM_SCREEN_BUFFERS];
+    /// Screen buffer for post processing
+    SharedPtr<Texture2D> screenBuffer_;
     /// Shadow map dummy color texture format
     unsigned dummyColorFormat_;
     /// Shadow map depth texture format

+ 1 - 2
Engine/Graphics/GraphicsDefs.h

@@ -218,9 +218,9 @@ enum ShaderParameter
     VSP_VIEWUPVECTOR,
     VSP_SKINMATRICES,
     PSP_AMBIENTCOLOR,
-    PSP_ANTIALIASWEIGHTS,
     PSP_CAMERAPOS,
     PSP_DEPTHRECONSTRUCT,
+    PSP_EDGEFILTERPARAMS,
     PSP_ELAPSEDTIME,
     PSP_FOGCOLOR,
     PSP_FOGPARAMS,
@@ -239,7 +239,6 @@ enum ShaderParameter
     PSP_SHADOWINTENSITY,
     PSP_SHADOWPROJ,
     PSP_SPOTPROJ,
-    PSP_VIEWPROJ,
     MAX_SHADER_PARAMETERS
 };
 

+ 9 - 18
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -174,9 +174,6 @@ bool Graphics::SetMode(RenderMode mode, int width, int height, bool fullscreen,
 {
     PROFILE(SetScreenMode);
     
-    /// \todo For now multisampling is not supported
-    multiSample = 0;
-
     // If zero dimensions, use the desktop default
     if ((width <= 0) || (height <= 0))
     {
@@ -286,6 +283,7 @@ bool Graphics::SetMode(RenderMode mode, int width, int height, bool fullscreen,
     fullscreen_ = fullscreen;
     vsync_ = vsync;
     mode_ = mode;
+    multiSample_ = multiSample;
     
     // Reset rendertargets and viewport for the new screen mode
     ResetRenderTargets();
@@ -2134,30 +2132,24 @@ void Graphics::CreateRenderTargets()
             depthBuffer_->SetSize(0, 0, GetDepthFormat(), TEXTURE_DEPTHSTENCIL);
         }
         
-        // If deferred mode temporal AA is used, reserve screen buffers
+        // If deferred antialiasing is used, reserve screen buffer
         // (later we will probably want the screen buffer reserved in any case, to do for example distortion effects,
         // which will also be useful in forward rendering)
         if (multiSample_)
         {
-            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
-            {
-                screenBuffers_[i] = new Texture2D(context_);
-                screenBuffers_[i]->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
-            }
+            screenBuffer_ = new Texture2D(context_);
+            screenBuffer_->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
+            screenBuffer_->SetFilterMode(FILTER_BILINEAR);
         }
         else
-        {
-            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
-                screenBuffers_[i].Reset();
-        }
+            screenBuffer_.Reset();
     }
     else
     {
         diffBuffer_.Reset();
         normalBuffer_.Reset();
         depthBuffer_.Reset();
-        for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
-            screenBuffers_[i].Reset();
+        screenBuffer_.Reset();
     }
 }
 
@@ -2260,9 +2252,9 @@ void Graphics::InitializeShaderParameters()
     shaderParameters_["SkinMatrices"] = VSP_SKINMATRICES;
     
     shaderParameters_["AmbientColor"] = PSP_AMBIENTCOLOR;
-    shaderParameters_["AntiAliasWeights"] = PSP_ANTIALIASWEIGHTS;
     shaderParameters_["CameraPosPS"] = PSP_CAMERAPOS;
     shaderParameters_["DepthReconstruct"] = PSP_DEPTHRECONSTRUCT;
+    shaderParameters_["EdgeFilterParams"] = PSP_EDGEFILTERPARAMS;
     shaderParameters_["ElapsedTimePS"] = PSP_ELAPSEDTIME;
     shaderParameters_["FogColor"] = PSP_FOGCOLOR;
     shaderParameters_["FogParams"] = PSP_FOGPARAMS;
@@ -2281,8 +2273,7 @@ void Graphics::InitializeShaderParameters()
     shaderParameters_["ShadowIntensity"] = PSP_SHADOWINTENSITY;
     shaderParameters_["ShadowProjPS"] = PSP_SHADOWPROJ;
     shaderParameters_["SpotProjPS"] = PSP_SPOTPROJ;
-    shaderParameters_["ViewProjPS"] = PSP_VIEWPROJ;
-    
+
     // Map texture units
     textureUnits_["NormalMap"] = TU_NORMAL;
     textureUnits_["DiffMap"] = TU_DIFFUSE;

+ 3 - 6
Engine/Graphics/OpenGL/OGLGraphics.h

@@ -46,9 +46,6 @@ class Vector3;
 class Vector4;
 class VertexBuffer;
 
-static const int IMMEDIATE_BUFFER_DEFAULT_SIZE = 1024;
-static const int NUM_SCREEN_BUFFERS = 2;
-
 typedef Map<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
 
 /// Graphics subsystem. Manages the application window, rendering state and GPU resources
@@ -338,7 +335,7 @@ public:
     /// Return depth buffer for deferred rendering. If reading hardware depth is supported, return a depth texture
     Texture2D* GetDepthBuffer() const { return depthBuffer_; }
     /// Return screen buffer for post-processing
-    Texture2D* GetScreenBuffer(unsigned index) const { return screenBuffers_[index & (NUM_SCREEN_BUFFERS - 1)]; }
+    Texture2D* GetScreenBuffer() const { return screenBuffer_; }
     
     /// Add a GPU object to keep track of. Called by GPUObject.
     void AddGPUObject(GPUObject* object);
@@ -440,8 +437,8 @@ private:
     SharedPtr<Texture2D> normalBuffer_;
     /// Deferred rendering depth buffer
     SharedPtr<Texture2D> depthBuffer_;
-    /// Screen buffers for post processing
-    SharedPtr<Texture2D> screenBuffers_[NUM_SCREEN_BUFFERS];
+    /// Screen buffer for post processing
+    SharedPtr<Texture2D> screenBuffer_;
     /// Shadow map depth texture format
     unsigned shadowMapFormat_;
     /// Shadow map 24-bit depth texture format

+ 14 - 0
Engine/Graphics/Renderer.cpp

@@ -240,6 +240,13 @@ static const String lightPSVariations[] =
 static const unsigned INSTANCING_BUFFER_MASK = MASK_INSTANCEMATRIX1 | MASK_INSTANCEMATRIX2 | MASK_INSTANCEMATRIX3;
 static const Viewport noViewport;
 
+void EdgeFilterParameters::Validate()
+{
+    radius_ = max(radius_, 0.0f);
+    threshold_ = max(threshold_, 0.0f);
+    strength_ = max(strength_, 0.0f);
+}
+
 OBJECTTYPESTATIC(Renderer);
 
 Renderer::Renderer(Context* context) :
@@ -259,6 +266,7 @@ Renderer::Renderer(Context* context) :
     shadowMapHiresDepth_(false),
     reuseShadowMaps_(true),
     dynamicInstancing_(true),
+    edgeFilter_(EdgeFilterParameters(2.0f, 1.0f, 0.66f)),
     maxOccluderTriangles_(5000),
     occlusionBufferSize_(256),
     occluderSizeThreshold_(0.1f),
@@ -402,6 +410,12 @@ void Renderer::SetDynamicInstancing(bool enable)
     dynamicInstancing_ = enable;
 }
 
+void Renderer::SetEdgeFilter(const EdgeFilterParameters& parameters)
+{
+    edgeFilter_ = parameters;
+    edgeFilter_.Validate();
+}
+
 void Renderer::SetMaxOccluderTriangles(int triangles)
 {
     maxOccluderTriangles_ = Max(triangles, 0);

+ 33 - 0
Engine/Graphics/Renderer.h

@@ -131,6 +131,33 @@ enum DeferredLightPSVariation
     MAX_DEFERRED_LIGHT_PS_VARIATIONS
 };
 
+/// Deferred rendering edge filter parameters
+struct EdgeFilterParameters
+{
+    /// Construct as 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
 class Renderer : public Object
 {
@@ -170,6 +197,8 @@ public:
     void SetNumShadowMaps(unsigned full, unsigned half, unsigned quarter);
     /// Set dynamic instancing on/off
     void SetDynamicInstancing(bool enable);
+    /// Set deferred rendering edge filter parameters
+    void SetEdgeFilter(const EdgeFilterParameters& parameters);
     /// Set maximum number of occluder trianges
     void SetMaxOccluderTriangles(int triangles);
     /// Set occluder buffer width
@@ -206,6 +235,8 @@ public:
     unsigned GetNumQuarterShadowMaps() const { return shadowMaps_[2].Size(); }
     /// Return whether dynamic instancing is in use
     bool GetDynamicInstancing() const { return dynamicInstancing_; }
+    /// Return deferred rendering edge filter parameters
+    const EdgeFilterParameters& GetEdgeFilter() const { return edgeFilter_; }
     /// Return maximum number of occluder triangles
     int GetMaxOccluderTriangles() const { return maxOccluderTriangles_; }
     /// Return occlusion buffer width
@@ -396,6 +427,8 @@ private:
     bool reuseShadowMaps_;
     /// Dynamic instancing flag
     bool dynamicInstancing_;
+    /// Deferred rendering edge filter parameters
+    EdgeFilterParameters edgeFilter_;
     /// Maximum occluder triangles
     int maxOccluderTriangles_;
     /// Occlusion buffer width

+ 15 - 54
Engine/Graphics/View.cpp

@@ -786,24 +786,9 @@ void View::RenderBatchesDeferred()
     Texture2D* normalBuffer = graphics_->GetNormalBuffer();
     Texture2D* depthBuffer = graphics_->GetDepthBuffer();
     
-    // Check for temporal antialiasing in deferred mode. Only use it on the main view (null rendertarget)
-    bool temporalAA = (!renderTarget_) && (graphics_->GetMultiSample() > 0);
-    if (temporalAA)
-    {
-        ++jitterCounter_;
-        if (jitterCounter_ > 3)
-            jitterCounter_ = 2;
-        
-        Vector2 jitter(-0.25f, -0.25f);
-        if (jitterCounter_ & 1)
-            jitter = -jitter;
-        jitter.x_ /= width_;
-        jitter.y_ /= height_;
-        
-        camera_->SetProjectionOffset(jitter);
-    }
-    
-    RenderSurface* renderBuffer = temporalAA ? graphics_->GetScreenBuffer(jitterCounter_ & 1)->GetRenderSurface() : renderTarget_;
+    // Check for deferred antialiasing (edge filter) in deferred mode. Only use it on the main view (null rendertarget)
+    bool edgeFilter = (!renderTarget_) && (graphics_->GetMultiSample() > 0);
+    RenderSurface* renderBuffer = edgeFilter ? graphics_->GetScreenBuffer()->GetRenderSurface() : renderTarget_;
     
     // Calculate shader parameters needed only in deferred rendering
     Vector3 nearVector, farVector;
@@ -993,52 +978,28 @@ void View::RenderBatchesDeferred()
         RenderBatchQueue(transparentQueue_, true);
     }
     
-    // Render temporal antialiasing now if enabled
-    if (temporalAA)
+    // Render deferred antialiasing now if enabled
+    if (edgeFilter)
     {
-        PROFILE(RenderTemporalAA);
+        PROFILE(RenderEdgeFilter);
         
-        // Disable averaging if it is the first frame rendered in this view
-        float thisFrameWeight = jitterCounter_ < 2 ? 1.0f : 0.5f;
+        const EdgeFilterParameters& parameters = renderer_->GetEdgeFilter();
+        ShaderVariation* vs = renderer_->GetVertexShader("EdgeFilter");
+        ShaderVariation* ps = renderer_->GetPixelShader("EdgeFilter");  
         
-        Vector4 depthMode = Vector4::ZERO;
-        if (camera_->IsOrthographic())
-            depthMode.z_ = 1.0f;
-        else
-            depthMode.w_ = 1.0f / camera_->GetFarClip();
-        
-        String shaderName = "TemporalAA";
-        if (camera_->IsOrthographic())
-            shaderName += "_Ortho";
+        HashMap<ShaderParameter, Vector4> shaderParameters(shaderParameters_);
+        shaderParameters[PSP_EDGEFILTERPARAMS] = Vector4(parameters.radius_, parameters.threshold_, parameters.strength_, 0.0f);
+        shaderParameters[PSP_SAMPLEOFFSETS] = Vector4(1.0f / gBufferWidth, 1.0f / gBufferHeight, 0.0f, 0.0f);
         
         graphics_->SetAlphaTest(false);
         graphics_->SetBlendMode(BLEND_REPLACE);
-        graphics_->SetDepthTest(CMP_ALWAYS);
-        graphics_->SetDepthWrite(false);
-        graphics_->SetScissorTest(false);
+        graphics_->SetDepthTest(CMP_ALWAYS),
         graphics_->SetStencilTest(false);
         graphics_->SetRenderTarget(0, renderTarget_);
         graphics_->SetDepthStencil(depthStencil_);
         graphics_->SetViewport(screenRect_);
-        
-        // Pre-select the right shaders so that we can set shader parameters that can not go into the parameter map
-        // (matrices)
-        graphics_->SetShaders(renderer_->GetVertexShader(shaderName), renderer_->GetPixelShader(shaderName));
-        graphics_->SetShaderParameter(VSP_CAMERAROT, camera_->GetWorldTransform().RotationMatrix());
-        graphics_->SetShaderParameter(VSP_DEPTHMODE, depthMode);
-        graphics_->SetShaderParameter(PSP_CAMERAPOS, camera_->GetWorldPosition());
-        graphics_->SetShaderParameter(PSP_ANTIALIASWEIGHTS, Vector4(thisFrameWeight, 1.0f - thisFrameWeight, 0.0f, 0.0f));
-        graphics_->SetShaderParameter(PSP_SAMPLEOFFSETS, Vector4(1.0f / gBufferWidth, 1.0f / gBufferHeight, 0.0f, 0.0f));
-        graphics_->SetShaderParameter(PSP_VIEWPROJ, camera_->GetProjection(false) * lastCameraView_);
-        graphics_->SetTexture(TU_DIFFBUFFER, graphics_->GetScreenBuffer(jitterCounter_ & 1));
-        graphics_->SetTexture(TU_NORMALBUFFER, graphics_->GetScreenBuffer((jitterCounter_ + 1) & 1));
-        graphics_->SetTexture(TU_DEPTHBUFFER, graphics_->GetDepthBuffer());
-        
-        renderer_->DrawFullScreenQuad(*camera_, renderer_->GetVertexShader(shaderName),
-            renderer_->GetPixelShader(shaderName), false, shaderParameters_);
-        
-        // Store view transform for next frame
-        lastCameraView_ = camera_->GetInverseWorldTransform();
+        graphics_->SetTexture(TU_DIFFBUFFER, graphics_->GetScreenBuffer());
+        renderer_->DrawFullScreenQuad(*camera_, vs, ps, false, shaderParameters);
     }
 }
 

+ 1 - 0
SourceAssets/GLSLShaders/CMakeLists.txt

@@ -2,6 +2,7 @@ set (ALL_SHADERS)
 
 add_shader (Ambient)
 add_shader (Basic)
+add_shader (EdgeFilter)
 add_shader (Forward)
 add_shader (GBuffer)
 add_shader (Light)

+ 41 - 0
SourceAssets/GLSLShaders/EdgeFilter.frag

@@ -0,0 +1,41 @@
+#include "Uniforms.frag"
+#include "Samplers.frag"
+
+varying vec2 vScreenPos;
+
+void main()
+{
+    // Shader based on the blog post http://www.gamestart3d.com/blog/ssaa-antialiasing
+    vec2 offsets = cEdgeFilterParams.x * cSampleOffsets.xy;
+
+    vec4 color = texture2D(sDiffBuffer, vScreenPos);
+    
+    // Get intensity at center, clamp to 10% minimum to not cause oversensitivity in dark areas
+    float base = max(GetIntensity(color.rgb), 0.1);
+    
+    // Get intensities at neighbour pixels
+    float up = GetIntensity(texture2D(sDiffBuffer, vScreenPos + vec2(0, -offsets.y)).rgb);
+    float down = GetIntensity(texture2D(sDiffBuffer, vScreenPos + vec2(0, offsets.y)).rgb);
+    float left = GetIntensity(texture2D(sDiffBuffer, vScreenPos + vec2(-offsets.x, 0)).rgb);
+    float right = GetIntensity(texture2D(sDiffBuffer, vScreenPos + vec2(offsets.x, 0)).rgb);
+    
+    // Calculate normal, scale with base intensity
+    vec2 normal = vec2((up - base) + (base - down), (right - base) + (base - left)) / base;
+    float len = length(normal);
+    
+    // Clamp normal to maximum
+    if (len > 1)
+        normal /= len;
+
+    // Blur if over threshold (compare to center pixel intensity)
+    if (len > cEdgeFilterParams.y)
+    {
+        normal *= cSampleOffsets.xy * cEdgeFilterParams.z;
+
+        gl_FragColor = (color +
+            texture2D(sDiffBuffer, vScreenPos + normal) +
+            texture2D(sDiffBuffer, vScreenPos - normal)) * (1.0 / 3.0);
+    }
+    else
+        gl_FragColor = color;
+}

+ 11 - 0
SourceAssets/GLSLShaders/EdgeFilter.vert

@@ -0,0 +1,11 @@
+#include "Uniforms.vert"
+#include "Transform.vert"
+#include "ScreenPos.vert"
+
+varying vec2 vScreenPos;
+
+void main()
+{
+    GetPosition(iPosition, gl_Position);
+    vScreenPos = GetScreenPosPreDiv(gl_Position);
+}

+ 4 - 0
SourceAssets/GLSLShaders/EdgeFilter.xml

@@ -0,0 +1,4 @@
+<shaders>
+    <shader name="EdgeFilter" type="vs" />
+    <shader name="EdgeFilter" type="ps" />
+</shaders>

+ 7 - 0
SourceAssets/GLSLShaders/Samplers.frag

@@ -27,3 +27,10 @@ float ReconstructDepth(float hwDepth)
 {
     return cDepthReconstruct.y / (hwDepth - cDepthReconstruct.x);
 }
+
+float GetIntensity(vec3 color)
+{
+    const float dotValue = 1.0 / 3.0;
+
+    return dot(color, vec3(dotValue, dotValue, dotValue));
+}

+ 1 - 2
SourceAssets/GLSLShaders/Uniforms.frag

@@ -1,7 +1,7 @@
 uniform vec3 cAmbientColor;
-uniform vec4 cAntiAliasWeights;
 uniform vec3 cCameraPosPS;
 uniform vec2 cDepthReconstruct;
+uniform vec3 cEdgeFilterParams;
 uniform vec2 cElapsedTimePS;
 uniform vec4 cFogParams;
 uniform vec3 cFogColor;
@@ -20,4 +20,3 @@ uniform vec4 cSampleOffsets;
 uniform vec2 cShadowIntensity;
 uniform mat4 cShadowProjPS;
 uniform mat4 cSpotProjPS;
-uniform mat4 cViewProjPS;

+ 1 - 1
SourceAssets/HLSLShaders/CMakeLists.txt

@@ -2,12 +2,12 @@ set (ALL_SHADERS)
 
 add_shader (Ambient)
 add_shader (Basic)
+add_shader (EdgeFilter)
 add_shader (Forward)
 add_shader (GBuffer)
 add_shader (GBufferFill)
 add_shader (Light)
 add_shader (Shadow)
 add_shader (Stencil)
-add_shader (TemporalAA)
 
 add_custom_target (Shaders ALL DEPENDS ${ALL_SHADERS})

+ 49 - 0
SourceAssets/HLSLShaders/EdgeFilter.hlsl

@@ -0,0 +1,49 @@
+#include "Uniforms.hlsl"
+#include "Samplers.hlsl"
+#include "Transform.hlsl"
+#include "ScreenPos.hlsl"
+
+void VS(float4 iPos : POSITION,
+    out float4 oPos : POSITION,
+    out float2 oScreenPos : TEXCOORD0)
+{
+    GetPosition(iPos, oPos);
+    oScreenPos = GetScreenPosPreDiv(oPos);
+}
+
+void PS(float2 iScreenPos : TEXCOORD0,
+    out float4 oColor : COLOR0)
+{
+    // Shader based on the blog post http://www.gamestart3d.com/blog/ssaa-antialiasing
+    float2 offsets = cEdgeFilterParams.x * cSampleOffsets.xy;
+
+    float4 color = tex2D(sDiffBuffer, iScreenPos);
+    
+    // Get intensity at center, clamp to 10% minimum to not cause oversensitivity in dark areas
+    float base = max(GetIntensity(color.rgb), 0.1);
+    
+    // Get intensities at neighbour pixels
+    float up = GetIntensity(tex2D(sDiffBuffer, iScreenPos + float2(0, -offsets.y)).rgb);
+    float down = GetIntensity(tex2D(sDiffBuffer, iScreenPos + float2(0, offsets.y)).rgb);
+    float left = GetIntensity(tex2D(sDiffBuffer, iScreenPos + float2(-offsets.x, 0)).rgb);
+    float right = GetIntensity(tex2D(sDiffBuffer, iScreenPos + float2(offsets.x, 0)).rgb);
+    
+    // Calculate normal, scale with base intensity
+    float2 normal = float2((up - base) + (base - down), (right - base) + (base - left)) / base;
+    float len = length(normal);
+    
+    // Clamp normal to maximum
+    if (len > 1)
+        normal /= len;
+    
+    // Blur if over threshold (compare to center pixel intensity)
+    if (len > cEdgeFilterParams.y)
+    {
+        normal *= cSampleOffsets.xy * cEdgeFilterParams.z;
+        oColor = (color +
+            Sample(sDiffBuffer, iScreenPos + normal) +
+            Sample(sDiffBuffer, iScreenPos - normal)) * (1.0 / 3.0);
+    }
+    else
+        oColor = color;
+}

+ 4 - 0
SourceAssets/HLSLShaders/EdgeFilter.xml

@@ -0,0 +1,4 @@
+<shaders>
+    <shader name="EdgeFilter" type="vs" />
+    <shader name="EdgeFilter" type="ps" />
+</shaders>

+ 7 - 0
SourceAssets/HLSLShaders/Samplers.hlsl

@@ -37,3 +37,10 @@ float3 UnpackNormal(float4 normalInput)
     normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
     return normal;
 }
+
+float GetIntensity(float3 color)
+{
+    const float dotValue = 1.0 / 3.0;
+
+    return dot(color, dotValue);
+}

+ 0 - 78
SourceAssets/HLSLShaders/TemporalAA.hlsl

@@ -1,78 +0,0 @@
-#include "Uniforms.hlsl"
-#include "Samplers.hlsl"
-#include "Transform.hlsl"
-#include "ScreenPos.hlsl"
-
-void VS(float4 iPos : POSITION,
-    out float4 oPos : POSITION,
-    out float3 oFarRay : TEXCOORD1,
-    #ifdef ORTHO
-        out float3 oNearRay : TEXCOORD2,
-    #endif
-    out float2 oScreenPos : TEXCOORD0)
-{
-    GetPosition(iPos, oPos);
-    oScreenPos = GetScreenPosPreDiv(oPos);
-
-    oFarRay = GetFarRay(oPos);
-    #ifdef ORTHO
-        oNearRay = GetNearRay(oPos);
-    #endif
-}
-
-void PS(float2 iScreenPos : TEXCOORD0,
-    float3 iFarRay : TEXCOORD1,
-    #ifdef ORTHO
-        float3 iNearRay : TEXCOORD2,
-    #endif
-    out float4 oColor : COLOR0)
-{
-    #ifdef ORTHO
-        float depth = Sample(sDepthBuffer, iScreenPos).r;
-        float3 worldPos = lerp(iNearRay, iFarRay, depth) + cCameraPosPS;
-    #else
-        float depth = Sample(sDepthBuffer, iScreenPos).r;
-        float3 worldPos = iFarRay * depth + cCameraPosPS;
-    #endif
-
-    // Calculate G-buffer position for pixel on last frame
-    float4 clipPosLast = mul(float4(worldPos, 1.0), cViewProjPS);
-    #ifdef ORTHO
-        float2 screenPosLast = float2(
-            clipPosLast.x * cGBufferOffsetsPS.z + cGBufferOffsetsPS.x,
-            -clipPosLast.y * cGBufferOffsetsPS.w + cGBufferOffsetsPS.y);
-    #else
-       float2 screenPosLast = float2(
-            clipPosLast.x / clipPosLast.w * cGBufferOffsetsPS.z + cGBufferOffsetsPS.x,
-            -clipPosLast.y / clipPosLast.w * cGBufferOffsetsPS.w + cGBufferOffsetsPS.y);
-    #endif
-
-    // Sample the pixels this frame and previous, assume we are going to blend
-    float4 current = Sample(sDiffBuffer, iScreenPos);
-    float4 last = Sample(sNormalBuffer, screenPosLast);
-
-    // If position is outside viewport, must disable blending
-    if ((screenPosLast.x < cGBufferViewport.x) || (screenPosLast.y < cGBufferViewport.y) ||
-        (screenPosLast.x > cGBufferViewport.z) || (screenPosLast.y > cGBufferViewport.w))
-        last = current;
-
-    // Compare coarse depth this and previous frame to check for ghosting
-    // Set coarse depth threshold to be more than 1/255 so that depth gradients do not trigger it
-    float threshold = 0.005;
-    if (abs(last.a - current.a) > threshold)
-    {
-        // If we seem to have ghosting, check if it is an edge. Always allow blending on the edges
-        float2 hOffset = float2(cSampleOffsets.x, 0);
-        float2 vOffset = float2(0, cSampleOffsets.y);
-        float4 adjacent = float4(Sample(sDiffBuffer, iScreenPos - hOffset).a,
-            Sample(sDiffBuffer, iScreenPos + hOffset).a,
-            Sample(sDiffBuffer, iScreenPos - vOffset).a,
-            Sample(sDiffBuffer, iScreenPos + vOffset).a);
-
-        float edge = dot(abs(adjacent - current.a) > threshold, 1);
-        if (edge == 0)
-            last = current;
-    }
-
-    oColor = cAntiAliasWeights.x * current + cAntiAliasWeights.y * last;
-}

+ 0 - 8
SourceAssets/HLSLShaders/TemporalAA.xml

@@ -1,8 +0,0 @@
-<shaders>
-    <shader name="TemporalAA" type="vs">
-        <option name="Ortho" define="ORTHO" />
-    </shader>
-    <shader name="TemporalAA" type="ps">
-        <option name="Ortho" define="ORTHO" />
-    </shader>
-</shaders>

+ 2 - 3
SourceAssets/HLSLShaders/Uniforms.hlsl

@@ -17,8 +17,8 @@ uniform float4x3 cSkinMatrices[64] : register(C27);
 
 // Pixel shader parameters
 uniform float3 cAmbientColor : register(C0);
-uniform float4 cAntiAliasWeights : register(C1);
-uniform float3 cCameraPosPS : register(C2);
+uniform float3 cCameraPosPS : register(C1);
+uniform float4 cEdgeFilterParams : register(C2);
 uniform float2 cElapsedTimePS : register(C3);
 uniform float4 cFogParams : register(C4);
 uniform float3 cFogColor : register(C5);
@@ -37,4 +37,3 @@ uniform float4 cSampleOffsets : register(C19);
 uniform float2 cShadowIntensity : register(C20);
 uniform float4x4 cShadowProjPS : register(C21);
 uniform float4x4 cSpotProjPS : register(C25);
-uniform float4x4 cViewProjPS : register(C25);