diff --git a/Rendering/Core/vtkRenderer.cxx b/Rendering/Core/vtkRenderer.cxx
index c17207335ffe868ca0e56ce2185d4b2a494d8c31..b8a5bfdd38d84e1646ce539637ec03a337e8ff2e 100644
--- a/Rendering/Core/vtkRenderer.cxx
+++ b/Rendering/Core/vtkRenderer.cxx
@@ -117,6 +117,15 @@ vtkRenderer::vtkRenderer()
 
   this->GL2PSSpecialPropCollection = NULL;
 
+  this->UseFXAA = false;
+  this->FXAARelativeContrastThreshold = 1.f / 8.f;
+  this->FXAAHardContrastThreshold = 1.f / 16.f;
+  this->FXAASubpixelBlendLimit = 3.f / 4.f;
+  this->FXAASubpixelContrastThreshold = 1.f / 4.f;
+  this->FXAAEndpointSearchIterations = 12;
+  this->FXAAUseHighQualityEndpoints = true;
+  this->FXAADebugOption = 0;
+
   this->UseShadows = 0;
 
   this->UseHiddenLineRemoval = 0;
diff --git a/Rendering/Core/vtkRenderer.h b/Rendering/Core/vtkRenderer.h
index 0f394868ff785e3875de0fd76b6d52677c691650..0809201dddfb0c08740ebc8d58c378b97109f604 100644
--- a/Rendering/Core/vtkRenderer.h
+++ b/Rendering/Core/vtkRenderer.h
@@ -528,6 +528,31 @@ public:
   // method to release graphics resources in any derived renderers.
   virtual void ReleaseGraphicsResources(vtkWindow *);
 
+  // Description:
+  // Turn on/off FXAA anti-aliasing, if supported. Initial value is off.
+  vtkSetMacro(UseFXAA, bool)
+  vtkGetMacro(UseFXAA, bool)
+  vtkBooleanMacro(UseFXAA, bool)
+
+  // Description:
+  // Tuning parameters for FXAA. See vtkOpenGLFXAAFilter.h for documentation
+  // and suggested values.
+  vtkSetClampMacro(FXAARelativeContrastThreshold, float, 0.f, 1.f)
+  vtkGetMacro(FXAARelativeContrastThreshold, float)
+  vtkSetClampMacro(FXAAHardContrastThreshold, float, 0.f, 1.f)
+  vtkGetMacro(FXAAHardContrastThreshold, float)
+  vtkSetClampMacro(FXAASubpixelBlendLimit, float, 0.f, 1.f)
+  vtkGetMacro(FXAASubpixelBlendLimit, float)
+  vtkSetClampMacro(FXAASubpixelContrastThreshold, float, 0.f, 1.f)
+  vtkGetMacro(FXAASubpixelContrastThreshold, float)
+  vtkSetClampMacro(FXAAEndpointSearchIterations, int, 0, VTK_INT_MAX)
+  vtkGetMacro(FXAAEndpointSearchIterations, int)
+  vtkSetMacro(FXAAUseHighQualityEndpoints, bool)
+  vtkGetMacro(FXAAUseHighQualityEndpoints, bool)
+  vtkBooleanMacro(FXAAUseHighQualityEndpoints, bool)
+  vtkSetMacro(FXAADebugOption, int)
+  vtkGetMacro(FXAADebugOption, int)
+
   // Description:
   // Turn on/off rendering of shadows if supported
   // Initial value is off.
@@ -691,6 +716,22 @@ protected:
   // This is only used internally.
   vtkCamera *GetActiveCameraAndResetIfCreated();
 
+  // Description:
+  // If this flag is on and the rendering engine supports it, FXAA will be used
+  // to antialias the scene. Default is off.
+  bool UseFXAA;
+
+  // Description:
+  // Parameters for FXAA. See vtkOpenGLFXAAFilter.h for documentation and
+  // suggested values.
+  float FXAARelativeContrastThreshold;
+  float FXAAHardContrastThreshold;
+  float FXAASubpixelBlendLimit;
+  float FXAASubpixelContrastThreshold;
+  int FXAAEndpointSearchIterations;
+  bool FXAAUseHighQualityEndpoints;
+  int FXAADebugOption;
+
   // Description:
   // If this flag is on and the rendering engine supports it render shadows
   // Initial value is off.
diff --git a/Rendering/OpenGL2/CMakeLists.txt b/Rendering/OpenGL2/CMakeLists.txt
index 666083665379d4cd61a2240be5e4ec17d9256c90..3e16cae7c2b36a521cd220dc4d466ce505f9a74a 100644
--- a/Rendering/OpenGL2/CMakeLists.txt
+++ b/Rendering/OpenGL2/CMakeLists.txt
@@ -33,6 +33,7 @@ set(Module_SRCS
   vtkOpenGLActor.cxx
   vtkOpenGLBufferObject.cxx
   vtkOpenGLCamera.cxx
+  vtkOpenGLFXAAFilter.cxx
   vtkOpenGLGL2PSHelper.cxx
   vtkOpenGLGlyph3DHelper.cxx
   vtkOpenGLGlyph3DMapper.cxx
@@ -148,6 +149,7 @@ set(shader_files
   glsl/vtkEDLBilateralFilterFS.glsl
   glsl/vtkEDLComposeFS.glsl
   glsl/vtkEDLShadeFS.glsl
+  glsl/vtkFXAAFilterFS.glsl
   glsl/vtkGaussianBlurPassFS.glsl
   glsl/vtkGaussianBlurPassVS.glsl
   glsl/vtkGlyph3DVS.glsl
diff --git a/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt b/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt
index e6940ffeadb9497c156338fea01565fdee28fc31..4811005ac6696d569735d8516463faf0a75b2106 100644
--- a/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt
+++ b/Rendering/OpenGL2/Testing/Cxx/CMakeLists.txt
@@ -6,6 +6,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
   TestDepthOfFieldPass.cxx
   TestDepthPeelingPass.cxx
   TestEDLPass.cxx
+  TestFXAAFilter.cxx
   TestGaussianBlurPass.cxx
   TestLightingMapLuminancePass.cxx
   TestLightingMapNormalsPass.cxx
diff --git a/Rendering/OpenGL2/Testing/Cxx/TestFXAAFilter.cxx b/Rendering/OpenGL2/Testing/Cxx/TestFXAAFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..d85866df083465fbdadfe8b5ba462ae1f790c106
--- /dev/null
+++ b/Rendering/OpenGL2/Testing/Cxx/TestFXAAFilter.cxx
@@ -0,0 +1,173 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+//
+// This test is unlikely to fail if FXAA isn't working, but can be used to
+// quickly check the same scene with/without FXAA enabled.
+//
+
+#include "vtkActor.h"
+#include "vtkCamera.h"
+#include "vtkConeSource.h"
+#include "vtkCylinderSource.h"
+#include "vtkDiskSource.h"
+#include "vtkLineSource.h"
+#include "vtkNew.h"
+#include "vtkOpenGLRenderer.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkRegressionTestImage.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderWindowInteractor.h"
+#include "vtkSphereSource.h"
+#include "vtkTextActor.h"
+#include "vtkTextProperty.h"
+
+namespace {
+
+void BuildRenderer(vtkRenderer *renderer, int widthBias)
+{
+  const size_t NUM_LINES = 10;
+
+  vtkNew<vtkLineSource> lines[NUM_LINES];
+  vtkNew<vtkPolyDataMapper> mappers[NUM_LINES];
+  vtkNew<vtkActor> actors[NUM_LINES];
+  for (size_t i = 0; i < NUM_LINES; ++i)
+    {
+    double c = static_cast<double>(2 * i) /
+               static_cast<double>(NUM_LINES - 1) - 1.;
+    lines[i]->SetPoint1(-1,  c, 0.);
+    lines[i]->SetPoint2( 1, -c, 0.);
+
+    mappers[i]->SetInputConnection(lines[i]->GetOutputPort());
+
+    actors[i]->SetMapper(mappers[i].Get());
+    actors[i]->GetProperty()->SetColor(0., 1., 0.);
+    actors[i]->GetProperty()->SetRepresentationToWireframe();
+    actors[i]->GetProperty()->SetLineWidth((i + widthBias) % 2 ? 1 : 3);
+    renderer->AddActor(actors[i].Get());
+    }
+
+  vtkNew<vtkSphereSource> sphere;
+  sphere->SetCenter(0., 0.6, 0.);
+  sphere->SetThetaResolution(80);
+  sphere->SetPhiResolution(80);
+  sphere->SetRadius(0.4);
+  vtkNew<vtkPolyDataMapper> sphereMapper;
+  sphereMapper->SetInputConnection(sphere->GetOutputPort());
+  vtkNew<vtkActor> sphereActor;
+  sphereActor->SetMapper(sphereMapper.Get());
+  sphereActor->GetProperty()->SetColor(0.9, 0.4, 0.2);
+  sphereActor->GetProperty()->SetAmbient(.6);
+  sphereActor->GetProperty()->SetDiffuse(.4);
+  renderer->AddActor(sphereActor.Get());
+
+  vtkNew<vtkConeSource> cone;
+  cone->SetCenter(0., 0.5, -0.5);
+  cone->SetResolution(160);
+  cone->SetRadius(.9);
+  cone->SetHeight(0.9);
+  cone->SetDirection(0., -1., 0.);
+  vtkNew<vtkPolyDataMapper> coneMapper;
+  coneMapper->SetInputConnection(cone->GetOutputPort());
+  vtkNew<vtkActor> coneActor;
+  coneActor->SetMapper(coneMapper.Get());
+  coneActor->GetProperty()->SetColor(0.9, .6, 0.8);
+  coneActor->GetProperty()->SetAmbient(.6);
+  coneActor->GetProperty()->SetDiffuse(.4);
+  renderer->AddActor(coneActor.Get());
+
+  vtkNew<vtkDiskSource> disk;
+  disk->SetCircumferentialResolution(80);
+  disk->SetInnerRadius(0);
+  disk->SetOuterRadius(0.5);
+  vtkNew<vtkPolyDataMapper> diskMapper;
+  diskMapper->SetInputConnection(disk->GetOutputPort());
+  vtkNew<vtkActor> diskActor;
+  diskActor->SetPosition(0., -0.5, -0.5);
+  diskActor->SetMapper(diskMapper.Get());
+  diskActor->GetProperty()->SetColor(.3, .1, .4);
+  diskActor->GetProperty()->SetAmbient(.6);
+  diskActor->GetProperty()->SetDiffuse(.4);
+  renderer->AddActor(diskActor.Get());
+
+  vtkNew<vtkCylinderSource> cyl;
+  cyl->SetCenter(0., -.5, 0.);
+  cyl->SetHeight(.6);
+  cyl->SetRadius(.2);
+  cyl->SetResolution(80);
+  vtkNew<vtkPolyDataMapper> cylMapper;
+  cylMapper->SetInputConnection(cyl->GetOutputPort());
+  vtkNew<vtkActor> cylActor;
+  cylActor->SetOrigin(cyl->GetCenter());
+  cylActor->RotateWXYZ(35, -0.2, 0., 1.);
+  cylActor->SetMapper(cylMapper.Get());
+  cylActor->GetProperty()->SetColor(0.3, .9, .4);
+  cylActor->GetProperty()->SetAmbient(.6);
+  cylActor->GetProperty()->SetDiffuse(.4);
+  renderer->AddActor(cylActor.Get());
+
+  renderer->SetBackground(0., 0., 0.);
+  renderer->GetActiveCamera()->ParallelProjectionOn();
+  renderer->ResetCamera();
+  renderer->ResetCameraClippingRange();
+  renderer->GetActiveCamera()->SetParallelScale(0.9);
+}
+
+} // end anon namespace
+
+int TestFXAAFilter(int argc, char *argv[])
+{
+  vtkNew<vtkRenderWindowInteractor> iren;
+  vtkNew<vtkRenderWindow> renWin;
+  renWin->SetMultiSamples(0);
+  iren->SetRenderWindow(renWin.Get());
+
+  vtkNew<vtkRenderer> renderer;
+  vtkNew<vtkRenderer> rendererFXAA;
+  rendererFXAA->UseFXAAOn();
+
+  vtkNew<vtkTextActor> label;
+  label->SetInput("No FXAA");
+  label->GetTextProperty()->SetFontSize(20);
+  label->GetTextProperty()->SetJustificationToCentered();
+  label->GetTextProperty()->SetVerticalJustificationToBottom();
+  label->SetPosition(85, 10);
+  renderer->AddActor2D(label.Get());
+
+  vtkNew<vtkTextActor> labelFXAA;
+  labelFXAA->SetInput("FXAA");
+  labelFXAA->GetTextProperty()->SetFontSize(20);
+  labelFXAA->GetTextProperty()->SetJustificationToCentered();
+  labelFXAA->GetTextProperty()->SetVerticalJustificationToBottom();
+  labelFXAA->SetPosition(85, 10);
+  rendererFXAA->AddActor2D(labelFXAA.Get());
+
+  renderer->SetViewport(0., 0., .5, 1.);
+  BuildRenderer(renderer.Get(), 0);
+  renWin->AddRenderer(renderer.Get());
+
+  rendererFXAA->SetViewport(.5, 0., 1., 1.);
+  BuildRenderer(rendererFXAA.Get(), 1);
+  renWin->AddRenderer(rendererFXAA.Get());
+
+  renWin->SetSize(1000, 500);
+  renWin->Render();
+
+  int retVal = vtkRegressionTestImage(renWin.Get());
+  if (retVal == vtkRegressionTester::DO_INTERACTOR)
+    {
+    iren->Start();
+    }
+  return !retVal;
+}
diff --git a/Rendering/OpenGL2/Testing/Data/Baseline/TestFXAAFilter.png.md5 b/Rendering/OpenGL2/Testing/Data/Baseline/TestFXAAFilter.png.md5
new file mode 100644
index 0000000000000000000000000000000000000000..16ccf2d14480b1f9a38daaab55a2c8a3da803cd0
--- /dev/null
+++ b/Rendering/OpenGL2/Testing/Data/Baseline/TestFXAAFilter.png.md5
@@ -0,0 +1 @@
+eb48d32d6179d54d4f259ca96c5b70a1
diff --git a/Rendering/OpenGL2/glsl/vtkFXAAFilterFS.glsl b/Rendering/OpenGL2/glsl/vtkFXAAFilterFS.glsl
new file mode 100644
index 0000000000000000000000000000000000000000..c43f07a1bf2d62f80e57905a9eab69f154dfbd1b
--- /dev/null
+++ b/Rendering/OpenGL2/glsl/vtkFXAAFilterFS.glsl
@@ -0,0 +1,788 @@
+//VTK::System::Dec
+
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkFXAAFilterFS.glsl
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+// Fragment shader for vtkOpenGLFXAAFilter.
+//
+// Based on the following implementation and description:
+//
+// Whitepaper:
+// http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf
+//
+// Sample implementation:
+// https://github.com/NVIDIAGameWorks/GraphicsSamples/blob/master/samples/es3-kepler/FXAA/FXAA3_11.h
+
+//VTK::Output::Dec
+
+//======================== Debugging Options: ==================================
+
+// Output a greyscale image showing the detected amount of subpixel aliasing.
+//#define FXAA_DEBUG_SUBPIXEL_ALIASING
+
+// Output vertical edges in red, and horizontal edges in blue.
+//#define FXAA_DEBUG_EDGE_DIRECTION
+
+// Output (number of steps taken) / (EndpointSearchIterations). Negative steps
+// in the red channel, positive steps in the blue.
+//#define FXAA_DEBUG_EDGE_NUM_STEPS
+
+// Output degrees of red if the edge is near the negative edge endpoint, or
+// shades of blue if near the positive edge endpoint. Pixels near an edge but
+// not eligible for edgeAA (e.g. they are on the unaliased side of an edge)
+// are shown in yellow.
+//#define FXAA_DEBUG_EDGE_DISTANCE
+
+// Output the length of the edge anti-aliasing offset vector in the red channel.
+//#define FXAA_DEBUG_EDGE_SAMPLE_OFFSET
+
+// Only apply a single form of anti-aliasing:
+// 1 - Only apply sub-pixel anti-aliasing.
+// 2 - Only apply edge anti-aliasing.
+// Other / undefined - Apply both sub-pixel and edge anti-aliasing.
+//#define FXAA_DEBUG_ONLY_SUBPIX_AA
+//#define FXAA_DEBUG_ONLY_EDGE_AA
+
+// Replacement stub for vtkShaderProgram::Substitute:
+//VTK::DebugOptions::Def
+
+//========================== Tuning Define: ====================================
+
+// Which edge search implementation to use. If defined, use VTK's endpoint
+// algorithm, otherwise use NVIDIA's.
+//
+// NVIDIA is faster, but gives poor results on single pixel lines (e.g.
+// vtkPolyDataMapper's wireframe/edges). VTK is slower, but gives nicer results
+// on single pixel lines.
+//#define FXAA_USE_HIGH_QUALITY_ENDPOINTS;
+
+// Replacement stub for vtkShaderProgram::Substitute:
+//VTK::EndpointAlgo::Def
+
+//========================= Input Parameters: ==================================
+
+// Current fragment texture coordinate:
+in vec2 texCoord;
+
+// Aliased color buffer (should be sRGB, ideally)
+uniform sampler2D Input;
+
+// 1.f/Input.width, 1.f/Input.height:
+uniform vec2 InvTexSize;
+
+//======================== Tuning Parameters: ==================================
+
+// See the vtkOpenGLFXAAFilter class documentation for details on these.
+
+// Minimum change in luminosity (relative to maxLum) to use FXAA:
+uniform float RelativeContrastThreshold;
+
+// Absolute minimum lum change required for FXAA (overrides
+// RelativeContrastThreshold value, not scaled):
+uniform float HardContrastThreshold;
+
+// Maximum amount of lowpass blending for subpixel anti-aliasing:
+uniform float SubpixelBlendLimit;
+
+// Ignore subpixel anti-aliasing that contributes less than this amount to the
+// total contrast:
+uniform float SubpixelContrastThreshold;
+
+// Maximum number of steps to take when searching for line edges:
+uniform int EndpointSearchIterations;
+
+//============================ Helper Methods ==================================
+// Converts rgb to luminosity:
+const vec3 LUMINOSITY_VEC = vec3(0.299, 0.587, 0.114);
+float luminosity(vec3 rgb)
+{
+  return dot(rgb, LUMINOSITY_VEC);
+}
+
+//======================= Endpoint Search Routines =============================
+// Identify the endpoints of a detected edge and compute a sampling offset to
+// correct for aliasing. The computed offset accounts for distance from edge
+// to create a gradient of antialiased values.
+//
+// Input parameters:
+// - posC: The texture coordinate position of the current pixel.
+// - lumC: The luminosity of the current pixel.
+// - lumHC: The luminosity of the highest contrast pixel to HC that is
+//          perpendicular to the detected edge.
+// - lengthSign: Single component magnitude and direction (in texture
+//               coordinates) from the center of C pointing to HC.
+// - tcPixel: (Width, Height) of a single pixel in texture coordinate units.
+// - horzSpan: True if the detected edge is horizontal.
+// - posEdgeAA: Output parameter with the position to resample the input texture
+//              to get an edge anti-aliased rgb value for the current pixel.
+//
+// Implementations:
+// - nvidiaEndpointSearch: The algorithm proposed by nVidia in their whitepaper
+//   and sample implementations. Faster, but poorly handles single-pixel lines.
+// - vtkEndpointSearch: Modified endpoint search that does more texture lookups,
+//   but does better detection of single pixel line endpoints.
+//
+// Return values for endpoint searches:
+const int FXAA_NO_EDGE_AA = 0;    // Edge AA not required.
+const int FXAA_NEED_EDGE_AA = 1;  // Edge AA required.
+const int FXAA_ABORT_EDGE_AA = 2; // Instruct to return. Used for debugging.
+
+//================ nVidia's Endpoint Search Implementation =====================
+
+int nvidiaEndpointSearch(vec2 posC, float lumC, float lumHC, float lengthSign,
+                         vec2 tcPixel, bool horzSpan, out vec2 posEdgeAA)
+{
+  /*****************************************************************************
+   * End of Edge Search                                                        *
+   *===========================================================================*
+   * Search along the direction of the detected edge to find both endpoints.   *
+   *                                                                           *
+   * We define HC as the Highest Contrast neighbor perpendicular to the edge   *
+   * direction (i.e. the pixel on the other side of the edge).                 *
+   *                                                                           *
+   * The luminosity of HC is lumHC, the contrast between C and HC is           *
+   * contrastCHC, and the average luminosity of HC and C is lumAveCHC.         *
+   *                                                                           *
+   * We'll walk along the edge boundary in both direction, sampling the average*
+   * luminosity of the pixels on both sides of the edge: lumAveN for the       *
+   * negative direction, lumAveP for the positive direction. We determine the  *
+   * end of the edge to be where:                                              *
+   *                                                                           *
+   * abs(lumAve[NP] - lumCHC) >= contrastHC / 4.                               *
+   *                                                                           *
+   * which indicates that the average luminosities have diverged enough to no  *
+   * longer be considered part of the edge.                                    *
+   ****************************************************************************/
+
+  float contrastCHC = abs(lumC - lumHC);
+
+  // Point on the boundary of C and HC:
+  vec2 boundaryCHC = posC; // Will be shifted later.
+
+  // Direction of the edge
+  vec2 edgeDir = vec2(0.f); // Component is set below:
+
+  if (horzSpan)
+    {
+    boundaryCHC.y += lengthSign * 0.5f;
+    edgeDir.x = tcPixel.x;
+    }
+  else
+    {
+    boundaryCHC.x += lengthSign * 0.5f;
+    edgeDir.y = tcPixel.y;
+    }
+
+  // Prepare for the search loop:
+  float contrastThreshold = contrastCHC / 4.f;
+  float lumAveCHC = 0.5f * (lumC + lumHC);
+  float lumAveN;
+  float lumAveP;
+  bool doneN = false;
+  bool doneP = false;
+  vec2 posN = boundaryCHC - edgeDir;
+  vec2 posP = boundaryCHC + edgeDir;
+
+#ifdef FXAA_DEBUG_EDGE_NUM_STEPS
+  int stepsN = 0;
+  int stepsP = 0;
+#endif // FXAA_DEBUG_EDGE_NUM_STEPS
+
+  for (int i = 0; i < EndpointSearchIterations; ++i)
+    {
+#ifdef FXAA_DEBUG_EDGE_NUM_STEPS
+    if (!doneN) stepsN += 1;
+    if (!doneP) stepsP += 1;
+#endif // FXAA_DEBUG_EDGE_NUM_STEPS
+
+    // Sample on the edge boundary in both directions:
+    if (!doneN) lumAveN = luminosity(texture2D(Input, posN).rgb);
+    if (!doneP) lumAveP = luminosity(texture2D(Input, posP).rgb);
+
+    // Edge endpoint is where the contrast changes significantly:
+    doneN = doneN || (abs(lumAveN - lumAveCHC) >= contrastThreshold);
+    doneP = doneP || (abs(lumAveP - lumAveCHC) >= contrastThreshold);
+    if (doneN && doneP) break;
+
+    // Step to next pixel:
+    if (!doneN) posN -= edgeDir;
+    if (!doneP) posP += edgeDir;
+    }
+
+#ifdef FXAA_DEBUG_EDGE_NUM_STEPS
+  gl_FragData[0] = vec4(float(stepsN) / float(EndpointSearchIterations), 0.f,
+                        float(stepsP) / float(EndpointSearchIterations), 1.f);
+  return FXAA_ABORT_EDGE_AA;
+#endif // FXAA_DEBUG_EDGE_NUM_STEPS
+
+  /*****************************************************************************
+   * Edge Search Analysis                                                      *
+   *===========================================================================*
+   * We've located the ends of the edge at this point. Next we figure out how *
+   * to interpolate the edge.                                                 *
+   *                                                                          *
+   * First we need to find out which end of the edge (N or P) is changing     *
+   * contrast relative to boundaryCHC. This is best explained visually:       *
+   *                                                                          *
+   * +------------+                                                           *
+   * |XX   E      |                                                           *
+   * |NXXXHXXP    |                                                           *
+   * |N   C  PXXXX|                                                           *
+   * |           X|                                                           *
+   * +------------+                                                           *
+   *                                                                          *
+   * In the above, an X represents a dark pixel, and a blank space is a light *
+   * pixel. C is the current pixel, and H is pixel HC. The negative endpoint N*
+   * of the edge is the midpoint between the first set of blank pixels to the *
+   * left of C and H, while the positive endpoint P is the first set of dark  *
+   * pixels to the right. The pixels under the "N" are light, while the pixels*
+   * under "P" are dark. The "P" side of the edge is changing contrast        *
+   * relative to C. We compute this condition as:                             *
+   *                                                                          *
+   * bool lumCLessThanAve = lumC < lumAveCHC;                                 *
+   * bool lumNLessThanAve = lumAveN < lumAveCHC;                              *
+   * bool lumPLessThanAve = lumAveP < lumAveCHC;                              *
+   * bool shadeIfNearN = lumCLessThanAve != lumNLessThanAve;                  *
+   * bool shadeIfNearP = lumCLessThanAve != lumPLessThanAve;                  *
+   *                                                                          *
+   * If shadeIfNearN is true, N is changing contrast relative to C. The same  *
+   * is true for P. Thus, the change in the average contrast of the the       *
+   * endpoint relative to lumAveHC must be opposite to the change in contrast *
+   * from C to lumAveHC.                                                      *
+   *                                                                          *
+   * In addition to checking the change in contrast, we also identify which   *
+   * endpoint is nearest to C. As the variable names suggest, we will only    *
+   * apply edge anti-aliasing if we're nearest an endpoint that has the       *
+   * desired contrast change. This prevents shading edge neighbors that do not*
+   * follow the direction of the line, such as point E in the diagram.        *
+   *                                                                          *
+   * bool CisNearN = (norm(posN - boundaryCHC) < norm(posP - boundaryCHC));   *
+   *                                                                          *
+   * If both of the above conditions are met (the nearest endpoint has the    *
+   * proper contrast change), then we compute the ratio of C's distance from  *
+   * the desired endpoint to the total length of the edge. This ratio is the  *
+   * fraction of a pixel that we shift C towards HC to resample C for         *
+   * anti-aliasing.                                                           *
+   ****************************************************************************/
+
+  // Check both endpoints for the contrast change condition:
+  bool lumCLessThanAve = lumC < lumAveCHC;
+  bool lumNLessThanAve = lumAveN < lumAveCHC;
+  bool lumPLessThanAve = lumAveP < lumAveCHC;
+  bool shadeIfNearN = lumCLessThanAve != lumNLessThanAve;
+  bool shadeIfNearP = lumCLessThanAve != lumPLessThanAve;
+
+  // Identify the closest point:
+  float dstN;
+  float dstP;
+  if (horzSpan)
+    {
+    dstN = boundaryCHC.x - posN.x;
+    dstP = posP.x - boundaryCHC.x;
+    }
+  else
+    {
+    dstN = boundaryCHC.y - posN.y;
+    dstP = posP.y - boundaryCHC.y;
+    }
+  bool nearestEndpointIsN = dstN < dstP;
+  float dst = min(dstN, dstP);
+
+  // Finally determine if we need shading:
+  bool needEdgeAA = nearestEndpointIsN ? shadeIfNearN : shadeIfNearP;
+
+#ifdef FXAA_DEBUG_EDGE_DISTANCE
+  if (needEdgeAA)
+    {
+    float maxDistance = EndpointSearchIterations;
+    if (nearestEndpointIsN)
+      {
+      gl_FragData[0] = vec4(1.f - dstN / maxDistance, 0.f, 0.f, 1.f);
+      }
+    else
+      {
+      gl_FragData[0] = vec4(0.f, 0.f, 1.f - dstP / maxDistance, 1.f);
+      }
+    }
+  else
+    {
+    gl_FragData[0] = vec4(1.f, 1.f, 0.f, 1.f);
+    }
+  return FXAA_ABORT_EDGE_AA;
+#endif // FXAA_DEBUG_EDGE_DISTANCE
+
+  // Compute the pixel offset:
+  float invNegSpanLength = -1.f / (dstN + dstP);
+  float pixelOffset = dst * invNegSpanLength + 0.5;
+
+#ifdef FXAA_DEBUG_EDGE_SAMPLE_OFFSET
+  if (needEdgeAA)
+    { // x2, since the max value is 0.5:
+    gl_FragData[0] = vec4(-2.f * dst * invNegSpanLength, 0.f, 0.f, 1.f);
+    return FXAA_ABORT_EDGE_AA;
+    }
+#endif // FXAA_DEBUG_EDGE_SAMPLE_OFFSET
+
+  // Resample the edge anti-aliased value:
+  posEdgeAA = posC;
+  if (horzSpan)
+    {
+    posEdgeAA.y += pixelOffset * lengthSign;
+    }
+  else
+    {
+    posEdgeAA.x += pixelOffset * lengthSign;
+    }
+
+  return needEdgeAA ? 1 : 0;
+}
+
+//================== VTK's Endpoint Search Implementation ======================
+
+int vtkEndpointSearch(vec2 posC, float lumC, float lumHC, float lengthSign,
+                      vec2 tcPixel, bool horzSpan, out vec2 posEdgeAA)
+{
+  /*****************************************************************************
+   * End of Edge Search                                                        *
+   *===========================================================================*
+   * Search along the direction of the detected edge to find both endpoints.   *
+   * +------------+                                                            *
+   * |X           | nVidia's endpoint detector handles this case poorly. If C  *
+   * | XXXXXX  C  | is the current pixel, it will detect N as the leftmost     *
+   * |       XXHXX| column of pixels, since it samples the average luminosity  *
+   * |           X| at the border of the rows containing C and HC. The actual  *
+   * +------------+ endpoint is 3 pixels to the left from C, but the average   *
+   * luminosity does not change at this point.                                 *
+   *                                                                           *
+   * We adapt the algorithm to sample both rows/columns containing C and HC on *
+   * the texel centers, rather than the interpolated border. We then detect    *
+   * the edge endpoints when:                                                  *
+   *                                                                           *
+   * abs(lumHCN - lumHC) > abs(lumHCN - lumC) ||                               *
+   * abs(lumCN -  lumC)  > abs(lumCN  - lumHC)                                 *
+   *                                                                           *
+   * where lumHCN is the luminosity of the sample in HC's row in the negative  *
+   * direction, lumCN is the luminosity of the sample in C's row in the        *
+   * negative direction, lumHC is the luminosity of HC, and lumC is the        *
+   * luminosity of C. Thus, the endpoint is where a sampled luminosity in C's  *
+   * row is closer to HC, or vice-versa. The positive endpoint is determined   *
+   * similarly.                                                                *
+   *                                                                           *
+   * After the endpoints has been determined, we decide whether or not the     *
+   * current pixel needs resampling. This is similar to nVidia's algorithm.    *
+   * We determine if the luminosity of the nearest endpoint's C sample is      *
+   * closer to C or HC. If it's closer to HC, it gets shaded. The resampling   *
+   * offset is computed identically to nVidia's algorithm.                     *
+   ****************************************************************************/
+
+  // Point on the boundary of C and HC:
+  vec2 posHC = posC; // Will be shifted later.
+
+  // Direction of the edge
+  vec2 edgeDir = vec2(0.f); // Component is set below:
+
+  if (horzSpan)
+    {
+    posHC.y += lengthSign;
+    edgeDir.x = tcPixel.x;
+    }
+  else
+    {
+    posHC.x += lengthSign;
+    edgeDir.y = tcPixel.y;
+    }
+
+  // Prepare for the search loop:
+  float lumHCN;
+  float lumHCP;
+  float lumCN;
+  float lumCP;
+  bool doneN = false;
+  bool doneP = false;
+  vec2 posHCN = posHC - edgeDir;
+  vec2 posHCP = posHC + edgeDir;
+  vec2 posCN  = posC - edgeDir;
+  vec2 posCP  = posC + edgeDir;
+
+#ifdef FXAA_DEBUG_EDGE_NUM_STEPS
+  int stepsN = 0;
+  int stepsP = 0;
+#endif // FXAA_DEBUG_EDGE_NUM_STEPS
+
+  for (int i = 0; i < EndpointSearchIterations; ++i)
+    {
+#ifdef FXAA_DEBUG_EDGE_NUM_STEPS
+    if (!doneN) stepsN += 1;
+    if (!doneP) stepsP += 1;
+#endif // FXAA_DEBUG_EDGE_NUM_STEPS
+
+    // Sample the luminosities along the edge:
+    if (!doneN)
+      {
+      lumHCN = luminosity(texture2D(Input, posHCN).rgb);
+      lumCN  = luminosity(texture2D(Input, posCN).rgb);
+      }
+    if (!doneP)
+      {
+      lumHCP = luminosity(texture2D(Input, posHCP).rgb);
+      lumCP  = luminosity(texture2D(Input, posCP).rgb);
+      }
+
+    // Check contrast to detect endpoint:
+    doneN = doneN || abs(lumHCN - lumHC) > abs(lumHCN - lumC)
+                  || abs(lumCN  - lumC)  > abs(lumCN  - lumHC);
+    doneP = doneP || abs(lumHCP - lumHC) > abs(lumHCP - lumC)
+                  || abs(lumCP  - lumC)  > abs(lumCP  - lumHC);
+
+    if (doneN && doneP)
+      {
+      break;
+      }
+
+    // Take next step.
+    if (!doneN)
+      {
+      posHCN -= edgeDir;
+      posCN  -= edgeDir;
+      }
+    if (!doneP)
+      {
+      posHCP += edgeDir;
+      posCP  += edgeDir;
+      }
+    }
+
+#ifdef FXAA_DEBUG_EDGE_NUM_STEPS
+  gl_FragData[0] = vec4(float(stepsN) / float(EndpointSearchIterations), 0.f,
+                        float(stepsP) / float(EndpointSearchIterations), 1.f);
+  return FXAA_ABORT_EDGE_AA;
+#endif // FXAA_DEBUG_EDGE_NUM_STEPS
+
+  // Identify the closest point:
+  float dstN;
+  float dstP;
+
+  if (horzSpan)
+    {
+    dstN = posC.x - posCN.x;
+    dstP = posCP.x - posC.x;
+    }
+  else
+    {
+    dstN = posC.y - posCN.y;
+    dstP = posCP.y - posC.y;
+    }
+
+  bool nearestEndpointIsN = dstN < dstP;
+  float dst = min(dstN, dstP);
+  float lumCNear = nearestEndpointIsN ? lumCN : lumCP;
+
+  // Resample if the nearest endpoint sample in C's row is closer in luminosity
+  // to HC than C.
+  bool needEdgeAA = abs(lumCNear - lumHC) < abs(lumCNear - lumC);
+
+#ifdef FXAA_DEBUG_EDGE_DISTANCE
+  if (needEdgeAA)
+    {
+    float maxDistance = EndpointSearchIterations;
+    if (nearestEndpointIsN)
+      {
+      gl_FragData[0] = vec4(1.f - dstN / maxDistance, 0.f, 0.f, 1.f);
+      }
+    else
+      {
+      gl_FragData[0] = vec4(0.f, 0.f, 1.f - dstP / maxDistance, 1.f);
+      }
+    }
+  else
+    {
+    gl_FragData[0] = vec4(1.f, 1.f, 0.f, 1.f);
+    }
+  return FXAA_ABORT_EDGE_AA;
+#endif // FXAA_DEBUG_EDGE_DISTANCE
+
+  // Compute the pixel offset:
+  float invNegSpanLength = -1.f / (dstN + dstP);
+  float pixelOffset = dst * invNegSpanLength + 0.5f;
+
+#ifdef FXAA_DEBUG_EDGE_SAMPLE_OFFSET
+  if (needEdgeAA)
+    { // x2, since the max value is 0.5:
+    gl_FragData[0] = vec4(-2.f * dst * invNegSpanLength, 0.f, 0.f, 1.f);
+    return FXAA_ABORT_EDGE_AA;
+    }
+#endif // FXAA_DEBUG_EDGE_SAMPLE_OFFSET
+
+  // Resample the edge anti-aliased value:
+  posEdgeAA = posC;
+  if (horzSpan)
+    {
+    posEdgeAA.y += pixelOffset * lengthSign;
+    }
+  else
+    {
+    posEdgeAA.x += pixelOffset * lengthSign;
+    }
+
+  return needEdgeAA ? 1 : 0;
+}
+
+//=============================== FXAA Body ====================================
+
+void main()
+{
+  // Pixel step size in texture coordinate units:
+  vec2 tcPixel = InvTexSize;
+
+  /****************************************************************************
+   * Compute Local Contrast Range And Early Abort                             *
+   *==========================================================================*
+   * Determine the contrast range for the current pixel and its neightbors    *
+   * to the North, South, West, and East. If the range is less than both of:  *
+   *                                                                          *
+   * a) RelativeContrastThreshold * lumMax                                    *
+   *                                                                          *
+   * and                                                                      *
+   *                                                                          *
+   * b) HardContrastThreshold                                                 *
+   *                                                                          *
+   * then skip anti-aliasing for this pixel.                                  *
+   ****************************************************************************/
+
+  // First compute the texture coordinates:
+  vec2 tcC = texCoord;
+  vec2 tcN = texCoord + vec2( 0.f,       -tcPixel.y);
+  vec2 tcS = texCoord + vec2( 0.f,        tcPixel.y);
+  vec2 tcW = texCoord + vec2(-tcPixel.x,  0.f);
+  vec2 tcE = texCoord + vec2( tcPixel.x,  0.f);
+
+  // Extract the rgb values of these pixels:
+  vec3 rgbC = texture2D(Input, tcC).rgb;
+  vec3 rgbN = texture2D(Input, tcN).rgb;
+  vec3 rgbS = texture2D(Input, tcS).rgb;
+  vec3 rgbW = texture2D(Input, tcW).rgb;
+  vec3 rgbE = texture2D(Input, tcE).rgb;
+
+  // Convert to luminosity:
+  float lumC = luminosity(rgbC);
+  float lumN = luminosity(rgbN);
+  float lumS = luminosity(rgbS);
+  float lumW = luminosity(rgbW);
+  float lumE = luminosity(rgbE);
+
+  // The min, max, and range of luminosity for CNSWE:
+  float lumMin = min(lumC, min(min(lumN, lumS), min(lumW, lumE)));
+  float lumMax = max(lumC, max(max(lumN, lumS), max(lumW, lumE)));
+  float lumRange = lumMax - lumMin;
+  float lumThresh = max(HardContrastThreshold,
+                        RelativeContrastThreshold * lumMax);
+
+  // Don't apply FXAA unless there's a significant change in luminosity around
+  // the current pixel:
+  if (lumRange < lumThresh)
+    {
+    gl_FragData[0] = vec4(rgbC, 1.f); // original color
+    return;
+    }
+
+  /****************************************************************************
+   * Fetch texels for complete 3x3 neighborhood.                              *
+   ****************************************************************************/
+
+  // Fetch additional texels for edge detection / subpixel antialiasing:
+  vec2 tcNE = texCoord + vec2( tcPixel.x, -tcPixel.y);
+  vec2 tcSE = texCoord + vec2( tcPixel.x,  tcPixel.y);
+  vec2 tcNW = texCoord + vec2(-tcPixel.x, -tcPixel.y);
+  vec2 tcSW = texCoord + vec2(-tcPixel.x,  tcPixel.y);
+  vec3 rgbNE = texture2D(Input, tcNE).rgb;
+  vec3 rgbSE = texture2D(Input, tcSE).rgb;
+  vec3 rgbNW = texture2D(Input, tcNW).rgb;
+  vec3 rgbSW = texture2D(Input, tcSW).rgb;
+  float lumNE = luminosity(rgbNE);
+  float lumSE = luminosity(rgbSE);
+  float lumNW = luminosity(rgbNW);
+  float lumSW = luminosity(rgbSW);
+
+  // Precompute some combined luminosities. These are reused later.
+  float lumNS = lumN + lumS;
+  float lumWE = lumW + lumE;
+  float lumNSWE = lumNS + lumWE;
+  float lumNWNE = lumNW + lumNE;
+  float lumSWSE = lumSW + lumSE;
+  float lumNWSW = lumNW + lumSW;
+  float lumNESE = lumNE + lumSE;
+
+  /****************************************************************************
+   * Subpixel Anti-aliasing                                                   *
+   *==========================================================================*
+   * Check if the current pixel is very high contrast to it's neighbors (e.g. *
+   * specular aliasing, noisy shadow textures, etc). If it is, compute the    *
+   * average color over the 3x3 neighborhood and a blending factor.           *
+   *                                                                          *
+   * The blending factor is computed as the minimum of:                       *
+   *                                                                          *
+   * 1) max(0.f, abs([average NSWE lum] - lumC) - SubpixelContrastThreshold)  *
+   *                  FXAA_SUBPIX_TRIM_SCALE                                  *
+   *                                                                          *
+   * or                                                                       *
+   *                                                                          *
+   * 2) SubpixelBlendLimit                                                    *
+   ****************************************************************************/
+
+  // Check for sub-pixel aliasing (e.g. current pixel has high contrast from
+  // neighbors):
+  float lumAveNSWE = 0.25f * (lumNSWE);
+  float lumSubRange = abs(lumAveNSWE - lumC);
+
+  // Compute the subpixel blend amount:
+  float blendSub = max(0.f, (lumSubRange / lumRange) -
+                            SubpixelContrastThreshold);
+  blendSub = min(SubpixelBlendLimit,
+                 blendSub * (1.f / (1.f - SubpixelContrastThreshold)));
+
+#ifdef FXAA_DEBUG_SUBPIXEL_ALIASING
+  if (blendSub > 0.f)
+    {
+    gl_FragData[0] = vec4(vec3(blendSub / SubpixelBlendLimit), 1.f);
+    }
+  else
+    {
+    gl_FragData[0] = vec4(rgbC, 1.f);
+    }
+  return;
+#endif // FXAA_DEBUG_SUBPIXEL_ALIASING
+
+  // Compute the subpixel blend color. Average the 3x3 neighborhood:
+  vec3 rgbSub = (1.f/9.f) *
+      (rgbNW + rgbN + rgbNE +
+       rgbW  + rgbC + rgbE  +
+       rgbSW + rgbS + rgbSE);
+
+  /****************************************************************************
+   * Edge Testing                                                             *
+   *==========================================================================*
+   * Apply vertical and horizontal edge detection techniques to determine the *
+   * direction of any edges in the 3x3 neighborhood.                          *
+   ****************************************************************************/
+
+  // Check for vertical edge. Pixel coeffecients are:
+  //  1 -2  1
+  //  2 -4  2
+  //  1 -2  1
+  // The absolute value of each row is taken, summed, and divided by 12.
+  // Operations are decomposed here to take advantage of FMA ops.
+  float edgeVertRow1 = abs(-2.f * lumN + lumNWNE);
+  float edgeVertRow2 = abs(-2.f * lumC + lumWE);
+  float edgeVertRow3 = abs(-2.f * lumS + lumSWSE);
+  float edgeVert = ((2.f * edgeVertRow2 + edgeVertRow1) + edgeVertRow3) / 12.f;
+
+  // Check for horizontal edge. Pixel coeffecients are:
+  //  1  2  1
+  // -2 -4 -2
+  //  1  2  1
+  // The absolute value of each column is taken, summed, and divided by 12.
+  // Operations are decomposed here to take advantage of FMA ops.
+  float edgeHorzCol1 = abs(-2.f * lumW + lumNWSW);
+  float edgeHorzCol2 = abs(-2.f * lumC + lumNS);
+  float edgeHorzCol3 = abs(-2.f * lumE + lumNESE);
+  float edgeHorz = ((2.f * edgeHorzCol2 + edgeHorzCol1) + edgeHorzCol3) / 12.f;
+
+  // Indicates that the edge span is horizontal:
+  bool horzSpan = edgeHorz >= edgeVert;
+
+#ifdef FXAA_DEBUG_EDGE_DIRECTION
+  gl_FragData[0] = horzSpan ? vec4(0.f, 0.f, 1.f, 1.f)
+                            : vec4(1.f, 0.f, 0.f, 1.f);
+  return;
+#endif // FXAA_DEBUG_EDGE_DIRECTION
+
+  /****************************************************************************
+   * Endpoint Search Preparation                                              *
+   *==========================================================================*
+   * Compute inputs for an endpoint detection algorithm. Mainly concerned     *
+   * locating HC -- the Highest Contrast pixel (relative to C) that's on the  *
+   * opposite side of the detected edge from C.                               *
+   ****************************************************************************/
+
+  // The two neighbor pixels perpendicular to the edge:
+  float lumHC1;
+  float lumHC2;
+
+  // Single-pixel texture coordinate offset that points from C to HC.
+  float lengthSign;
+
+  if (horzSpan)
+    {
+    lumHC1 = lumN;
+    lumHC2 = lumS;
+    lengthSign = -tcPixel.y; // Assume N for now.
+    }
+  else
+    {
+    lumHC1 = lumW;
+    lumHC2 = lumE;
+    lengthSign = -tcPixel.x; // Assume W for now.
+    }
+
+  // Luminosity of the NSWE pixel perpendicular to the edge with the highest
+  // contrast to C:
+  float lumHC;
+  if (abs(lumC - lumHC1) >= abs(lumC - lumHC2))
+    {
+    lumHC = lumHC1;
+    }
+  else
+    {
+    lumHC = lumHC2;
+    // Also reverse the offset direction in this case:
+    lengthSign = -lengthSign;
+    }
+
+  vec2 posEdgeAA; // Position to resample C at to get edge-antialiasing.
+
+#ifdef FXAA_USE_HIGH_QUALITY_ENDPOINTS
+  int endpointResult = vtkEndpointSearch(tcC, lumC, lumHC, lengthSign,
+                                         tcPixel, horzSpan, posEdgeAA);
+#else // FXAA_USE_HIGH_QUALITY_ENDPOINTS
+  int endpointResult = nvidiaEndpointSearch(tcC, lumC, lumHC, lengthSign,
+                                            tcPixel, horzSpan, posEdgeAA);
+#endif // FXAA_USE_HIGH_QUALITY_ENDPOINTS
+
+  // Only sample texture if needed. Reuse rgbC otherwise.
+  vec3 rgbEdgeAA = rgbC;
+
+  switch (endpointResult)
+    {
+    case FXAA_ABORT_EDGE_AA: // Used for debugging (endpoint search set colors)
+      return;
+
+    case FXAA_NEED_EDGE_AA: // Resample the texture at the requested position.
+      rgbEdgeAA = texture2D(Input, posEdgeAA).rgb;
+      break;
+
+    case FXAA_NO_EDGE_AA: // Current pixel does not need edge anti-aliasing.
+    default:
+      break;
+    }
+
+#ifdef FXAA_DEBUG_ONLY_SUBPIX_AA
+  rgbEdgeAA = rgbC;
+#endif // FXAA_DEBUG_ONLY_SUBPIX_AA
+#ifdef FXAA_DEBUG_ONLY_EDGE_AA
+  blendSub = 0.f;
+#endif // FXAA_DEBUG_ONLY_EDGE_AA
+
+  // Blend the edgeAA and subpixelAA results together:
+  gl_FragData[0] = vec4(mix(rgbEdgeAA, rgbSub, blendSub), 1.f);
+}
diff --git a/Rendering/OpenGL2/module.cmake b/Rendering/OpenGL2/module.cmake
index 94896376026f44ac59c6daaf7998818d7fdb2e41..604d01e0ff9348aa51189baa5d4d689973724265 100644
--- a/Rendering/OpenGL2/module.cmake
+++ b/Rendering/OpenGL2/module.cmake
@@ -11,6 +11,7 @@ vtk_module(vtkRenderingOpenGL2
   TEST_DEPENDS
     vtkIOLegacy
     vtkRenderingImage
+    vtkRenderingFreeType
     vtkTestingCore
     vtkTestingRendering
     vtkImagingGeneral
diff --git a/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..89e5be7a4b98f9c8d2639d9f24d8a4d6248171fd
--- /dev/null
+++ b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx
@@ -0,0 +1,449 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkOpenGLFXAAFilter.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#include "vtkOpenGLFXAAFilter.h"
+
+#include "vtk_glew.h"
+
+#include "vtkObjectFactory.h"
+#include "vtkOpenGLBufferObject.h"
+#include "vtkOpenGLError.h"
+#include "vtkOpenGLRenderer.h"
+#include "vtkOpenGLRenderUtilities.h"
+#include "vtkOpenGLRenderWindow.h"
+#include "vtkOpenGLShaderCache.h"
+#include "vtkOpenGLVertexArrayObject.h"
+#include "vtkShaderProgram.h"
+#include "vtkTextureObject.h"
+#include "vtkTimerLog.h"
+#include "vtkTypeTraits.h"
+
+#include <algorithm>
+#include <cassert>
+
+// Our fragment shader:
+#include "vtkFXAAFilterFS.h"
+
+// Define to perform/dump benchmarking info:
+//#define FXAA_BENCHMARK
+
+vtkStandardNewMacro(vtkOpenGLFXAAFilter)
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::PrintSelf(std::ostream &os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os, indent);
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::Execute(vtkOpenGLRenderer *ren)
+{
+  assert(ren);
+  this->Renderer = ren;
+
+  this->StartTimeQuery(this->PreparationTimeQuery,
+                       this->PreparationTimeQueryActive);
+
+  this->Prepare();
+  this->LoadInput();
+
+  this->EndTimeQuery(this->PreparationTimeQuery,
+                     this->PreparationTimeQueryActive);
+  this->StartTimeQuery(this->FXAATimeQuery, this->FXAATimeQueryActive);
+
+  this->ApplyFilter();
+
+  this->EndTimeQuery(this->FXAATimeQuery, this->FXAATimeQueryActive);
+
+  this->Finalize();
+
+  this->Renderer = NULL;
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::ReleaseGraphicsResources()
+{
+  this->FreeGLObjects();
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::SetUseHighQualityEndpoints(bool val)
+{
+  if (this->UseHighQualityEndpoints != val)
+    {
+    this->NeedToRebuildShader = true;
+    this->Modified();
+    this->UseHighQualityEndpoints = val;
+    }
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::SetDebugOptionValue(DebugOption opt)
+{
+  if (this->DebugOptionValue != opt)
+    {
+    this->NeedToRebuildShader = true;
+    this->Modified();
+    this->DebugOptionValue = opt;
+    }
+}
+
+//------------------------------------------------------------------------------
+vtkOpenGLFXAAFilter::vtkOpenGLFXAAFilter()
+  : BlendState(false),
+    DepthTestState(false),
+    PreparationTimeQueryActive(false),
+    FXAATimeQueryActive(false),
+    PreparationTimeQuery(0),
+    FXAATimeQuery(0),
+    PreparationTime(0),
+    FXAATime(0),
+    RelativeContrastThreshold(1.f/8.f),
+    HardContrastThreshold(1.f/16.f),
+    SubpixelBlendLimit(3.f/4.f),
+    SubpixelContrastThreshold(1.f/4.f),
+    EndpointSearchIterations(12),
+    UseHighQualityEndpoints(true),
+    NeedToRebuildShader(true),
+    Renderer(NULL),
+    Input(NULL),
+    Program(NULL),
+    VAO(NULL),
+    VBO(NULL)
+{
+  std::fill(this->Viewport, this->Viewport + 4, 0);
+}
+
+//------------------------------------------------------------------------------
+vtkOpenGLFXAAFilter::~vtkOpenGLFXAAFilter()
+{
+  this->FreeGLObjects();
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::Prepare()
+{
+  this->Renderer->GetTiledSizeAndOrigin(&this->Viewport[2], &this->Viewport[3],
+                                        &this->Viewport[0], &this->Viewport[1]);
+
+  // Check if we need to create a new working texture:
+  if (this->Input)
+    {
+    unsigned int rendererWidth = static_cast<unsigned int>(this->Viewport[2]);
+    unsigned int rendererHeight = static_cast<unsigned int>(this->Viewport[3]);
+    if (this->Input->GetWidth()  != rendererWidth ||
+        this->Input->GetHeight() != rendererHeight)
+      {
+      this->FreeGLObjects();
+      }
+    }
+
+  if (!this->Input)
+    {
+    this->CreateGLObjects();
+    }
+
+  this->BlendState = glIsEnabled(GL_BLEND) == GL_TRUE;
+  this->DepthTestState = glIsEnabled(GL_DEPTH_TEST) == GL_TRUE;
+
+  glDisable(GL_BLEND);
+  glDisable(GL_DEPTH_TEST);
+
+  vtkOpenGLCheckErrorMacro("Error after saving GL state.");
+}
+
+//------------------------------------------------------------------------------
+// Delete the vtkObject subclass pointed at by ptr if it is set.
+namespace {
+template <typename T> void DeleteHelper(T *& ptr)
+{
+  if (ptr)
+    {
+    ptr->Delete();
+    ptr = NULL;
+    }
+}
+} // end anon namespace
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::FreeGLObjects()
+{
+  DeleteHelper(this->Input);
+//  DeleteHelper(this->Program); // Managed by the shader cache
+  DeleteHelper(this->VAO);
+  DeleteHelper(this->VBO);
+
+  if (this->FXAATimeQuery > 0)
+    {
+    glDeleteQueries(1, &this->FXAATimeQuery);
+    this->FXAATimeQuery = 0;
+    }
+  if (this->PreparationTimeQuery > 0)
+    {
+    glDeleteQueries(1, &this->PreparationTimeQuery);
+    this->PreparationTimeQuery = 0;
+    }
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::CreateGLObjects()
+{
+  assert(!this->Input);
+  this->Input = vtkTextureObject::New();
+  this->Input->SetContext(static_cast<vtkOpenGLRenderWindow*>(
+                            this->Renderer->GetRenderWindow()));
+  this->Input->SetFormat(GL_RGB);
+  this->Input->SetInternalFormat(GL_RGB8);
+
+  // Required for FXAA, since we interpolate texels for blending.
+  this->Input->SetMinificationFilter(vtkTextureObject::Linear);
+  this->Input->SetMagnificationFilter(vtkTextureObject::Linear);
+
+  // Clamp to edge, since we'll be sampling off-texture texels:
+  this->Input->SetWrapS(vtkTextureObject::ClampToEdge);
+  this->Input->SetWrapT(vtkTextureObject::ClampToEdge);
+  this->Input->SetWrapR(vtkTextureObject::ClampToEdge);
+
+  this->Input->Allocate2D(this->Viewport[2], this->Viewport[3], 4,
+                          vtkTypeTraits<vtkTypeUInt8>::VTK_TYPE_ID);
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::LoadInput()
+{
+  this->Input->CopyFromFrameBuffer(this->Viewport[0], this->Viewport[1], 0, 0,
+                                   this->Viewport[2], this->Viewport[3]);
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::ApplyFilter()
+{
+  typedef vtkOpenGLRenderUtilities GLUtil;
+
+  vtkOpenGLRenderWindow *renWin = static_cast<vtkOpenGLRenderWindow*>(
+        this->Renderer->GetRenderWindow());
+
+  this->Input->Activate();
+
+  if (this->NeedToRebuildShader)
+    {
+    DeleteHelper(this->VAO);
+    DeleteHelper(this->VBO);
+    this->Program = NULL; // Don't free, shader cache manages these.
+    this->NeedToRebuildShader = false;
+    }
+
+  if (!this->Program)
+    {
+    std::string fragShader = vtkFXAAFilterFS;
+    this->SubstituteFragmentShader(fragShader);
+    this->Program = renWin->GetShaderCache()->ReadyShaderProgram(
+          GLUtil::GetFullScreenQuadVertexShader().c_str(),
+          fragShader.c_str(),
+          GLUtil::GetFullScreenQuadGeometryShader().c_str());
+    }
+  else
+    {
+    renWin->GetShaderCache()->ReadyShaderProgram(this->Program);
+    }
+
+  if (!this->VAO)
+    {
+    this->VBO = vtkOpenGLBufferObject::New();
+    this->VAO = vtkOpenGLVertexArrayObject::New();
+    GLUtil::PrepFullScreenVAO(this->VBO, this->VAO, this->Program);
+    }
+
+  this->Program->SetUniformi("Input", this->Input->GetTextureUnit());
+  float invTexSize[2] = { 1.f / static_cast<float>(this->Viewport[2]),
+                          1.f / static_cast<float>(this->Viewport[3]) };
+  this->Program->SetUniform2f("InvTexSize", invTexSize);
+
+  this->Program->SetUniformf("RelativeContrastThreshold",
+                             this->RelativeContrastThreshold);
+  this->Program->SetUniformf("HardContrastThreshold",
+                             this->HardContrastThreshold);
+  this->Program->SetUniformf("SubpixelBlendLimit",
+                             this->SubpixelBlendLimit);
+  this->Program->SetUniformf("SubpixelContrastThreshold",
+                             this->SubpixelContrastThreshold);
+  this->Program->SetUniformi("EndpointSearchIterations",
+                             this->EndpointSearchIterations);
+
+  this->VAO->Bind();
+  GLUtil::DrawFullScreenQuad();
+  this->VAO->Release();
+  this->Input->Deactivate();
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::SubstituteFragmentShader(std::string &fragShader)
+{
+  if (this->UseHighQualityEndpoints)
+    {
+    vtkShaderProgram::Substitute(fragShader, "//VTK::EndpointAlgo::Def",
+                                 "#define FXAA_USE_HIGH_QUALITY_ENDPOINTS");
+    }
+
+#define DEBUG_OPT_CASE(optName) \
+  case optName: \
+    vtkShaderProgram::Substitute(fragShader, "//VTK::DebugOptions::Def", \
+                                 "#define " #optName); \
+    break
+
+
+  switch (this->DebugOptionValue)
+    {
+    default:
+    case FXAA_NO_DEBUG:
+      break;
+    DEBUG_OPT_CASE(FXAA_DEBUG_SUBPIXEL_ALIASING);
+    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_DIRECTION);
+    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_NUM_STEPS);
+    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_DISTANCE);
+    DEBUG_OPT_CASE(FXAA_DEBUG_EDGE_SAMPLE_OFFSET);
+    DEBUG_OPT_CASE(FXAA_DEBUG_ONLY_SUBPIX_AA);
+    DEBUG_OPT_CASE(FXAA_DEBUG_ONLY_EDGE_AA);
+    }
+
+#undef DEBUG_OPT_CASE
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::Finalize()
+{
+  if (this->BlendState)
+    {
+    glEnable(GL_BLEND);
+    }
+  if (this->DepthTestState)
+    {
+    glEnable(GL_DEPTH_TEST);
+    }
+
+  vtkOpenGLCheckErrorMacro("Error after restoring GL state.");
+
+#ifdef FXAA_BENCHMARK
+  if (this->GetTimeQueryResult(this->PreparationTimeQuery,
+                               this->PreparationTime) ||
+      this->GetTimeQueryResult(this->FXAATimeQuery,
+                               this->FXAATime))
+    {
+    this->PrintBenchmark();
+    }
+#endif // FXAA_BENCHMARK
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::StartTimeQuery(unsigned int &queryId, bool &tracker)
+{
+#ifdef FXAA_BENCHMARK
+  // Don't start a new query until the old results are in:
+  if (tracker || queryId != 0)
+    {
+    return;
+    }
+
+  glGenQueries(1, &queryId);
+  glBeginQuery(GL_TIME_ELAPSED, queryId);
+  tracker = true;
+
+  vtkOpenGLCheckErrorMacro("Error after starting GL_TIME_ELAPSED query.");
+#else // FXAA_BENCHMARK
+  (void)queryId;
+  (void)tracker;
+#endif // FXAA_BENCHMARK
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::EndTimeQuery(unsigned int &queryId, bool &tracker)
+{
+#ifdef FXAA_BENCHMARK
+  if (!tracker || queryId == 0)
+    { // Shouldn't happen, but just in case...
+    return;
+    }
+
+  glEndQuery(GL_TIME_ELAPSED);
+  tracker = false;
+
+  vtkOpenGLCheckErrorMacro("Error after ending GL_TIME_ELAPSED query.");
+#else // FXAA_BENCHMARK
+  (void)queryId;
+  (void)tracker;
+#endif // FXAA_BENCHMARK
+}
+
+//------------------------------------------------------------------------------
+bool vtkOpenGLFXAAFilter::GetTimeQueryResult(unsigned int &queryId,
+                                             unsigned int &result)
+{
+#ifdef FXAA_BENCHMARK
+  if (queryId == 0)
+    {
+    return false;
+    }
+
+  GLint ready;
+  glGetQueryObjectiv(queryId, GL_QUERY_RESULT_AVAILABLE, &ready);
+  vtkOpenGLCheckErrorMacro("Error after checking GL_TIME_ELAPSED status.");
+  if (!ready)
+    {
+    return false;
+    }
+
+  glGetQueryObjectuiv(queryId, GL_QUERY_RESULT, &result);
+  vtkOpenGLCheckErrorMacro("Error after checking GL_TIME_ELAPSED result.");
+
+  // Free the query:
+  glDeleteQueries(1, &queryId);
+  queryId = 0;
+
+  return true;
+#else // FXAA_BENCHMARK
+  (void)queryId;
+  (void)result;
+  return false;
+#endif // FXAA_BENCHMARK
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLFXAAFilter::PrintBenchmark()
+{
+#ifdef FXAA_BENCHMARK
+  int numPixels = this->Input->GetWidth() * this->Input->GetHeight();
+  float ptime = static_cast<float>(this->PreparationTime);
+  float ftime = static_cast<float>(this->FXAATime);
+  float ttime = ptime + ftime;
+
+  float ptimePerPixel = ptime / numPixels;
+  float ftimePerPixel = ftime / numPixels;
+  float ttimePerPixel = ttime / numPixels;
+
+  // Convert the non-per-pixel times to ms:
+  ptime *= 1e-6f;
+  ftime *= 1e-6f;
+  ttime *= 1e-6f;
+
+  std::cerr << "FXAA Info:\n"
+            << " - Number of pixels: " << numPixels << "\n"
+            << " - Preparation time: " << ptime << "ms ("
+            << ptimePerPixel << "ns per pixel)\n"
+            << " - FXAA time: " << ftime << "ms ("
+            << ftimePerPixel << "ns per pixel)\n"
+            << " - Total time: " << ttime << "ms ("
+            << ttimePerPixel << "ns per pixel)\n";
+#endif // FXAA_BENCHMARK
+}
diff --git a/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ca4db0e6e369bf30cbe42ca32b73998fe995b02
--- /dev/null
+++ b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h
@@ -0,0 +1,241 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkOpenGLFXAAFilter.h
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// .NAME vtkOpenGLFXAAFilter - Perform FXAA antialiasing on the current
+// framebuffer.
+//
+// .SECTION Description
+// Call Execute() to run a FXAA antialiasing pass on the current OpenGL
+// framebuffer. See method documentation for tunable parameters.
+//
+// Based on the following implementation and description:
+//
+// Whitepaper:
+// http://developer.download.nvidia.com/assets/gamedev/files/sdk/11/FXAA_WhitePaper.pdf
+//
+// Sample implementation:
+// https://github.com/NVIDIAGameWorks/GraphicsSamples/blob/master/samples/es3-kepler/FXAA/FXAA3_11.h
+//
+// TODO there are currently some "banding" artifacts on some edges, particularly
+// single pixel lines. These seem to be caused by using a linear RGB input,
+// rather than a gamma-correct sRGB input. Future work should combine this pass
+// with a gamma correction pass to correct this. Bonus points for precomputing
+// luminosity into the sRGB's alpha channel to save cycles in the FXAA shader!
+
+#ifndef vtkOpenGLFXAAFilter_h
+#define vtkOpenGLFXAAFilter_h
+
+#include "vtkRenderingOpenGL2Module.h" // For export macro
+#include "vtkObject.h"
+
+class vtkOpenGLBufferObject;
+class vtkOpenGLVertexArrayObject;
+class vtkOpenGLRenderer;
+class vtkShaderProgram;
+class vtkTextureObject;
+
+class VTKRENDERINGOPENGL2_EXPORT vtkOpenGLFXAAFilter: public vtkObject
+{
+public:
+  static vtkOpenGLFXAAFilter* New();
+  vtkTypeMacro(vtkOpenGLFXAAFilter, vtkObject)
+  virtual void PrintSelf(ostream &os, vtkIndent indent);
+
+  // Description:
+  // Perform FXAA on the current render buffer in @a ren.
+  void Execute(vtkOpenGLRenderer *ren);
+
+  // Description:
+  // Release all OpenGL state.
+  void ReleaseGraphicsResources();
+
+  // Description:
+  // Threshold for applying FXAA to a pixel, relative to the maximum luminosity
+  // of its 4 immediate neighbors.
+  //
+  // The luminosity of the current pixel and it's NSWE neighbors is computed.
+  // The maximum luminosity and luminosity range (contrast) of all 5 pixels is
+  // found. If the contrast is less than RelativeContrastThreshold * maxLum,
+  // the pixel is not considered aliased and will not be affected by FXAA.
+  //
+  // Suggested settings:
+  // - 1/3: Too little
+  // - 1/4: Low quality
+  // - 1/8: High quality (default)
+  // - 1/16: Overkill
+  vtkSetClampMacro(RelativeContrastThreshold, float, 0.f, 1.f)
+  vtkGetMacro(RelativeContrastThreshold, float)
+
+  // Description:
+  // Similar to RelativeContrastThreshold, but not scaled by the maximum
+  // luminosity.
+  //
+  // If the contrast of the current pixel and it's 4 immediate NSWE neighbors is
+  // less than HardContrastThreshold, the pixel is not considered aliased and
+  // will not be affected by FXAA.
+  //
+  // Suggested settings:
+  // - 1/32: Visible limit
+  // - 1/16: High quality (default)
+  // - 1/12: Upper limit (start of visible unfiltered edges)
+  vtkSetClampMacro(HardContrastThreshold, float, 0.f, 1.f)
+  vtkGetMacro(HardContrastThreshold, float)
+
+  // Description:
+  // Subpixel aliasing is corrected by applying a lowpass filter to the current
+  // pixel. This is implemented by blending an average of the 3x3 neighborhood
+  // around the pixel into the final result. The amount of blending is
+  // determined by comparing the detected amount of subpixel aliasing to the
+  // total contrasting of the CNSWE pixels:
+  //
+  // SubpixelBlending = abs(lumC - lumAveNSWE) / (lumMaxCNSWE - lumMinCNSWE)
+  //
+  // This parameter sets an upper limit to the amount of subpixel blending to
+  // prevent the image from simply getting blurred.
+  //
+  // Suggested settings:
+  // - 1/2: Low amount of blending.
+  // - 3/4: Medium amount of blending (default)
+  // - 7/8: High amount of blending.
+  // - 1: Maximum amount of blending.
+  vtkSetClampMacro(SubpixelBlendLimit, float, 0.f, 1.f)
+  vtkGetMacro(SubpixelBlendLimit, float)
+
+  // Description:
+  // Minimum amount of subpixel aliasing required for subpixel antialiasing to
+  // be applied.
+  //
+  // Subpixel aliasing is corrected by applying a lowpass filter to the current
+  // pixel. This is implemented by blending an average of the 3x3 neighborhood
+  // around the pixel into the final result. The amount of blending is
+  // determined by comparing the detected amount of subpixel aliasing to the
+  // total contrasting of the CNSWE pixels:
+  //
+  // SubpixelBlending = abs(lumC - lumAveNSWE) / (lumMaxCNSWE - lumMinCNSWE)
+  //
+  // If SubpixelBlending is less than this threshold, no lowpass blending will
+  // occur.
+  //
+  // Suggested settings:
+  // - 1/2: Low subpixel aliasing removal
+  // - 1/3: Medium subpixel aliasing removal
+  // - 1/4: Default subpixel aliasing removal
+  // - 1/8: High subpixel aliasing removal
+  // - 0: Complete subpixel aliasing removal
+  vtkSetClampMacro(SubpixelContrastThreshold, float, 0.f, 1.f)
+  vtkGetMacro(SubpixelContrastThreshold, float)
+
+  // Description:
+  // Use an improved edge endpoint detection algorithm.
+  //
+  // If true, a modified edge endpoint detection algorithm is used that requires
+  // more texture lookups, but will properly detect aliased single-pixel lines.
+  //
+  // If false, the edge endpoint algorithm proposed by NVIDIA will by used. This
+  // algorithm is faster (fewer lookups), but will fail to detect endpoints of
+  // single pixel edge steps.
+  //
+  // Default setting is true.
+  virtual void SetUseHighQualityEndpoints(bool val);
+  vtkGetMacro(UseHighQualityEndpoints, bool)
+  vtkBooleanMacro(UseHighQualityEndpoints, bool)
+
+  // Description:
+  // Set the number of iterations for the endpoint search algorithm. Increasing
+  // this value will increase runtime, but also properly detect longer edges.
+  // The current implementation steps one pixel in both the positive and
+  // negative directions per iteration. The default value is 12, which will
+  // resolve endpoints of edges < 25 pixels long (2 * 12 + 1).
+  vtkSetClampMacro(EndpointSearchIterations, int, 0, VTK_INT_MAX)
+  vtkGetMacro(EndpointSearchIterations, int)
+
+  // Description:
+  // Debugging options that affect the output color buffer. See
+  // vtkFXAAFilterFS.glsl for details.
+  enum DebugOption
+  {
+    FXAA_NO_DEBUG = 0,
+    FXAA_DEBUG_SUBPIXEL_ALIASING,
+    FXAA_DEBUG_EDGE_DIRECTION,
+    FXAA_DEBUG_EDGE_NUM_STEPS,
+    FXAA_DEBUG_EDGE_DISTANCE,
+    FXAA_DEBUG_EDGE_SAMPLE_OFFSET,
+    FXAA_DEBUG_ONLY_SUBPIX_AA,
+    FXAA_DEBUG_ONLY_EDGE_AA
+  };
+
+  // Description:
+  // Debugging options that affect the output color buffer. See
+  // vtkFXAAFilterFS.glsl for details. Only one may be active at a time.
+  virtual void SetDebugOptionValue(DebugOption opt);
+  vtkGetMacro(DebugOptionValue, DebugOption)
+
+protected:
+  vtkOpenGLFXAAFilter();
+  ~vtkOpenGLFXAAFilter();
+
+  void Prepare();
+  void FreeGLObjects();
+  void CreateGLObjects();
+  void LoadInput();
+  void ApplyFilter();
+  void SubstituteFragmentShader(std::string &fragShader);
+  void Finalize();
+
+  void StartTimeQuery(unsigned int &queryId, bool &tracker);
+  void EndTimeQuery(unsigned int &queryId, bool &tracker);
+  bool GetTimeQueryResult(unsigned int &queryId, unsigned int &result);
+  void PrintBenchmark();
+
+  // Cache GL state that we modify
+  bool BlendState;
+  bool DepthTestState;
+
+  int Viewport[4]; // x, y, width, height
+
+  // Used to measure execution time:
+  bool PreparationTimeQueryActive;
+  bool FXAATimeQueryActive;
+  unsigned int PreparationTimeQuery;
+  unsigned int FXAATimeQuery;
+  unsigned int PreparationTime;
+  unsigned int FXAATime;
+
+  // Parameters:
+  float RelativeContrastThreshold;
+  float HardContrastThreshold;
+  float SubpixelBlendLimit;
+  float SubpixelContrastThreshold;
+  int EndpointSearchIterations;
+
+  bool UseHighQualityEndpoints;
+  DebugOption DebugOptionValue;
+
+  // Set to true when the shader definitions change so we know when to rebuild.
+  bool NeedToRebuildShader;
+
+  vtkOpenGLRenderer *Renderer;
+  vtkTextureObject *Input;
+
+  vtkShaderProgram *Program;
+  vtkOpenGLVertexArrayObject *VAO;
+  vtkOpenGLBufferObject *VBO;
+
+private:
+  vtkOpenGLFXAAFilter(const vtkOpenGLFXAAFilter&) VTK_DELETE_FUNCTION;
+  void operator=(const vtkOpenGLFXAAFilter&) VTK_DELETE_FUNCTION;
+};
+
+#endif // vtkOpenGLFXAAFilter_h
diff --git a/Rendering/OpenGL2/vtkOpenGLRenderer.cxx b/Rendering/OpenGL2/vtkOpenGLRenderer.cxx
index 23240406c51f9a8442d534a0e645c012e0680823..7671dcaf2e702aeb4566ec2b1e2207e859148c17 100644
--- a/Rendering/OpenGL2/vtkOpenGLRenderer.cxx
+++ b/Rendering/OpenGL2/vtkOpenGLRenderer.cxx
@@ -27,6 +27,7 @@ PURPOSE.  See the above copyright notice for more information.
 #include "vtkObjectFactory.h"
 #include "vtkOpenGLCamera.h"
 #include "vtkOpenGLError.h"
+#include "vtkOpenGLFXAAFilter.h"
 #include "vtkOpenGLRenderWindow.h"
 #include "vtkPointData.h"
 #include "vtkPoints.h"
@@ -73,6 +74,7 @@ vtkOpenGLRenderer::vtkOpenGLRenderer()
   this->PickInfo->NumPicked = 0;
   this->PickedZ = 0;
 
+  this->FXAAFilter = 0;
   this->DepthPeelingPass = 0;
   this->ShadowMapPass = 0;
   this->DepthPeelingHigherLayer=0;
@@ -228,6 +230,33 @@ int vtkOpenGLRenderer::UpdateGeometry()
       }
     }
 
+  // Apply FXAA before volumes and overlays. Volumes don't need AA, and overlays
+  // are usually things like text, which are already antialiased.
+  if (this->UseFXAA)
+    {
+    if (!this->FXAAFilter)
+      {
+      this->FXAAFilter = vtkOpenGLFXAAFilter::New();
+      }
+
+    this->FXAAFilter->SetRelativeContrastThreshold(
+          this->FXAARelativeContrastThreshold);
+    this->FXAAFilter->SetHardContrastThreshold(
+          this->FXAAHardContrastThreshold);
+    this->FXAAFilter->SetSubpixelBlendLimit(
+          this->FXAASubpixelBlendLimit);
+    this->FXAAFilter->SetSubpixelContrastThreshold(
+          this->FXAASubpixelContrastThreshold);
+    this->FXAAFilter->SetEndpointSearchIterations(
+          this->FXAAEndpointSearchIterations);
+    this->FXAAFilter->SetUseHighQualityEndpoints(
+          this->FXAAUseHighQualityEndpoints);
+    this->FXAAFilter->SetDebugOptionValue(
+          static_cast<vtkOpenGLFXAAFilter::DebugOption>(this->FXAADebugOption));
+
+    this->FXAAFilter->Execute(this);
+    }
+
   // loop through props and give them a chance to
   // render themselves as volumetric geometry.
   for ( i = 0; i < this->PropArrayCount; i++ )
@@ -587,6 +616,10 @@ void vtkOpenGLRenderer::ReleaseGraphicsResources(vtkWindow *w)
     {
     this->Pass->ReleaseGraphicsResources(w);
     }
+  if (this->FXAAFilter)
+    {
+    this->FXAAFilter->ReleaseGraphicsResources();
+    }
   if (w && this->DepthPeelingPass)
     {
     this->DepthPeelingPass->ReleaseGraphicsResources(w);
@@ -715,6 +748,12 @@ vtkOpenGLRenderer::~vtkOpenGLRenderer()
     this->Pass = NULL;
     }
 
+  if (this->FXAAFilter)
+    {
+    this->FXAAFilter->Delete();
+    this->FXAAFilter = 0;
+    }
+
   if (this->ShadowMapPass)
     {
     this->ShadowMapPass->Delete();
diff --git a/Rendering/OpenGL2/vtkOpenGLRenderer.h b/Rendering/OpenGL2/vtkOpenGLRenderer.h
index 1f2adafe8352221b320a0fa530ee21c00d63b07c..5da5f7df6f446ccb466e0da500692cd118a8cae2 100644
--- a/Rendering/OpenGL2/vtkOpenGLRenderer.h
+++ b/Rendering/OpenGL2/vtkOpenGLRenderer.h
@@ -24,6 +24,7 @@
 #include "vtkRenderer.h"
 #include <vector>  // STL Header
 
+class vtkOpenGLFXAAFilter;
 class vtkRenderPass;
 class vtkOpenGLTexture;
 class vtkTextureObject;
@@ -107,6 +108,10 @@ protected:
   friend class vtkOpenGLImageSliceMapper;
   friend class vtkOpenGLImageResliceMapper;
 
+  // Description:
+  // FXAA is delegated to an instance of vtkOpenGLFXAAFilter
+  vtkOpenGLFXAAFilter *FXAAFilter;
+
   // Description:
   // Depth peeling is delegated to an instance of vtkDepthPeelingPass
   vtkDepthPeelingPass *DepthPeelingPass;