Browse Source

Replaced edge filtering with temporal antialiasing with ghosting prevention.

Lasse Öörni 14 years ago
parent
commit
a89cb22161

+ 1 - 1
Docs/GettingStarted.dox

@@ -116,7 +116,7 @@ Urho3D.exe understands the following command line options:
 -sm2        Force SM2.0 rendering
 \endverbatim
 
-(*) Only forward rendering supports hardware multisampling. In deferred rendering a post-process edge filter based on NVIDIA FXAA II will be used instead.
+(*) Only forward rendering supports hardware multisampling. In deferred rendering temporal antialiasing will be used instead.
 
 
 \page Structure Overall structure

+ 2 - 2
Docs/Reference.dox

@@ -369,7 +369,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 as incompatible in deferred rendering mode; instead it is replaced by a post-process edge filter, based on NVIDIA's FXAA II.
+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 temporal antialiasing post-process filter.
 
 When setting the initial screen mode, Graphics does a few checks:
 
@@ -638,7 +638,7 @@ After light accumulation, emissive properties of materials as well as effects li
 
 \section ForwardDeferred_DeferredAntiAlias Deferred antialiasing
 
-As actual hardware multisampling is incompatible with deferred rendering, a post-process edge filter based on NVIDIA's FXAA II will be used instead, if multisampling is requested, to detect luminance gradients and smooth them. Depending on the GPU speed, this has a low-to-moderate performance impact.
+As actual hardware multisampling is incompatible with deferred rendering, a temporal antialiasing post-process filter will be used instead. Depending on the GPU speed, this has a low-to-moderate performance impact.
 
 \section ForwardDeferred_Conclusion Conclusion
 

+ 1 - 0
Docs/Urho3D.dox

@@ -55,6 +55,7 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org/) and Horde3D (http://
 - Red Black Trees by Julienne Walker (http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx)
 - Comparison of several sorting algorithms by Juha Nieminen (http://warp.povusers.org/SortComparison/)
 - NVIDIA FXAA II for Consoles by Timothy Lottes (http://timothylottes.blogspot.com/2011/04/nvidia-fxaa-ii-for-console.html)
+- DX11 Perspective Matrix Jittering Temporal AA by yakiimo02 (http://www.yakiimo3d.com/2010/09/28/dx11-perspective-matrix-jittering-temporal-aa/)
 
 Urho3D uses the following third-party libraries:
 

+ 0 - 25
Engine/Engine/GraphicsAPI.cpp

@@ -730,35 +730,12 @@ 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);
     
-    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);
     engine->RegisterObjectMethod("Renderer", "void DrawDebugGeometry(bool) const", asMETHOD(Renderer, DrawDebugGeometry), asCALL_THISCALL);
@@ -786,8 +763,6 @@ 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);

+ 0 - 19
Engine/Graphics/Batch.cpp

@@ -107,25 +107,6 @@ void Batch::Prepare(Graphics* graphics, const HashMap<StringHash, Vector4>& shad
     if (graphics->NeedParameterUpdate(VSP_CAMERAROT, camera_))
         graphics->SetShaderParameter(VSP_CAMERAROT, camera_->GetWorldTransform().RotationMatrix());
     
-    if (graphics->NeedParameterUpdate(VSP_DEPTHMODE, camera_))
-    {
-        Vector4 depthMode = Vector4::ZERO;
-        if (camera_->IsOrthographic())
-        {
-            depthMode.x_ = 1.0f;
-            #ifdef USE_OPENGL
-            depthMode.z_ = 0.5f;
-            depthMode.w_ = 0.5f;
-            #else
-            depthMode.z_ = 1.0f;
-            #endif
-        }
-        else
-            depthMode.w_ = 1.0f / camera_->GetFarClip();
-        
-        graphics->SetShaderParameter(VSP_DEPTHMODE, depthMode);
-    }
-    
     if (overrideView_)
     {
         // If we override the view matrix, also disable any projection jittering

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

@@ -430,7 +430,8 @@ void Graphics::Close()
         diffBuffer_.Reset();
         normalBuffer_.Reset();
         depthBuffer_.Reset();
-        screenBuffer_.Reset();
+        for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+            screenBuffers_[i].Reset();
         immediateVertexBuffers_.Clear();
         
         DestroyWindow(impl_->window_);
@@ -2213,24 +2214,31 @@ void Graphics::CreateRenderTargets()
                 depthBuffer_->SetSize(0, 0, (D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z'), TEXTURE_DEPTHSTENCIL);
         }
         
-        // If deferred antialiasing is used, reserve screen buffer
+        // If deferred antialiasing is used, reserve screen buffers
         // (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_ > 1)
         {
-            screenBuffer_ = new Texture2D(context_);
-            screenBuffer_->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
-            screenBuffer_->SetFilterMode(FILTER_BILINEAR);
+            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+            {
+                screenBuffers_[i] = new Texture2D(context_);
+                screenBuffers_[i]->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
+                screenBuffers_[i]->SetFilterMode(FILTER_BILINEAR);
+            }
         }
         else
-            screenBuffer_.Reset();
+        {
+            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+                screenBuffers_[i].Reset();
+        }
     }
     else
     {
         diffBuffer_.Reset();
         normalBuffer_.Reset();
         depthBuffer_.Reset();
-        screenBuffer_.Reset();
+        for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+            screenBuffers_[i].Reset();
     }
 }
 

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

@@ -48,6 +48,7 @@ class VertexDeclaration;
 
 static const int IMMEDIATE_BUFFER_DEFAULT_SIZE = 1024;
 static const unsigned NUM_QUERIES = 2;
+static const unsigned NUM_SCREEN_BUFFERS = 2;
 
 /// %Shader parameter definition.
 struct ShaderParameter
@@ -340,7 +341,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() const { return screenBuffer_; }
+    Texture2D* GetScreenBuffer(unsigned index) const { return screenBuffers_[index]; }
     
     /// Add a GPU object to keep track of. Called by GPUObject.
     void AddGPUObject(GPUObject* object);
@@ -465,7 +466,7 @@ private:
     /// Deferred rendering depth buffer.
     SharedPtr<Texture2D> depthBuffer_;
     /// Screen buffer for post processing.
-    SharedPtr<Texture2D> screenBuffer_;
+    SharedPtr<Texture2D> screenBuffers_[NUM_SCREEN_BUFFERS];
     /// Shadow map dummy color texture format.
     unsigned dummyColorFormat_;
     /// Shadow map depth texture format.

+ 4 - 1
Engine/Graphics/GraphicsDefs.cpp

@@ -42,11 +42,13 @@ StringHash VSP_VIEWRIGHTVECTOR("ViewRightVector");
 StringHash VSP_VIEWUPVECTOR("ViewUpVector");
 StringHash VSP_SKINMATRICES("SkinMatrices");
 StringHash PSP_AMBIENTCOLOR("AmbientColor");
+StringHash PSP_ANTIALIASWEIGHTS("AntiAliasWeights");
+StringHash PSP_CAMERAPOS("CameraPosPS");
 StringHash PSP_DEPTHRECONSTRUCT("DepthReconstruct");
-StringHash PSP_EDGEFILTERPARAMS("EdgeFilterParams");
 StringHash PSP_ELAPSEDTIME("ElapsedTimePS");
 StringHash PSP_FOGCOLOR("FogColor");
 StringHash PSP_FOGPARAMS("FogParams");
+StringHash PSP_GBUFFEROFFSETS("GBufferOffsetsPS");
 StringHash PSP_LIGHTATTEN("LightAtten");
 StringHash PSP_LIGHTCOLOR("LightColor");
 StringHash PSP_LIGHTDIR("LightDir");
@@ -60,3 +62,4 @@ StringHash PSP_SAMPLEOFFSETS("SampleOffsets");
 StringHash PSP_SHADOWINTENSITY("ShadowIntensity");
 StringHash PSP_SHADOWPROJ("ShadowProjPS");
 StringHash PSP_SPOTPROJ("SpotProjPS");
+StringHash PSP_VIEWPROJ("ViewProjPS");

+ 4 - 1
Engine/Graphics/GraphicsDefs.h

@@ -216,11 +216,13 @@ extern StringHash VSP_VIEWRIGHTVECTOR;
 extern StringHash VSP_VIEWUPVECTOR;
 extern StringHash VSP_SKINMATRICES;
 extern StringHash PSP_AMBIENTCOLOR;
+extern StringHash PSP_ANTIALIASWEIGHTS;
+extern StringHash PSP_CAMERAPOS;
 extern StringHash PSP_DEPTHRECONSTRUCT;
-extern StringHash PSP_EDGEFILTERPARAMS;
 extern StringHash PSP_ELAPSEDTIME;
 extern StringHash PSP_FOGCOLOR;
 extern StringHash PSP_FOGPARAMS;
+extern StringHash PSP_GBUFFEROFFSETS;
 extern StringHash PSP_LIGHTATTEN;
 extern StringHash PSP_LIGHTCOLOR;
 extern StringHash PSP_LIGHTDIR;
@@ -234,6 +236,7 @@ extern StringHash PSP_SAMPLEOFFSETS;
 extern StringHash PSP_SHADOWINTENSITY;
 extern StringHash PSP_SHADOWPROJ;
 extern StringHash PSP_SPOTPROJ;
+extern StringHash PSP_VIEWPROJ;
 
 /// Texture units.
 enum TextureUnit

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

@@ -2007,24 +2007,34 @@ void Graphics::CreateRenderTargets()
             depthBuffer_->SetSize(0, 0, GetDepthFormat(), TEXTURE_DEPTHSTENCIL);
         }
         
-        // If deferred antialiasing is used, reserve screen buffer
+        // If deferred antialiasing is used, reserve screen buffers
+        // (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 deferred antialiasing is used, reserve screen buffers
         // (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_ > 1)
         {
-            screenBuffer_ = new Texture2D(context_);
-            screenBuffer_->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
-            screenBuffer_->SetFilterMode(FILTER_BILINEAR);
+            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+            {
+                screenBuffers_[i] = new Texture2D(context_);
+                screenBuffers_[i]->SetSize(0, 0, GetRGBAFormat(), TEXTURE_RENDERTARGET);
+                screenBuffers_[i]->SetFilterMode(FILTER_BILINEAR);
+            }
         }
         else
-            screenBuffer_.Reset();
+        {
+            for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+                screenBuffers_[i].Reset();
+        }
     }
     else
     {
         diffBuffer_.Reset();
         normalBuffer_.Reset();
         depthBuffer_.Reset();
-        screenBuffer_.Reset();
+        for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+            screenBuffers_[i].Reset();
     }
 }
 
@@ -2120,7 +2130,8 @@ void Graphics::Release()
     diffBuffer_.Reset();
     normalBuffer_.Reset();
     depthBuffer_.Reset();
-    screenBuffer_.Reset();
+    for (unsigned i = 0; i < NUM_SCREEN_BUFFERS; ++i)
+        screenBuffers_[i].Reset();
     depthTextures_.Clear();
     
     // If GPU objects exist ie. it's a context delete/recreate, not Close(), tell them to save and release themselves

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

@@ -49,6 +49,8 @@ class VertexBuffer;
 
 typedef Map<Pair<ShaderVariation*, ShaderVariation*>, SharedPtr<ShaderProgram> > ShaderProgramMap;
 
+static const unsigned NUM_SCREEN_BUFFERS = 2;
+
 /// %Graphics subsystem. Manages the application window, rendering state and GPU resources.
 class Graphics : public Object
 {
@@ -334,7 +336,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() const { return screenBuffer_; }
+    Texture2D* GetScreenBuffer(unsigned index) const { return screenBuffers_[index]; }
     
     /// Add a GPU object to keep track of. Called by GPUObject.
     void AddGPUObject(GPUObject* object);
@@ -430,8 +432,8 @@ private:
     SharedPtr<Texture2D> normalBuffer_;
     /// Deferred rendering depth buffer.
     SharedPtr<Texture2D> depthBuffer_;
-    /// Screen buffer for post processing.
-    SharedPtr<Texture2D> screenBuffer_;
+    /// Screen buffers for post processing.
+    SharedPtr<Texture2D> screenBuffers_[NUM_SCREEN_BUFFERS];
     /// Shadow map depth texture format.
     unsigned shadowMapFormat_;
     /// Shadow map 24-bit depth texture format.

+ 0 - 14
Engine/Graphics/Renderer.cpp

@@ -257,13 +257,6 @@ 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) :
@@ -283,7 +276,6 @@ Renderer::Renderer(Context* context) :
     shadowMapHiresDepth_(false),
     reuseShadowMaps_(true),
     dynamicInstancing_(true),
-    edgeFilter_(EdgeFilterParameters(0.4f, 0.5f, 0.9f)),
     maxOccluderTriangles_(5000),
     occlusionBufferSize_(256),
     occluderSizeThreshold_(0.1f),
@@ -427,12 +419,6 @@ 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);

+ 0 - 33
Engine/Graphics/Renderer.h

@@ -131,33 +131,6 @@ enum DeferredLightPSVariation
     MAX_DEFERRED_LIGHT_PS_VARIATIONS
 };
 
-/// Deferred rendering edge filter parameters.
-struct EdgeFilterParameters
-{
-    /// Construct undefined.
-    EdgeFilterParameters()
-    {
-    }
-    
-    /// Construct with initial values.
-    EdgeFilterParameters(float radius, float threshold, float strength) :
-        radius_(radius),
-        threshold_(threshold),
-        strength_(strength)
-    {
-    }
-    
-    //! Validate parameters.
-    void Validate();
-    
-    //! Radius for calculating luminance gradient.
-    float radius_;
-    //! Luminance difference threshold needed to pass pixel.
-    float threshold_;
-    //! Filter strength.
-    float strength_;
-};
-
 /// High-level rendering subsystem. Manages drawing of 3D views.
 class Renderer : public Object
 {
@@ -197,8 +170,6 @@ 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.
@@ -235,8 +206,6 @@ 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.
@@ -423,8 +392,6 @@ private:
     bool reuseShadowMaps_;
     /// Dynamic instancing flag.
     bool dynamicInstancing_;
-    /// Deferred rendering edge filter parameters.
-    EdgeFilterParameters edgeFilter_;
     /// Maximum occluder triangles.
     int maxOccluderTriangles_;
     /// Occlusion buffer width.

+ 107 - 47
Engine/Graphics/View.cpp

@@ -66,7 +66,8 @@ View::View(Context* context) :
     camera_(0),
     zone_(0),
     renderTarget_(0),
-    depthStencil_(0)
+    depthStencil_(0),
+    jitterCounter_(0)
 {
     frame_.camera_ = 0;
 }
@@ -758,41 +759,24 @@ void View::RenderBatchesDeferred()
     Texture2D* normalBuffer = graphics_->GetNormalBuffer();
     Texture2D* depthBuffer = graphics_->GetDepthBuffer();
     
-    // Check for deferred antialiasing (edge filter) in deferred mode. Only use it on the main view (null rendertarget)
-    bool edgeFilter = !renderTarget_ && graphics_->GetMultiSample() > 1;
-    RenderSurface* renderBuffer = edgeFilter ? graphics_->GetScreenBuffer()->GetRenderSurface() : renderTarget_;
-    
-    // Calculate shader parameters needed only in deferred rendering
-    Vector3 nearVector, farVector;
-    camera_->GetFrustumSize(nearVector, farVector);
-    Vector4 viewportParams(farVector.x_, farVector.y_, farVector.z_, 0.0f);
-    
-    float gBufferWidth = (float)diffBuffer->GetWidth();
-    float gBufferHeight = (float)diffBuffer->GetHeight();
-    float widthRange = 0.5f * width_ / gBufferWidth;
-    float heightRange = 0.5f * height_ / gBufferHeight;
-    
-    // Hardware depth is non-linear in perspective views, so calculate the depth reconstruction parameters
-    float farClip = camera_->GetFarClip();
-    float nearClip = camera_->GetNearClip();
-    Vector4 depthReconstruct = Vector4::ZERO;
-    depthReconstruct.x_ = farClip / (farClip - nearClip);
-    depthReconstruct.y_ = -nearClip / (farClip - nearClip);
-    shaderParameters_[PSP_DEPTHRECONSTRUCT] = depthReconstruct;
-    
-    #ifdef USE_OPENGL
-    Vector4 bufferUVOffset(((float)screenRect_.left_) / gBufferWidth + widthRange,
-        ((float)screenRect_.top_) / gBufferHeight + heightRange, widthRange, heightRange);
-    #else
-    Vector4 bufferUVOffset((0.5f + (float)screenRect_.left_) / gBufferWidth + widthRange,
-        (0.5f + (float)screenRect_.top_) / gBufferHeight + heightRange, widthRange, heightRange);
-    #endif
-    
-    Vector4 viewportSize((float)screenRect_.left_ / gBufferWidth, (float)screenRect_.top_ / gBufferHeight,
-        (float)screenRect_.right_ / gBufferWidth, (float)screenRect_.bottom_ / gBufferHeight);
+    // Check for temporal antialiasing in deferred mode. Only use it on the main view (null rendertarget)
+    bool temporalAA = (!renderTarget_) && (graphics_->GetMultiSample() > 1);
+    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);
+    }
     
-    shaderParameters_[VSP_FRUSTUMSIZE] = viewportParams;
-    shaderParameters_[VSP_GBUFFEROFFSETS] = bufferUVOffset;
+    RenderSurface* renderBuffer = temporalAA ? graphics_->GetScreenBuffer(jitterCounter_ & 1)->GetRenderSurface() : renderTarget_;
     
     {
         // Clear and render the G-buffer
@@ -958,28 +942,53 @@ void View::RenderBatchesDeferred()
         RenderBatchQueue(transparentQueue_, true);
     }
     
-    // Render deferred antialiasing now if enabled
-    if (edgeFilter)
+    // Render temporal antialiasing now if enabled
+    if (temporalAA)
     {
-        PROFILE(RenderEdgeFilter);
+        PROFILE(RenderTemporalAA);
         
-        const EdgeFilterParameters& parameters = renderer_->GetEdgeFilter();
-        ShaderVariation* vs = renderer_->GetVertexShader("EdgeFilter");
-        ShaderVariation* ps = renderer_->GetPixelShader("EdgeFilter");
+        // Disable averaging if it is the first frame rendered in this view
+        float thisFrameWeight = jitterCounter_ < 2 ? 1.0f : 0.5f;
         
-        HashMap<StringHash, 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);
+        String vsName = "TemporalAA";
+        String psName = vsName;
+        if (camera_->IsOrthographic())
+        {
+            vsName += "_Ortho";
+            psName += "_Ortho";
+        }
+        else if (!graphics_->GetHardwareDepthSupport())
+            psName += "_Linear";
         
         graphics_->SetAlphaTest(false);
         graphics_->SetBlendMode(BLEND_REPLACE);
-        graphics_->SetDepthTest(CMP_ALWAYS),
+        graphics_->SetDepthTest(CMP_ALWAYS);
+        graphics_->SetDepthWrite(false);
+        graphics_->SetScissorTest(false);
         graphics_->SetStencilTest(false);
         graphics_->SetRenderTarget(0, renderTarget_);
         graphics_->SetDepthStencil(depthStencil_);
         graphics_->SetViewport(screenRect_);
-        graphics_->SetTexture(TU_DIFFBUFFER, graphics_->GetScreenBuffer());
-        DrawFullScreenQuad(*camera_, vs, ps, false, shaderParameters);
+        
+        // Pre-select the right shaders so that we can set shader parameters that can not go into the parameter map
+        // (matrices)
+        float gBufferWidth = (float)graphics_->GetWidth();
+        float gBufferHeight = (float)graphics_->GetHeight();
+        ShaderVariation* vertexShader = renderer_->GetVertexShader(vsName);
+        ShaderVariation* pixelShader = renderer_->GetPixelShader(psName);
+        graphics_->SetShaders(vertexShader, pixelShader);
+        graphics_->SetShaderParameter(VSP_CAMERAROT, camera_->GetWorldTransform().RotationMatrix());
+        graphics_->SetShaderParameter(PSP_CAMERAPOS, camera_->GetWorldPosition());
+        graphics_->SetShaderParameter(PSP_SAMPLEOFFSETS, Vector4(1.0f / gBufferWidth, 1.0f / gBufferHeight, thisFrameWeight, 1.0f - thisFrameWeight));
+        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());
+        
+        DrawFullScreenQuad(*camera_, vertexShader, pixelShader, false, shaderParameters_);
+        
+        // Store view transform for next frame
+        lastCameraView_ = camera_->GetInverseWorldTransform();
     }
 }
 
@@ -1934,12 +1943,63 @@ void View::CalculateShaderParameters()
     Vector4 fogParams(fogStart / farClip, fogEnd / farClip, 1.0f / (fogRange / farClip), 0.0f);
     Vector4 elapsedTime((time->GetTotalMSec() & 0x3fffff) / 1000.0f, 0.0f, 0.0f, 0.0f);
     
+    Vector4 depthMode = Vector4::ZERO;
+    if (camera_->IsOrthographic())
+    {
+        depthMode.x_ = 1.0f;
+        #ifdef USE_OPENGL
+        depthMode.z_ = 0.5f;
+        depthMode.w_ = 0.5f;
+        #else
+        depthMode.z_ = 1.0f;
+        #endif
+    }
+    else
+        depthMode.w_ = 1.0f / camera_->GetFarClip();
+    
     shaderParameters_.Clear();
+    shaderParameters_[VSP_DEPTHMODE] = depthMode;
     shaderParameters_[VSP_ELAPSEDTIME] = elapsedTime;
     shaderParameters_[PSP_AMBIENTCOLOR] = zone_->GetAmbientColor().ToVector4();
     shaderParameters_[PSP_ELAPSEDTIME] = elapsedTime;
     shaderParameters_[PSP_FOGCOLOR] = zone_->GetFogColor().ToVector4(),
     shaderParameters_[PSP_FOGPARAMS] = fogParams;
+    
+    if (mode_ == RENDER_DEFERRED)
+    {
+        // Calculate shader parameters needed only in deferred rendering
+        Vector3 nearVector, farVector;
+        camera_->GetFrustumSize(nearVector, farVector);
+        Vector4 viewportParams(farVector.x_, farVector.y_, farVector.z_, 0.0f);
+        
+        float gBufferWidth = (float)graphics_->GetWidth();
+        float gBufferHeight = (float)graphics_->GetHeight();
+        float widthRange = 0.5f * width_ / gBufferWidth;
+        float heightRange = 0.5f * height_ / gBufferHeight;
+        
+        // Hardware depth is non-linear in perspective views, so calculate the depth reconstruction parameters
+        float farClip = camera_->GetFarClip();
+        float nearClip = camera_->GetNearClip();
+        Vector4 depthReconstruct = Vector4::ZERO;
+        depthReconstruct.x_ = farClip / (farClip - nearClip);
+        depthReconstruct.y_ = -nearClip / (farClip - nearClip);
+        shaderParameters_[PSP_DEPTHRECONSTRUCT] = depthReconstruct;
+        
+        #ifdef USE_OPENGL
+        Vector4 bufferUVOffset(((float)screenRect_.left_) / gBufferWidth + widthRange,
+            ((float)screenRect_.top_) / gBufferHeight + heightRange, widthRange, heightRange);
+        #else
+        Vector4 bufferUVOffset((0.5f + (float)screenRect_.left_) / gBufferWidth + widthRange,
+            (0.5f + (float)screenRect_.top_) / gBufferHeight + heightRange, widthRange, heightRange);
+        #endif
+        
+        Vector4 viewportSize((float)screenRect_.left_ / gBufferWidth, (float)screenRect_.top_ / gBufferHeight,
+            (float)screenRect_.right_ / gBufferWidth, (float)screenRect_.bottom_ / gBufferHeight);
+        
+        shaderParameters_[VSP_FRUSTUMSIZE] = viewportParams;
+        shaderParameters_[VSP_GBUFFEROFFSETS] = bufferUVOffset;
+        shaderParameters_[PSP_GBUFFEROFFSETS] = bufferUVOffset;
+    }
 }
 
 void View::SetupLightBatch(Batch& batch, bool firstSplit)

+ 4 - 0
Engine/Graphics/View.h

@@ -216,6 +216,10 @@ private:
     int materialQuality_;
     /// Maximum number of occluder triangles.
     int maxOccluderTriangles_;
+    /// Jitter counter for temporal antialiasing.
+    int jitterCounter_;
+    /// Previous view matrix for temporal antialiasing.
+    Matrix3x4 lastCameraView_;
     /// Information of the frame being rendered.
     FrameInfo frame_;
     /// Combined bounding box of visible geometries.

+ 2 - 0
Readme.txt

@@ -34,6 +34,8 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D
   http://warp.povusers.org/SortComparison/
 - NVIDIA FXAA II for Consoles by Timothy Lottes
   http://timothylottes.blogspot.com/2011/04/nvidia-fxaa-ii-for-console.html
+- DX11 Perspective Matrix Jittering Temporal AA by yakiimo02 
+  http://www.yakiimo3d.com/2010/09/28/dx11-perspective-matrix-jittering-temporal-aa/
 
 Urho3D uses the following third-party libraries:
 - AngelScript 2.21.1 WIP (http://www.angelcode.com/angelscript/)

+ 1 - 1
SourceAssets/GLSLShaders/CMakeLists.txt

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

+ 0 - 73
SourceAssets/GLSLShaders/EdgeFilter.frag

@@ -1,73 +0,0 @@
-/*============================================================================
- 
-                  FXAA v2 CONSOLE by TIMOTHY LOTTES @ NVIDIA                                
-
-============================================================================*/
-
-// Adapted for Urho3D from http://timothylottes.blogspot.com/2011/04/nvidia-fxaa-ii-for-console.html
-
-#include "Uniforms.frag"
-#include "Samplers.frag"
-
-varying vec2 vScreenPos;
-
-void main()
-{
-    float FXAA_SUBPIX_SHIFT = 1.0/4.0; // Not used
-    float FXAA_SPAN_MAX = 8.0;
-    float FXAA_REDUCE_MUL = 1.0/8.0;
-    float FXAA_REDUCE_MIN = 1.0/128.0;
-
-    vec2 posOffset = cSampleOffsets.xy * cEdgeFilterParams.x;
-
-    vec3 rgbNW = texture2D(sDiffBuffer, vScreenPos + vec2(-posOffset.x, -posOffset.y)).rgb;
-    vec3 rgbNE = texture2D(sDiffBuffer, vScreenPos + vec2(posOffset.x, -posOffset.y)).rgb;
-    vec3 rgbSW = texture2D(sDiffBuffer, vScreenPos + vec2(-posOffset.x, posOffset.y)).rgb;
-    vec3 rgbSE = texture2D(sDiffBuffer, vScreenPos + vec2(posOffset.x, posOffset.y)).rgb;
-    vec3 rgbM  = texture2D(sDiffBuffer, vScreenPos).rgb;
-
-    vec3 luma = vec3(0.299, 0.587, 0.114);
-    float lumaNW = dot(rgbNW, luma);
-    float lumaNE = dot(rgbNE, luma);
-    float lumaSW = dot(rgbSW, luma);
-    float lumaSE = dot(rgbSE, luma);
-    float lumaM  = dot(rgbM,  luma);
-
-    float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
-    float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
-
-    if (((lumaMax - lumaMin) / lumaMin) >= cEdgeFilterParams.y)
-    {
-        vec2 dir;
-        dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
-        dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));
-
-        float dirReduce = max(
-            (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL),
-            FXAA_REDUCE_MIN);
-        float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
-        dir = min(vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),
-              max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
-              dir * rcpDirMin)) * cSampleOffsets.xy;
-
-        dir *= cEdgeFilterParams.z;
-
-        vec3 rgbA = (1.0/2.0) * (
-            texture2D(sDiffBuffer, vScreenPos + dir * (1.0/3.0 - 0.5)).xyz +
-            texture2D(sDiffBuffer, vScreenPos + dir * (2.0/3.0 - 0.5)).xyz);
-        vec3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
-            texture2D(sDiffBuffer, vScreenPos + dir * (0.0/3.0 - 0.5)).xyz +
-            texture2D(sDiffBuffer, vScreenPos + dir * (3.0/3.0 - 0.5)).xyz);
-        float lumaB = dot(rgbB, luma);
-
-        vec3 rgbOut;
-        if((lumaB < lumaMin) || (lumaB > lumaMax))
-            rgbOut = rgbA;
-        else
-            rgbOut = rgbB;
-    
-        gl_FragColor = vec4(rgbOut, 1.0);
-    }
-    else
-        gl_FragColor = vec4(rgbM, 1.0);
-}

+ 0 - 4
SourceAssets/GLSLShaders/EdgeFilter.xml

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

+ 59 - 0
SourceAssets/GLSLShaders/TemporalAA.frag

@@ -0,0 +1,59 @@
+#include "Uniforms.frag"
+#include "Samplers.frag"
+
+varying vec2 vScreenPos;
+varying vec3 vFarRay;
+#ifdef ORTHO
+varying vec3 vNearRay;
+#endif
+
+void main()
+{
+    vec2 hOffset = vec2(cSampleOffsets.x, 0);
+    vec2 vOffset = vec2(0, cSampleOffsets.y);
+    vec3 current = texture2D(sDiffBuffer, vScreenPos).rgb;
+
+    // Reconstruct position for this frame's pixel
+    #ifdef ORTHO
+        float depth = texture2D(sDepthBuffer, vScreenPos).r;
+        vec3 worldPos = mix(vNearRay, vFarRay, depth) + cCameraPosPS;
+    #else
+        #ifdef LINEAR
+            float depth = texture2D(sDepthBuffer, vScreenPos).r;
+        #else
+            float depth = ReconstructDepth(texture2D(sDepthBuffer, vScreenPos).r);
+        #endif
+        vec3 worldPos = vFarRay * depth + cCameraPosPS;
+    #endif
+
+    // Calculate G-buffer position for pixel on last frame
+    vec4 clipPosLast = cViewProjPS * vec4(worldPos, 1.0);
+
+    #ifdef ORTHO
+        vec2 screenPosLast = vec2(
+            clipPosLast.x * cGBufferOffsetsPS.z + cGBufferOffsetsPS.x,
+            clipPosLast.y * cGBufferOffsetsPS.w + cGBufferOffsetsPS.y);
+    #else
+       vec2 screenPosLast = vec2(
+            clipPosLast.x / clipPosLast.w * cGBufferOffsetsPS.z + cGBufferOffsetsPS.x,
+            clipPosLast.y / clipPosLast.w * cGBufferOffsetsPS.w + cGBufferOffsetsPS.y);
+    #endif
+
+    vec3 last = texture2D(sNormalBuffer, screenPosLast).rgb;
+    vec3 color = cSampleOffsets.z * current + cSampleOffsets.w * last;
+
+    // To eliminate ghosting, clamp RGB values to the pixel's neighbours
+    // This is rather expensive, so only do it if there is a large color difference
+    if (dot(abs(current - last), 1.0) > 0.25)
+    /{
+        vec3 left = texture2D(sDiffBuffer, vScreenPos - hOffset).rgb;
+        vec3 right = texture2D(sDiffBuffer, vScreenPos + hOffset).rgb;
+        vec3 up = texture2D(sDiffBuffer, vScreenPos - vOffset).rgb;
+        vec3 down = texture2D(sDiffBuffer, vScreenPos + vOffset).rgb;
+        vec3 minColor = min(min(left, right), min(up, down));
+        vec3 maxColor = max(max(left, right), max(up, down));
+        gl_FragColor = vec4(clamp(color, minColor, maxColor), 1.0);
+    }
+    else
+        gl_FragColor = vec4(color, 1.0);
+}

+ 9 - 0
SourceAssets/GLSLShaders/EdgeFilter.vert → SourceAssets/GLSLShaders/TemporalAA.vert

@@ -3,9 +3,18 @@
 #include "ScreenPos.vert"
 
 varying vec2 vScreenPos;
+varying vec3 vFarRay;
+#ifdef ORTHO
+varying vec3 vNearRay;
+#endif
 
 void main()
 {
     GetPosition(iPosition, gl_Position);
     vScreenPos = GetScreenPosPreDiv(gl_Position);
+
+    vFarRay = GetFarRay(gl_Position);
+    #ifdef ORTHO
+        vNearRay = GetNearRay(gl_Position);
+    #endif
 }

+ 9 - 0
SourceAssets/GLSLShaders/TemporalAA.xml

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

+ 3 - 1
SourceAssets/GLSLShaders/Uniforms.frag

@@ -1,9 +1,10 @@
 uniform vec3 cAmbientColor;
+uniform vec3 cCameraPosPS;
 uniform vec2 cDepthReconstruct;
-uniform vec3 cEdgeFilterParams;
 uniform vec2 cElapsedTimePS;
 uniform vec4 cFogParams;
 uniform vec3 cFogColor;
+uniform vec4 cGBufferOffsetsPS;
 uniform float cLightAtten;
 uniform vec4 cLightColor;
 uniform vec3 cLightDir;
@@ -17,3 +18,4 @@ 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})

+ 0 - 82
SourceAssets/HLSLShaders/EdgeFilter.hlsl

@@ -1,82 +0,0 @@
-/*============================================================================
- 
-                  FXAA v2 CONSOLE by TIMOTHY LOTTES @ NVIDIA                                
-
-============================================================================*/
-
-// Adapted for Urho3D from http://timothylottes.blogspot.com/2011/04/nvidia-fxaa-ii-for-console.html
-
-#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)
-{
-    float FXAA_SUBPIX_SHIFT = 1.0/4.0; // Not used
-    float FXAA_SPAN_MAX = 8.0;
-    float FXAA_REDUCE_MUL = 1.0/8.0;
-    float FXAA_REDUCE_MIN = 1.0/128.0;
-
-    float2 posOffset = cSampleOffsets.xy * cEdgeFilterParams.x;
-
-    float3 rgbNW = Sample(sDiffBuffer, iScreenPos + float2(-posOffset.x, -posOffset.y)).rgb;
-    float3 rgbNE = Sample(sDiffBuffer, iScreenPos + float2(posOffset.x, -posOffset.y)).rgb;
-    float3 rgbSW = Sample(sDiffBuffer, iScreenPos + float2(-posOffset.x, posOffset.y)).rgb;
-    float3 rgbSE = Sample(sDiffBuffer, iScreenPos + float2(posOffset.x, posOffset.y)).rgb;
-    float3 rgbM  = Sample(sDiffBuffer, iScreenPos).rgb;
-
-    float3 luma = float3(0.299, 0.587, 0.114);
-    float lumaNW = dot(rgbNW, luma);
-    float lumaNE = dot(rgbNE, luma);
-    float lumaSW = dot(rgbSW, luma);
-    float lumaSE = dot(rgbSE, luma);
-    float lumaM  = dot(rgbM,  luma);
-
-    float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));
-    float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));
-
-    if (((lumaMax - lumaMin) / lumaMin) >= cEdgeFilterParams.y)
-    {
-        float2 dir;
-        dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
-        dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));
-    
-        float dirReduce = max(
-            (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL),
-            FXAA_REDUCE_MIN);
-        float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce);
-        dir = min(float2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),
-              max(float2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),
-              dir * rcpDirMin)) * cSampleOffsets.xy;
-    
-        dir *= cEdgeFilterParams.z;
-    
-        float3 rgbA = (1.0/2.0) * (
-            Sample(sDiffBuffer, iScreenPos + dir * (1.0/3.0 - 0.5)).xyz +
-            Sample(sDiffBuffer, iScreenPos + dir * (2.0/3.0 - 0.5)).xyz);
-        float3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
-            Sample(sDiffBuffer, iScreenPos + dir * (0.0/3.0 - 0.5)).xyz +
-            Sample(sDiffBuffer, iScreenPos + dir * (3.0/3.0 - 0.5)).xyz);
-        float lumaB = dot(rgbB, luma);
-        
-        float3 rgbOut;
-        if((lumaB < lumaMin) || (lumaB > lumaMax))
-            rgbOut = rgbA;
-        else
-            rgbOut = rgbB;
-
-        oColor = float4(rgbOut, 1.0);
-    }
-    else
-        oColor = float4(rgbM, 1.0);
-}

+ 0 - 4
SourceAssets/HLSLShaders/EdgeFilter.xml

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

+ 76 - 0
SourceAssets/HLSLShaders/TemporalAA.hlsl

@@ -0,0 +1,76 @@
+#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)
+{
+    float2 hOffset = float2(cSampleOffsets.x, 0);
+    float2 vOffset = float2(0, cSampleOffsets.y);
+    float3 current = Sample(sDiffBuffer, iScreenPos).rgb;
+
+    // Reconstruct position for this frame's pixel
+    #ifdef ORTHO
+        float depth = Sample(sDepthBuffer, iScreenPos).r;
+        float3 worldPos = lerp(iNearRay, iFarRay, depth) + cCameraPosPS;
+    #else
+        #ifdef LINEAR
+            float depth = Sample(sDepthBuffer, iScreenPos).r;
+        #else
+            float depth = ReconstructDepth(Sample(sDepthBuffer, iScreenPos).r);
+        #endif
+        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
+
+    float3 last = Sample(sNormalBuffer, screenPosLast).rgb;
+    float3 color = cSampleOffsets.z * current + cSampleOffsets.w * last;
+
+    // To eliminate ghosting, clamp RGB values to the pixel's neighbours
+    // This is rather expensive, so only do it if there is a large color difference
+    if (dot(abs(current - last), 1.0) > 0.25)
+    {
+        float3 left = Sample(sDiffBuffer, iScreenPos - hOffset).rgb;
+        float3 right = Sample(sDiffBuffer, iScreenPos + hOffset).rgb;
+        float3 up = Sample(sDiffBuffer, iScreenPos - vOffset).rgb;
+        float3 down = Sample(sDiffBuffer, iScreenPos + vOffset).rgb;
+        float3 minColor = min(min(left, right), min(up, down));
+        float3 maxColor = max(max(left, right), max(up, down));
+        oColor = float4(clamp(color, minColor, maxColor), 1.0);
+    }
+    else
+        oColor = float4(color, 1.0);
+}

+ 9 - 0
SourceAssets/HLSLShaders/TemporalAA.xml

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

+ 17 - 15
SourceAssets/HLSLShaders/Uniforms.hlsl

@@ -17,21 +17,23 @@ uniform float4x3 cSkinMatrices[64] : register(C27);
 
 // Pixel shader parameters
 uniform float3 cAmbientColor : register(C0);
-uniform float2 cDepthReconstruct : register(C1);
-uniform float4 cEdgeFilterParams : register(C2);
+uniform float3 cCameraPosPS : register(C1);
+uniform float2 cDepthReconstruct : register(C2);
 uniform float2 cElapsedTimePS : register(C3);
 uniform float4 cFogParams : register(C4);
 uniform float3 cFogColor : register(C5);
-uniform float cLightAtten : register(C6);
-uniform float4 cLightColor : register(C7);
-uniform float3 cLightDir : register(C8);
-uniform float3 cLightPos : register(C9);
-uniform float4 cLightSplits : register(C10);
-uniform float3x3 cLightVecRot: register(C11);
-uniform float4 cMatDiffColor : register(C14);
-uniform float3 cMatEmissiveColor : register(C15);
-uniform float2 cMatSpecProperties : register(C16);
-uniform float4 cSampleOffsets : register(C17);
-uniform float2 cShadowIntensity : register(C18);
-uniform float4x4 cShadowProjPS : register(C19);
-uniform float4x4 cSpotProjPS : register(C23);
+uniform float4 cGBufferOffsetsPS : register(C6);
+uniform float cLightAtten : register(C7);
+uniform float4 cLightColor : register(C8);
+uniform float3 cLightDir : register(C9);
+uniform float3 cLightPos : register(C10);
+uniform float4 cLightSplits : register(C11);
+uniform float3x3 cLightVecRot: register(C12);
+uniform float4 cMatDiffColor : register(C15);
+uniform float3 cMatEmissiveColor : register(C16);
+uniform float2 cMatSpecProperties : register(C17);
+uniform float4 cSampleOffsets : register(C18);
+uniform float2 cShadowIntensity : register(C19);
+uniform float4x4 cShadowProjPS : register(C20);
+uniform float4x4 cSpotProjPS : register(C24);
+uniform float4x4 cViewProjPS : register(C24);