Browse Source

Switched to NVIDIA FXAA II based edge filter.

Lasse Öörni 14 years ago
parent
commit
83bf3d1464

+ 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 deferred rendering a post-process edge filter will be used instead.
+(*) Only forward rendering supports hardware multisampling. In deferred rendering a post-process edge filter based on NVIDIA FXAA II will be used instead.
 
 
 \page Structure Overall structure

+ 2 - 2
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 as incompatible in deferred rendering mode; instead it is replaced by a post-process edge filter.
+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.
 
 When setting the initial screen mode, Graphics does a few checks:
 
@@ -621,7 +621,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 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.
+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.
 
 \section ForwardDeferred_Conclusion Conclusion
 

+ 1 - 0
Docs/Urho3D.dox

@@ -52,6 +52,7 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org/) and Horde3D (http://
 - Euler Angle Formulas by David Eberly (http://www.geometrictools.com/Documentation/EulerAngles.pdf)
 - 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)
 
 Urho3D uses the following third-party libraries:
 

+ 1 - 1
Engine/Graphics/Renderer.cpp

@@ -266,7 +266,7 @@ Renderer::Renderer(Context* context) :
     shadowMapHiresDepth_(false),
     reuseShadowMaps_(true),
     dynamicInstancing_(true),
-    edgeFilter_(EdgeFilterParameters(1.5f, 1.0f, 0.66f)),
+    edgeFilter_(EdgeFilterParameters(0.5f, 1.0f, 0.75f)),
     maxOccluderTriangles_(5000),
     occlusionBufferSize_(256),
     occluderSizeThreshold_(0.1f),

+ 1 - 1
Engine/Graphics/View.cpp

@@ -985,7 +985,7 @@ void View::RenderBatchesDeferred()
         
         const EdgeFilterParameters& parameters = renderer_->GetEdgeFilter();
         ShaderVariation* vs = renderer_->GetVertexShader("EdgeFilter");
-        ShaderVariation* ps = renderer_->GetPixelShader("EdgeFilter");  
+        ShaderVariation* ps = renderer_->GetPixelShader("EdgeFilter");
         
         HashMap<ShaderParameter, Vector4> shaderParameters(shaderParameters_);
         shaderParameters[PSP_EDGEFILTERPARAMS] = Vector4(parameters.radius_, parameters.threshold_, parameters.strength_, 0.0f);

+ 2 - 0
Readme.txt

@@ -32,6 +32,8 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D
   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
 
 Urho3D uses the following third-party libraries:
 - AngelScript 2.21.0 WIP (http://www.angelcode.com/angelscript/)

+ 57 - 30
SourceAssets/GLSLShaders/EdgeFilter.frag

@@ -1,3 +1,11 @@
+/*============================================================================
+ 
+                  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"
 
@@ -5,37 +13,56 @@ varying vec2 vScreenPos;
 
 void main()
 {
-    // Shader based on the blog post http://www.gamestart3d.com/blog/ssaa-antialiasing
-    vec2 offsets = cEdgeFilterParams.x * cSampleOffsets.xy;
+    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;
 
-    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 - down, right - left) / base;
-    float len = length(normal);
+    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)));
+
+    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);
     
-    // 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);
-    }
+    vec3 rgbOut;
+    if((lumaB < lumaMin) || (lumaB > lumaMax))
+        rgbOut = rgbA;
     else
-        gl_FragColor = color;
+        rgbOut = rgbB;
+    
+    gl_FragColor = vec4(rgbOut, 1.0);
 }

+ 0 - 7
SourceAssets/GLSLShaders/Samplers.frag

@@ -27,10 +27,3 @@ 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));
-}

+ 57 - 29
SourceAssets/HLSLShaders/EdgeFilter.hlsl

@@ -1,3 +1,11 @@
+/*============================================================================
+ 
+                  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"
@@ -14,36 +22,56 @@ void VS(float4 iPos : POSITION,
 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;
+    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;
 
-    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 - down, right - left) / base;
-    float len = length(normal);
-    
-    // Clamp normal to maximum
-    if (len > 1)
-        normal /= len;
+    float2 posOffset = cSampleOffsets.xy * cEdgeFilterParams.x;
+
+    float3 rgbNW = tex2D(sDiffBuffer, iScreenPos + float2(-posOffset.x, -posOffset.y)).rgb;
+    float3 rgbNE = tex2D(sDiffBuffer, iScreenPos + float2(posOffset.x, -posOffset.y)).rgb;
+    float3 rgbSW = tex2D(sDiffBuffer, iScreenPos + float2(-posOffset.x, posOffset.y)).rgb;
+    float3 rgbSE = tex2D(sDiffBuffer, iScreenPos + float2(posOffset.x, posOffset.y)).rgb;
+    float3 rgbM  = tex2D(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)));
+
+    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) * (
+        tex2D(sDiffBuffer, iScreenPos + dir * (1.0/3.0 - 0.5)).xyz +
+        tex2D(sDiffBuffer, iScreenPos + dir * (2.0/3.0 - 0.5)).xyz);
+    float3 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (
+        tex2D(sDiffBuffer, iScreenPos + dir * (0.0/3.0 - 0.5)).xyz +
+        tex2D(sDiffBuffer, iScreenPos + dir * (3.0/3.0 - 0.5)).xyz);
+    float lumaB = dot(rgbB, luma);
     
-    // 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);
-    }
+    float3 rgbOut;
+    if((lumaB < lumaMin) || (lumaB > lumaMax))
+        rgbOut = rgbA;
     else
-        oColor = color;
+        rgbOut = rgbB;
+    
+    oColor = float4(rgbOut, 1.0);
 }

+ 0 - 7
SourceAssets/HLSLShaders/Samplers.hlsl

@@ -37,10 +37,3 @@ 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);
-}