Commit b3adde6e authored by Alvaro Sanchez's avatar Alvaro Sanchez
Browse files

Fixed stochastic jittering and added API to customize noise settings.

The mapper uses vtkPerlinNoise by default with a texture size and frequency
equivalent to the window size.  It is possible to fully customize the noise
generator and texture size.  UseJittering changed to be off by default so some
of the baselines were updated.  Added tests for both default and customized noise
ray jittering.  Disabled TestGPURayCastMapperSampleDistance in legacy OpenGL.
parent 94d03e0b
......@@ -17,7 +17,6 @@ set (GenericVolumeCxxTests
TestGPURayCastFourComponentsMinIP.cxx
TestGPURayCastFourComponentsMIP.cxx
TestGPURayCastMapperBenchmark.cxx
TestGPURayCastMapperSampleDistance.cxx
TestGPURayCastMIPBinaryMask.cxx
TestGPURayCastMIPToComposite.cxx
TestGPURayCastNearestDataTypesMIP.cxx
......@@ -67,7 +66,10 @@ set (VolumeOpenGL2CxxTests
TestGPURayCastGradientOpacityLight.cxx
TestGPURayCastIndependentComponentsLightParameters.cxx
TestGPURayCastIndependentVectorMode.cxx
TestGPURayCastJittering.cxx
TestGPURayCastJitteringCustom.cxx
TestGPURayCastLargeColorTransferFunction.cxx
TestGPURayCastMapperSampleDistance.cxx
TestGPURayCastPositionalLights.cxx
TestGPURayCastReleaseResources.cxx
TestGPURayCastRenderDepthToImage.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestGPURayCastJittering.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.
=========================================================================*/
/** Tests stochastic jittering by rendering a volume exhibiting aliasing due
* to a big sampling distance (low sampling frequency), a.k.a. wood-grain
* artifacts. The expected output is 'filtered' due to the noise introduced
* by jittering the entry point of the rays.
*/
#include <iostream>
#include <vtkCamera.h>
#include <vtkColorTransferFunction.h>
#include <vtkGPUVolumeRayCastMapper.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkNew.h>
#include <vtkPiecewiseFunction.h>
#include <vtkRegressionTestImage.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkStructuredPointsReader.h>
#include <vtkTestUtilities.h>
#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
static const char* TestGPURayCastJitteringLog =
"# StreamVersion 1\n"
"EnterEvent 298 27 0 0 0 0 0\n"
"MouseWheelForwardEvent 200 142 0 0 0 0 0\n"
"LeaveEvent 311 71 0 0 0 0 0\n";
int TestGPURayCastJittering(int argc, char* argv[])
{
cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl;
char* volumeFile = vtkTestUtilities::ExpandDataFileName(
argc, argv, "Data/ironProt.vtk");
vtkNew<vtkStructuredPointsReader> reader;
reader->SetFileName(volumeFile);
delete[] volumeFile;
vtkNew<vtkGPUVolumeRayCastMapper> mapper;
mapper->SetInputConnection(reader->GetOutputPort());
mapper->SetAutoAdjustSampleDistances(0);
mapper->SetSampleDistance(2.0);
mapper->UseJitteringOn();
vtkNew<vtkColorTransferFunction> color;
color->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
color->AddRGBPoint(64.0, 1.0, 0.0, 0.0);
color->AddRGBPoint(128.0, 0.0, 0.0, 1.0);
color->AddRGBPoint(192.0, 0.0, 1.0, 0.0);
color->AddRGBPoint(255.0, 0.0, 0.2, 0.0);
vtkNew<vtkPiecewiseFunction> opacity;
opacity->AddPoint(0.0, 0.0);
opacity->AddPoint(255.0, 1.0);
vtkNew<vtkVolumeProperty> property;
property->SetColor(color.GetPointer());
property->SetScalarOpacity(opacity.GetPointer());
property->SetInterpolationTypeToLinear();
property->ShadeOff();
vtkNew<vtkVolume> volume;
volume->SetMapper(mapper.GetPointer());
volume->SetProperty(property.GetPointer());
vtkNew<vtkRenderWindow> renWin;
renWin->SetSize(400, 400);
renWin->SetMultiSamples(0);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin.GetPointer());
vtkNew<vtkInteractorStyleTrackballCamera> style;
iren->SetInteractorStyle(style.GetPointer());
vtkNew<vtkRenderer> ren;
renWin->AddRenderer(ren.GetPointer());
ren->AddVolume(volume.GetPointer());
ren->ResetCamera();
ren->GetActiveCamera()->SetPosition(79.1817, 14.6622, 62.9264);
ren->GetActiveCamera()->SetFocalPoint(32.0598, 26.5308, 28.0257);
renWin->Render();
iren->Initialize();
int rv = vtkTesting::InteractorEventLoop(argc, argv, iren.GetPointer(),
TestGPURayCastJitteringLog);
return rv;
}
/*=========================================================================
Program: Visualization Toolkit
Module: TestGPURayCastJitteringCustom.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.
=========================================================================*/
/** Tests stochastic jittering by rendering a volume exhibiting aliasing due
* to a big sampling distance (low sampling frequency), a.k.a. wood-grain
* artifacts. The expected output is 'filtered' due to the noise introduced
* by a customized noise generator.
*/
#include <iostream>
#include <vtkCamera.h>
#include <vtkColorTransferFunction.h>
#include <vtkGPUVolumeRayCastMapper.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkNew.h>
#include <vtkOpenGLGPUVolumeRayCastMapper.h>
#include <vtkPerlinNoise.h>
#include <vtkPiecewiseFunction.h>
#include <vtkRegressionTestImage.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkStructuredPointsReader.h>
#include <vtkTestUtilities.h>
#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
static const char* TestGPURayCastJitteringCustomLog =
"# StreamVersion 1\n"
"EnterEvent 298 27 0 0 0 0 0\n"
"MouseWheelForwardEvent 200 142 0 0 0 0 0\n"
"LeaveEvent 311 71 0 0 0 0 0\n";
int TestGPURayCastJitteringCustom(int argc, char* argv[])
{
cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl;
char* volumeFile = vtkTestUtilities::ExpandDataFileName(
argc, argv, "Data/ironProt.vtk");
vtkNew<vtkStructuredPointsReader> reader;
reader->SetFileName(volumeFile);
delete[] volumeFile;
vtkNew<vtkGPUVolumeRayCastMapper> mapper;
mapper->SetInputConnection(reader->GetOutputPort());
mapper->SetAutoAdjustSampleDistances(0);
mapper->SetSampleDistance(2.0);
mapper->UseJitteringOn();
vtkNew<vtkColorTransferFunction> color;
color->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
color->AddRGBPoint(64.0, 1.0, 0.0, 0.0);
color->AddRGBPoint(128.0, 0.0, 0.0, 1.0);
color->AddRGBPoint(192.0, 0.0, 1.0, 0.0);
color->AddRGBPoint(255.0, 0.0, 0.2, 0.0);
vtkNew<vtkPiecewiseFunction> opacity;
opacity->AddPoint(0.0, 0.0);
opacity->AddPoint(255.0, 1.0);
vtkNew<vtkVolumeProperty> property;
property->SetColor(color.GetPointer());
property->SetScalarOpacity(opacity.GetPointer());
property->SetInterpolationTypeToLinear();
property->ShadeOff();
vtkNew<vtkVolume> volume;
volume->SetMapper(mapper.GetPointer());
volume->SetProperty(property.GetPointer());
vtkNew<vtkRenderWindow> renWin;
renWin->SetSize(400, 400);
renWin->SetMultiSamples(0);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin.GetPointer());
vtkNew<vtkInteractorStyleTrackballCamera> style;
iren->SetInteractorStyle(style.GetPointer());
vtkNew<vtkRenderer> ren;
renWin->AddRenderer(ren.GetPointer());
ren->AddVolume(volume.GetPointer());
ren->ResetCamera();
ren->GetActiveCamera()->SetPosition(79.1817, 14.6622, 62.9264);
ren->GetActiveCamera()->SetFocalPoint(32.0598, 26.5308, 28.0257);
renWin->Render();
iren->Initialize();
// Customize the noise function and texture size
vtkOpenGLGPUVolumeRayCastMapper* glMapper =
vtkOpenGLGPUVolumeRayCastMapper::SafeDownCast(mapper.GetPointer());
int texSize[2] = {600, 600};
glMapper->SetNoiseTextureSize(texSize);
vtkPerlinNoise* generator = vtkPerlinNoise::New();
generator->SetFrequency(1024.0, 1024.0, 1.0);
generator->SetAmplitude(0.5);
glMapper->SetNoiseGenerator(generator);
generator->Delete();
renWin->Render();
int rv = vtkTesting::InteractorEventLoop(argc, argv, iren.GetPointer(),
TestGPURayCastJitteringCustomLog);
return rv;
}
c7e9a85963c511e7ab5df6f7ee101d63
91d78e44c46c14aa2d132b8be076ec5f
0a7df901d35fd6a97b8fabcf1d6b1d2d
821a0e5598e9af3d084ab23820679702
......@@ -50,7 +50,7 @@ vtkGPUVolumeRayCastMapper::vtkGPUVolumeRayCastMapper()
this->RenderToImage = 0;
this->DepthImageScalarType = VTK_FLOAT;
this->ClampDepthToBackface = 0;
this->UseJittering = 1;
this->UseJittering = 0;
this->UseDepthPass = 0;
this->DepthPassContourValues = NULL;
this->SampleDistance = 1.0;
......
......@@ -103,7 +103,7 @@ public:
this->CubeIndicesId = 0;
this->InterpolationType = vtkTextureObject::Linear;
this->VolumeTextureObject = 0;
this->NoiseTextureObject = 0;
this->NoiseTextureObject = NULL;
this->DepthTextureObject = 0;
this->TextureWidth = 1024;
this->ActualSampleDistance = 1.0;
......@@ -127,7 +127,7 @@ public:
this->CurrentSelectionPass = vtkHardwareSelector::MIN_KNOWN_PASS - 1;
this->CellScale[0] = this->CellScale[1] = this->CellScale[2] = 0.0;
this->NoiseTextureData = 0;
this->NoiseTextureData = NULL;
this->NumberOfLights = 0;
this->LightComplexity = 0;
......@@ -173,37 +173,37 @@ public:
if (this->NoiseTextureObject)
{
this->NoiseTextureObject->Delete();
this->NoiseTextureObject = 0;
this->NoiseTextureObject = NULL;
}
if (this->DepthTextureObject)
{
this->DepthTextureObject->Delete();
this->DepthTextureObject = 0;
this->DepthTextureObject = NULL;
}
if (this->FBO)
{
this->FBO->Delete();
this->FBO = 0;
this->FBO = NULL;
}
if (this->RTTDepthBufferTextureObject)
{
this->RTTDepthBufferTextureObject->Delete();
this->RTTDepthBufferTextureObject = 0;
this->RTTDepthBufferTextureObject = NULL;
}
if (this->RTTDepthTextureObject)
{
this->RTTDepthTextureObject->Delete();
this->RTTDepthTextureObject = 0;
this->RTTDepthTextureObject = NULL;
}
if (this->RTTColorTextureObject)
{
this->RTTColorTextureObject->Delete();
this->RTTColorTextureObject = 0;
this->RTTColorTextureObject = NULL;
}
this->DeleteTransferFunctions();
......@@ -382,7 +382,6 @@ public:
std::vector<double> Bias;
float* NoiseTextureData;
GLint NoiseTextureSize;
float ActualSampleDistance;
......@@ -1353,64 +1352,82 @@ int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::
void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::CreateNoiseTexture(
vtkRenderer* ren)
{
vtkOpenGLRenderWindow* glWindow = vtkOpenGLRenderWindow::SafeDownCast(
ren->GetRenderWindow());
if (!this->NoiseTextureObject)
{
this->NoiseTextureObject = vtkTextureObject::New();
}
this->NoiseTextureObject->SetContext(glWindow);
this->NoiseTextureObject->SetContext(vtkOpenGLRenderWindow::SafeDownCast(
ren->GetRenderWindow()));
bool updateSize = false;
bool useUserSize = this->Parent->NoiseTextureSize[0] > 0 &&
this->Parent->NoiseTextureSize[1] > 0;
if (useUserSize)
{
int const twidth = this->NoiseTextureObject->GetWidth();
int const theight = this->NoiseTextureObject->GetHeight();
updateSize = this->Parent->NoiseTextureSize[0] != twidth ||
this->Parent->NoiseTextureSize[1] != theight;
}
if (!this->NoiseTextureObject->GetHandle())
if (!this->NoiseTextureObject->GetHandle() || updateSize ||
this->NoiseTextureObject->GetMTime() < this->Parent->NoiseGenerator->GetMTime())
{
GLsizei size = 128;
GLint maxSize;
int* winSize = ren->GetRenderWindow()->GetSize();
int sizeX = useUserSize ? this->Parent->NoiseTextureSize[0] : winSize[0];
int sizeY = useUserSize ? this->Parent->NoiseTextureSize[1] : winSize[1];
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxSize);
if (size > maxSize)
int const maxSize = vtkTextureObject::GetMaximumTextureSize(glWindow);
if (sizeX > maxSize || sizeY > maxSize)
{
size = maxSize;
sizeX = vtkMath::Max(sizeX, maxSize);
sizeY = vtkMath::Max(sizeY, maxSize);
}
if (this->NoiseTextureSize != size)
// Allocate buffer. After controlling for the maximum supported size sizeX/Y
// might have changed, so an additional check is needed.
int const twidth = this->NoiseTextureObject->GetWidth();
int const theight = this->NoiseTextureObject->GetHeight();
bool sizeChanged = sizeX != twidth || sizeY != theight;
if (sizeChanged || !this->NoiseTextureData)
{
delete[] this->NoiseTextureData;
this->NoiseTextureData = 0;
this->NoiseTextureData = NULL;
this->NoiseTextureData = new float[sizeX * sizeY];
}
if (this->NoiseTextureData == 0)
// Generate jitter noise
if (!this->Parent->NoiseGenerator)
{
this->NoiseTextureData = new float[size * size];
this->NoiseTextureSize = size;
vtkNew<vtkPerlinNoise> noiseGenerator;
noiseGenerator->SetFrequency(size, 1.0, 1.0);
noiseGenerator->SetPhase(0.0, 0.0, 0.0);
// -0.5 and 0.5 range
noiseGenerator->SetAmplitude(0.1);
int j = 0;
while(j < size)
{
int i = 0;
while(i < size)
{
this->NoiseTextureData[j * size + i] =
static_cast<float>(noiseGenerator->EvaluateFunction(i, j, 0.0) + 0.1);
++i;
}
++j;
}
// Use default settings
vtkPerlinNoise* perlinNoise = vtkPerlinNoise::New();
perlinNoise->SetPhase(0.0, 0.0, 0.0);
perlinNoise->SetFrequency(sizeX, sizeY, 1.0);
perlinNoise->SetAmplitude(0.5); /* [-n, n] */
this->Parent->NoiseGenerator = perlinNoise;
}
this->NoiseTextureObject->Create2DFromRaw(size,
size,
1,
VTK_FLOAT,
this->NoiseTextureData);
int const bufferSize = sizeX * sizeY;
for (int i = 0; i < bufferSize; i++)
{
int const x = i % sizeX;
int const y = i / sizeY;
this->NoiseTextureData[i] = static_cast<float>(
this->Parent->NoiseGenerator->EvaluateFunction(x, y, 0.0) + 0.1);
}
// Prepare texture
this->NoiseTextureObject->Create2DFromRaw(sizeX, sizeY, 1, VTK_FLOAT,
this->NoiseTextureData);
this->NoiseTextureObject->SetWrapS(vtkTextureObject::Repeat);
this->NoiseTextureObject->SetWrapT(vtkTextureObject::Repeat);
this->NoiseTextureObject->SetMagnificationFilter(vtkTextureObject::Nearest);
this->NoiseTextureObject->SetMinificationFilter(vtkTextureObject::Nearest);
this->NoiseTextureObject->SetBorderColor(0.0f, 0.0f, 0.0f, 0.0f);
this->NoiseTextureObject->Modified();
}
}
......@@ -2558,6 +2575,8 @@ vtkOpenGLGPUVolumeRayCastMapper::vtkOpenGLGPUVolumeRayCastMapper() :
this->Impl = new vtkInternal(this);
this->ReductionFactor = 1.0;
this->CurrentPass = RenderPass;
this->NoiseTextureSize[0] = this->NoiseTextureSize[1] = -1;
this->NoiseGenerator = NULL;
this->ResourceCallback = new vtkOpenGLResourceFreeCallback<vtkOpenGLGPUVolumeRayCastMapper>(this,
&vtkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources);
......@@ -2572,6 +2591,13 @@ vtkOpenGLGPUVolumeRayCastMapper::~vtkOpenGLGPUVolumeRayCastMapper()
delete this->ResourceCallback;
this->ResourceCallback = NULL;
}
if (this->NoiseGenerator)
{
this->NoiseGenerator->Delete();
this->NoiseGenerator = NULL;
}
delete this->Impl;
this->Impl = 0;
}
......@@ -3287,7 +3313,10 @@ void vtkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer* ren,
}
// Update noise sampler texture
this->Impl->CreateNoiseTexture(ren);
if (this->UseJittering)
{
this->Impl->CreateNoiseTexture(ren);
}
// Grab depth sampler buffer (to handle cases when we are rendering geometry
// and in_volume together
......@@ -3539,9 +3568,12 @@ void vtkOpenGLGPUVolumeRayCastMapper::DoGPURender(vtkRenderer* ren,
}
}
this->Impl->NoiseTextureObject->Activate();
prog->SetUniformi("in_noiseSampler",
this->Impl->NoiseTextureObject->GetTextureUnit());
if (this->Impl->NoiseTextureObject)
{
this->Impl->NoiseTextureObject->Activate();
prog->SetUniformi("in_noiseSampler",
this->Impl->NoiseTextureObject->GetTextureUnit());
}
// currently broken on ES
#if GL_ES_VERSION_2_0 != 1
......@@ -3806,7 +3838,10 @@ void vtkOpenGLGPUVolumeRayCastMapper::DoGPURender(vtkRenderer* ren,
// Undo binds and de-activate buffers
//--------------------------------------------------------------------------
this->Impl->VolumeTextureObject->Deactivate();
this->Impl->NoiseTextureObject->Deactivate();
if (this->Impl->NoiseTextureObject)
{
this->Impl->NoiseTextureObject->Deactivate();
}
this->Impl->DepthTextureObject->Deactivate();
for (int i = 0; i < numberOfSamplers; ++i)
......@@ -3839,3 +3874,7 @@ void vtkOpenGLGPUVolumeRayCastMapper::DoGPURender(vtkRenderer* ren,
vtkOpenGLCheckErrorMacro("failed after Render");
}
//----------------------------------------------------------------------------
vtkCxxSetObjectMacro(vtkOpenGLGPUVolumeRayCastMapper, NoiseGenerator,
vtkImplicitFunction);
......@@ -25,6 +25,7 @@ class vtkOpenGLCamera;
class vtkShaderProgram;
class vtkTextureObject;
class vtkGenericOpenGLResourceFreeCallback;
class vtkImplicitFunction;
//----------------------------------------------------------------------------
class VTKRENDERINGVOLUMEOPENGL2_EXPORT vtkOpenGLGPUVolumeRayCastMapper :
......@@ -73,6 +74,17 @@ public:
// the user.
vtkGetMacro(CurrentPass, int);
//@{
/**
* Sets a user defined function to generate the ray jittering noise.
* vtkPerlinNoise is used by default with a texture size equivlent to
* the window size. These settings will have no effect when UseJittering
* is Off.
*/
void SetNoiseGenerator(vtkImplicitFunction* generator);
vtkSetVector2Macro(NoiseTextureSize, int);
//@}
protected:
vtkOpenGLGPUVolumeRayCastMapper();
~vtkOpenGLGPUVolumeRayCastMapper();
......@@ -158,6 +170,9 @@ private:
class vtkInternal;
vtkInternal* Impl;
vtkImplicitFunction* NoiseGenerator;
int NoiseTextureSize[2];
vtkOpenGLGPUVolumeRayCastMapper(
const vtkOpenGLGPUVolumeRayCastMapper&) VTK_DELETE_FUNCTION;
void operator=(const vtkOpenGLGPUVolumeRayCastMapper&) VTK_DELETE_FUNCTION;
......
......@@ -339,9 +339,20 @@ namespace vtkvolume
\n g_dirStep = (ip_inverseTextureDataAdjusted *\
\n vec4(rayDir, 0.0)).xyz * in_sampleDistance;\
\n\
\n float jitterValue = (texture2D(in_noiseSampler, g_dataPos.xy).x);\
\n // 2D Texture fragment coordinates [0,1] from fragment coordinates.\
\n // The frame buffer texture has the size of the plain buffer but \
\n // we use a fraction of it. The texture coordinate is less than 1 if\
\n // the reduction factor is less than 1.\
\n // Device coordinates are between -1 and 1. We need texture\
\n // coordinates between 0 and 1. The in_noiseSampler and in_depthSampler\
\n // buffers have the original size buffer.\