Commit fc9878ef authored by David C. Lonie's avatar David C. Lonie

Add an FXAA implementation.

FXAA is an antialiasing technique that is applied in a post-processing
pass. This implementation:

- Is a single-pass fragment shader.
- Detects edges.
- Searches for edge endpoints.
- Interpolates colors along the edge to give a smooth, antialiased
  color gradient.
- Detects and corrects sub-pixel antialiasing.
parent 94dfcdac
......@@ -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;
......
......@@ -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.
......
......@@ -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
......
......@@ -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
......
/*=========================================================================
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;
}
This diff is collapsed.
......@@ -11,6 +11,7 @@ vtk_module(vtkRenderingOpenGL2
TEST_DEPENDS
vtkIOLegacy
vtkRenderingImage
vtkRenderingFreeType
vtkTestingCore
vtkTestingRendering
vtkImagingGeneral
......
/*=========================================================================
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);