diff --git a/Charts/Core/vtkScalarsToColorsItem.cxx b/Charts/Core/vtkScalarsToColorsItem.cxx index 69d898520e96617d350e1e4135a99793cc5476aa..86fae042eb4b5298b8cc18dbb3cf223b6ba0bb85 100644 --- a/Charts/Core/vtkScalarsToColorsItem.cxx +++ b/Charts/Core/vtkScalarsToColorsItem.cxx @@ -173,7 +173,8 @@ bool vtkScalarsToColorsItem::Paint(vtkContext2D* painter) trapezoids->Delete(); } - if (this->PolyLinePen->GetLineType() != vtkPen::NO_PEN) + if (this->PolyLinePen->GetLineType() != vtkPen::NO_PEN + && size >= 2) { const vtkRectd& ss = this->ShiftScale; diff --git a/Rendering/Core/vtkVolumeProperty.cxx b/Rendering/Core/vtkVolumeProperty.cxx index 66a587be33037c03c20a30bdb518288962a628c2..9665cd969904b470742f68e0fd77acb9f5c5860c 100644 --- a/Rendering/Core/vtkVolumeProperty.cxx +++ b/Rendering/Core/vtkVolumeProperty.cxx @@ -14,9 +14,14 @@ =========================================================================*/ #include "vtkVolumeProperty.h" +#include "vtkColorTransferFunction.h" +#include "vtkDataArray.h" +#include "vtkImageData.h" +#include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPiecewiseFunction.h" -#include "vtkColorTransferFunction.h" +#include "vtkPointData.h" + vtkStandardNewMacro(vtkVolumeProperty); @@ -36,8 +41,10 @@ vtkVolumeProperty::vtkVolumeProperty() this->ScalarOpacity[i] = NULL; this->ScalarOpacityUnitDistance[i] = 1.0; this->GradientOpacity[i] = NULL; + this->TransferFunction2D[i] = NULL; this->DefaultGradientOpacity[i] = NULL; this->DisableGradientOpacity[i] = 0; + this->TransferFunctionMode = vtkVolumeProperty::TF_1D; this->ComponentWeight[i] = 1.0; @@ -74,6 +81,11 @@ vtkVolumeProperty::~vtkVolumeProperty() this->GradientOpacity[i]->UnRegister(this); } + if (this->TransferFunction2D[i] != NULL) + { + this->TransferFunction2D[i]->UnRegister(this); + } + if (this->DefaultGradientOpacity[i] != NULL) { this->DefaultGradientOpacity[i]->UnRegister(this); @@ -140,6 +152,7 @@ void vtkVolumeProperty::UpdateMTimes() this->RGBTransferFunctionMTime[i].Modified(); this->ScalarOpacityMTime[i].Modified(); this->GradientOpacityMTime[i].Modified(); + this->TransferFunction2DMTime[i].Modified(); } } @@ -157,11 +170,11 @@ vtkMTimeType vtkVolumeProperty::GetMTime() { // time that Gray transfer function pointer was set time = this->GrayTransferFunctionMTime[i]; - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); // time that Gray transfer function was last modified time = this->GrayTransferFunction[i]->GetMTime(); - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); } } else if (this->ColorChannels[i] == 3) @@ -170,11 +183,11 @@ vtkMTimeType vtkVolumeProperty::GetMTime() { // time that RGB transfer function pointer was set time = this->RGBTransferFunctionMTime[i]; - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); // time that RGB transfer function was last modified time = this->RGBTransferFunction[i]->GetMTime(); - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); } } @@ -183,24 +196,36 @@ vtkMTimeType vtkVolumeProperty::GetMTime() { // time that Scalar opacity transfer function pointer was set time = this->ScalarOpacityMTime[i]; - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); // time that Scalar opacity transfer function was last modified time = this->ScalarOpacity[i]->GetMTime(); - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); + } + + // 2D Transfer Function MTimes + if (this->TransferFunction2D[i]) + { + // time that the TransferFunction2D pointer was set + time = this->TransferFunction2DMTime[i]; + mTime = vtkMath::Max(mTime, time); + + // time that the TransferFunction2D was last modified + time = this->TransferFunction2D[i]->GetMTime(); + mTime = vtkMath::Max(mTime, time); } if (this->GradientOpacity[i]) { // time that Gradient opacity transfer function pointer was set time = this->GradientOpacityMTime[i]; - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); if (!this->DisableGradientOpacity[i]) { // time that Gradient opacity transfer function was last modified time = this->GradientOpacity[i]->GetMTime(); - mTime = (mTime > time ? mTime : time); + mTime = vtkMath::Max(mTime, time); } } } @@ -237,6 +262,7 @@ void vtkVolumeProperty::SetColor( int index, vtkPiecewiseFunction *function ) this->GrayTransferFunctionMTime[index].Modified(); this->Modified(); + this->TransferFunctionMode = vtkVolumeProperty::TF_1D; } if (this->ColorChannels[index] != 1) @@ -282,6 +308,7 @@ void vtkVolumeProperty::SetColor( int index, vtkColorTransferFunction *function } this->RGBTransferFunctionMTime[index].Modified(); this->Modified(); + this->TransferFunctionMode = vtkVolumeProperty::TF_1D; } if (this->ColorChannels[index] != 3) @@ -328,6 +355,7 @@ void vtkVolumeProperty::SetScalarOpacity( int index, vtkPiecewiseFunction *funct this->ScalarOpacityMTime[index].Modified(); this->Modified(); + this->TransferFunctionMode = vtkVolumeProperty::TF_1D; } } @@ -390,6 +418,7 @@ void vtkVolumeProperty::SetGradientOpacity( int index, vtkPiecewiseFunction *fun this->GradientOpacityMTime[index].Modified(); this->Modified(); + this->TransferFunctionMode = vtkVolumeProperty::TF_1D; } } @@ -421,6 +450,45 @@ vtkPiecewiseFunction *vtkVolumeProperty::GetGradientOpacity( int index ) return this->GetStoredGradientOpacity(index); } +void vtkVolumeProperty::SetTransferFunction2D(int index, vtkImageData* function) +{ + if (this->TransferFunction2D[index] != function) + { + vtkDataArray* dataArr = function->GetPointData()->GetScalars(); + const int* dims = function->GetDimensions(); + if (!dataArr || dataArr->GetNumberOfComponents() != 4 || + dataArr->GetDataType() != VTK_FLOAT || dims[0] == 0) + { + const int type = dataArr->GetDataType(); + const int comp = dataArr->GetNumberOfComponents(); + vtkErrorMacro(<< "Invalid type (" << type << ") or number of components (" + << comp << ") or dimensions (" << dims[0] << ", " << dims[1] << ")." + " Expected VTK_FLOAT, 4 Components, dimensions > 0!"); + return; + } + + if (this->TransferFunction2D[index] != NULL) + { + this->TransferFunction2D[index]->UnRegister(this); + } + + this->TransferFunction2D[index] = function; + if (this->TransferFunction2D[index] != NULL) + { + this->TransferFunction2D[index]->Register(this); + } + + this->TransferFunction2DMTime[index].Modified(); + this->Modified(); + this->TransferFunctionMode = vtkVolumeProperty::TF_2D; + } +} + +vtkImageData* vtkVolumeProperty::GetTransferFunction2D(int index) +{ + return this->TransferFunction2D[index]; +} + // Get the gradient opacity transfer function. Create one if none set. vtkPiecewiseFunction *vtkVolumeProperty::GetStoredGradientOpacity( int index ) { @@ -597,6 +665,11 @@ vtkTimeStamp vtkVolumeProperty::GetRGBTransferFunctionMTime( int index ) return this->RGBTransferFunctionMTime[index]; } +vtkTimeStamp vtkVolumeProperty::GetTransferFunction2DMTime(int index) +{ + return this->TransferFunction2DMTime[index]; +} + vtkTimeStamp vtkVolumeProperty::GetGrayTransferFunctionMTime( int index ) { return this->GrayTransferFunctionMTime[index]; @@ -639,6 +712,8 @@ void vtkVolumeProperty::PrintSelf(ostream& os, vtkIndent indent) os << indent << "DisableGradientOpacity: " << (this->DisableGradientOpacity[i] ? "On" : "Off") << "\n"; + os << indent << "2D Transfer Function: " + << this->TransferFunction2D[i] << "\n"; os << indent << "ComponentWeight: " << this->ComponentWeight[i] << "\n"; @@ -655,6 +730,4 @@ void vtkVolumeProperty::PrintSelf(ostream& os, vtkIndent indent) // this->GrayTransferFunctionMTime // this->RGBTransferFunctionMTime // this->ScalarOpacityMTime - } - diff --git a/Rendering/Core/vtkVolumeProperty.h b/Rendering/Core/vtkVolumeProperty.h index d52c909a6bf1eab8487207baa851e14147e5bb47..f83f7fe05f01eb6edc4a0a8abb35833bde194509 100644 --- a/Rendering/Core/vtkVolumeProperty.h +++ b/Rendering/Core/vtkVolumeProperty.h @@ -17,18 +17,30 @@ * @class vtkVolumeProperty * @brief represents the common properties for rendering a volume. * - * * vtkVolumeProperty is used to represent common properties associated * with volume rendering. This includes properties for determining the type * of interpolation to use when sampling a volume, the color of a volume, * the scalar opacity of a volume, the gradient opacity of a volume, and the * shading parameters of a volume. * + * Color, scalar opacity and gradient magnitude opacity transfer functions + * can be set as either 3 separate 1D functions or as a single 2D transfer + * function. + * + * - 1D Transfer functions (vtkVolumeProperty::TF_1D) + * Color, scalar opacity and gradient magnitude opacity are defined by 1 + * vtkColorTransferFunction and 2 vtkPiecewiseFunctions respectively. * When the scalar opacity or the gradient opacity of a volume is not set, * then the function is defined to be a constant value of 1.0. When a * scalar and gradient opacity are both set simultaneously, then the opacity * is defined to be the product of the scalar opacity and gradient opacity - * transfer functions. + * transfer functions. 1D transfer functions is the legacy and default behavior. + * + * - 2D Transfer functions (vtkVolumeProperty::TF_2D) + * Color and scalar/gradient magnitude opacity are defined by a 4-component + * vtkImageData instance mapping scalar value vs. gradient magnitude on its + * x and y axis respectively. This mode is only available if a 2D TF has been + * explicitly set (see SetTransferFunction2D). * * Most properties can be set per "component" for volume mappers that * support multiple independent components. If you are using 2 component @@ -37,9 +49,8 @@ * will be used (and all color functions will be ignored). Omitting the * index parameter on the Set/Get methods will access index = 0. * - * @sa - * vtkPiecewiseFunction vtkColorTransferFunction -*/ + * @sa vtkPiecewiseFunction vtkColorTransferFunction + */ #ifndef vtkVolumeProperty_h #define vtkVolumeProperty_h @@ -47,9 +58,10 @@ #include "vtkRenderingCoreModule.h" // For export macro #include "vtkObject.h" +class vtkColorTransferFunction; +class vtkImageData; class vtkPiecewiseFunction; class vtkTimeStamp; -class vtkColorTransferFunction; class VTKRENDERINGCORE_EXPORT vtkVolumeProperty : public vtkObject { @@ -202,6 +214,42 @@ public: void SetGradientOpacity(vtkPiecewiseFunction *function) { this->SetGradientOpacity(0, function); } + //@{ + /** + * Set/Get a 2D transfer function. Volume mappers interpret the x-axis of + * of this transfer function as scalar value and the y-axis as gradient + * magnitude. The value at (X, Y) corresponds to the color and opacity + * for a salar value of X and a gradient magnitude of Y. + */ + void SetTransferFunction2D(int index, vtkImageData* function); + void SetTransferFunction2D(vtkImageData* function) + { + this->SetTransferFunction2D(0, function); + }; + + vtkImageData* GetTransferFunction2D(int index); + vtkImageData* GetTransferFunction2D() + { + return this->GetTransferFunction2D(0); + }; + + /** + * Color-opacity transfer function mode. TF_1D is its default value. + * - TF_1D Mappers will use 3 separate 1D functions for color, scalar opacity + * and gradient mag. opacity. + * - TF_2D Mappers will use a single 2D function for color and scalar/gradient mag. + * opacity. + */ + enum TransferMode + { + TF_1D = 0, + TF_2D + }; + + vtkSetClampMacro(TransferFunctionMode, int, 0, 1) + vtkGetMacro(TransferFunctionMode, int) + //@} + /** * Get the gradient magnitude opacity transfer function for * the given component. @@ -247,8 +295,12 @@ public: * will not work as in the former case, GetDisableGradientOpacity returns * false by default and in the later case, a default gradient opacity will be created. */ - bool HasGradientOpacity(int index=0) { - return (this->GradientOpacity[index] != NULL); + bool HasGradientOpacity(int index = 0) { + switch(this->TransferFunctionMode) { + case TF_1D: return (this->GradientOpacity[index] != NULL); + case TF_2D: return true; + } + return false; } //@{ @@ -379,6 +431,18 @@ protected: vtkVolumeProperty(); ~vtkVolumeProperty() VTK_OVERRIDE; + /** + * WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE + * Get the time when the TransferFunction2D was set. + */ + vtkTimeStamp GetTransferFunction2DMTime(int index); + vtkTimeStamp GetTransferFunction2DMTime() + { + return this->GetTransferFunction2DMTime(0); + } + + virtual void CreateDefaultGradientOpacity(int index); + int IndependentComponents; double ComponentWeight[VTK_MAX_VRCOMP]; @@ -398,17 +462,20 @@ protected: vtkPiecewiseFunction *GradientOpacity[VTK_MAX_VRCOMP]; vtkTimeStamp GradientOpacityMTime[VTK_MAX_VRCOMP]; + vtkPiecewiseFunction *DefaultGradientOpacity[VTK_MAX_VRCOMP]; int DisableGradientOpacity[VTK_MAX_VRCOMP]; + int TransferFunctionMode; + vtkImageData* TransferFunction2D[VTK_MAX_VRCOMP]; + vtkTimeStamp TransferFunction2DMTime[VTK_MAX_VRCOMP]; + int Shade[VTK_MAX_VRCOMP]; double Ambient[VTK_MAX_VRCOMP]; double Diffuse[VTK_MAX_VRCOMP]; double Specular[VTK_MAX_VRCOMP]; double SpecularPower[VTK_MAX_VRCOMP]; - virtual void CreateDefaultGradientOpacity(int index); - private: vtkVolumeProperty(const vtkVolumeProperty&) VTK_DELETE_FUNCTION; void operator=(const vtkVolumeProperty&) VTK_DELETE_FUNCTION; diff --git a/Rendering/Volume/Testing/Cxx/CMakeLists.txt b/Rendering/Volume/Testing/Cxx/CMakeLists.txt index e24c51332b028107a938b384e8b77342238b98ca..d2855a834e84c1513eef5849f9742fb6c438de9d 100644 --- a/Rendering/Volume/Testing/Cxx/CMakeLists.txt +++ b/Rendering/Volume/Testing/Cxx/CMakeLists.txt @@ -88,6 +88,7 @@ set (VolumeOpenGL2CxxTests TestGPURayCastTextureStreaming.cxx TestGPURayCastThreeComponentsAdditive.cxx TestGPURayCastThreeComponentsIndependent.cxx + TestGPURayCastTransfer2D.cxx TestGPURayCastTwoComponentsDependent.cxx TestGPURayCastTwoComponentsDependentGradient.cxx TestGPURayCastTwoComponentsGradient.cxx diff --git a/Rendering/Volume/Testing/Cxx/TestGPURayCastTransfer2D.cxx b/Rendering/Volume/Testing/Cxx/TestGPURayCastTransfer2D.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f736ccf856bd9018154a7147a3fa43b64dfe6042 --- /dev/null +++ b/Rendering/Volume/Testing/Cxx/TestGPURayCastTransfer2D.cxx @@ -0,0 +1,164 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: TestGPURayCastTransfer2D.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. + +=========================================================================*/ +/** + * Test 2D transfer function support in GPUVolumeRayCastMapper. The transfer + * function is created manually using known value/gradient histogram information + * of the test data (tooth.hdr). A filter to create these histograms will be + * added in the future. + */ + +#include "vtkCamera.h" +#include "vtkColorTransferFunction.h" +#include "vtkGPUVolumeRayCastMapper.h" +#include "vtkImageData.h" +#include "vtkInteractorStyleTrackballCamera.h" +#include "vtkNew.h" +#include "vtkNrrdReader.h" +#include "vtkPiecewiseFunction.h" +#include "vtkPointData.h" +#include "vtkRegressionTestImage.h" +#include "vtkRenderer.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkTestUtilities.h" +#include "vtkFloatArray.h" +#include "vtkVolume.h" +#include "vtkVolumeProperty.h" + + +typedef vtkSmartPointer<vtkImageData> Transfer2DPtr; +Transfer2DPtr Create2DTransfer() +{ + int bins[2] = {256, 256}; + Transfer2DPtr image = Transfer2DPtr::New(); + image->SetDimensions(bins[0], bins[1], 1); + image->AllocateScalars(VTK_FLOAT, 4); + vtkFloatArray* arr = vtkFloatArray::SafeDownCast( + image->GetPointData()->GetScalars()); + + // Initialize to zero + void* dataPtr = arr->GetVoidPointer(0); + memset(dataPtr, 0, bins[0] * bins[1] * 4 * sizeof(float)); + + // Setting RGBA [1.0, 0,0, 0.05] for a square in the histogram (known) + // containing some of the interesting edges (e.g. tooth root). + for (int j = 0; j < bins[1]; j++) + for (int i = 0; i < bins[0]; i++) + { + if (i > 130 && i < 190 && j < 50) + { + double const jFactor = 256.0 / 50; + + vtkIdType const index = bins[0] * j + i; + double const red = static_cast<double>(i) / bins[0]; + double const green = jFactor * static_cast<double>(j) / bins[1]; + double const blue = jFactor * static_cast<double>(j) / bins[1]; + double const alpha = 0.25 * jFactor * static_cast<double>(j) / bins[0]; + + double color[4] = {red, green, blue, alpha}; + arr->SetTuple(index, color); + } + } + + return image; +} + +//////////////////////////////////////////////////////////////////////////////// +int TestGPURayCastTransfer2D(int argc, char* argv[]) +{ + cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl; + + // Load data + char* fname = vtkTestUtilities::ExpandDataFileName(argc, argv, + "Data/tooth.nhdr"); + vtkNew<vtkNrrdReader> reader; + reader->SetFileName(fname); + reader->Update(); + delete[] fname; + + vtkNew<vtkVolumeProperty> volumeProperty; + volumeProperty->ShadeOn(); + volumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION); + + vtkDataArray* arr = reader->GetOutput()->GetPointData()->GetScalars(); + double range[2]; + arr->GetRange(range); + + // Prepare 1D Transfer Functions + vtkNew<vtkColorTransferFunction> ctf; + ctf->AddRGBPoint(0, 0.0, 0.0, 0.0); + ctf->AddRGBPoint(510, 0.4, 0.4, 1.0); + ctf->AddRGBPoint(640, 1.0, 1.0, 1.0); + ctf->AddRGBPoint(range[1], 0.9, 0.1, 0.1); + + vtkNew<vtkPiecewiseFunction> pf; + pf->AddPoint(0, 0.00); + pf->AddPoint(510, 0.00); + pf->AddPoint(640, 0.5); + pf->AddPoint(range[1], 0.4); + + vtkNew<vtkPiecewiseFunction> gf; + gf->AddPoint(0, 0.0); + gf->AddPoint(range[1] / 4.0, 1.0); + + volumeProperty->SetScalarOpacity(pf.GetPointer()); + volumeProperty->SetGradientOpacity(gf.GetPointer()); + volumeProperty->SetColor(ctf.GetPointer()); + + // Prepare 2D Transfer Functions + Transfer2DPtr tf2d = Create2DTransfer(); + + volumeProperty->SetTransferFunction2D(tf2d); + + // Setup rendering context + vtkNew<vtkRenderWindow> renWin; + renWin->SetSize(512, 512); + renWin->SetMultiSamples(0); + + vtkNew<vtkRenderer> ren; + renWin->AddRenderer(ren.GetPointer()); + ren->SetBackground(0.0, 0.0, 0.0); + + vtkNew<vtkGPUVolumeRayCastMapper> mapper; + mapper->SetInputConnection(reader->GetOutputPort()); + mapper->SetUseJittering(1); + + vtkNew<vtkVolume> volume; + volume->SetMapper(mapper.GetPointer()); + volume->SetProperty(volumeProperty.GetPointer()); + ren->AddVolume(volume.GetPointer()); + + ren->ResetCamera(); + ren->GetActiveCamera()->Elevation(-90.0); + ren->GetActiveCamera()->Zoom(1.4); + + // Interactor + vtkNew<vtkRenderWindowInteractor> iren; + iren->SetRenderWindow(renWin.GetPointer()); + + vtkNew<vtkInteractorStyleTrackballCamera> style; + iren->SetInteractorStyle(style.GetPointer()); + + renWin->Render(); + + int retVal = vtkTesting::Test(argc, argv, renWin.GetPointer(), 90); + if (retVal == vtkRegressionTester::DO_INTERACTOR) + { + iren->Start(); + } + + return !((retVal == vtkTesting::PASSED) || + (retVal == vtkTesting::DO_INTERACTOR)); +} diff --git a/Rendering/Volume/Testing/Data/Baseline/TestGPURayCastTransfer2D.png.md5 b/Rendering/Volume/Testing/Data/Baseline/TestGPURayCastTransfer2D.png.md5 new file mode 100644 index 0000000000000000000000000000000000000000..ca65ba6dda98da4b97d0a3d7b3f27507a1e816fb --- /dev/null +++ b/Rendering/Volume/Testing/Data/Baseline/TestGPURayCastTransfer2D.png.md5 @@ -0,0 +1 @@ +528e99e377f471a6d00515fa2af93c82 diff --git a/Rendering/VolumeOpenGL2/shaders/raycasterfs.glsl b/Rendering/VolumeOpenGL2/shaders/raycasterfs.glsl index 121a847a4215c5dc5c7c116e2c02758dc0583c29..068f763b9507cfc485d8a10987964542552b98db 100644 --- a/Rendering/VolumeOpenGL2/shaders/raycasterfs.glsl +++ b/Rendering/VolumeOpenGL2/shaders/raycasterfs.glsl @@ -66,10 +66,14 @@ uniform vec4 in_volume_bias; //VTK::CompositeMask::Dec +//VTK::GradientCache::Dec + //VTK::ComputeOpacity::Dec //VTK::ComputeGradient::Dec +//VTK::ComputeGradientOpacity1D::Dec + //VTK::ComputeLighting::Dec //VTK::ComputeColor::Dec @@ -190,6 +194,8 @@ vec4 castRay(const float zStart, const float zEnd) //VTK::CompositeMask::Impl + //VTK::PreComputeGradients::Impl + //VTK::Shading::Impl //VTK::RenderToImage::Impl diff --git a/Rendering/VolumeOpenGL2/vtkOpenGLGPUVolumeRayCastMapper.cxx b/Rendering/VolumeOpenGL2/vtkOpenGLGPUVolumeRayCastMapper.cxx index 4186f0ae75f424d9fb22e685cd3e2e2bcbe7dcd8..d009ed280053b0d536abb63121cf933d44570b2e 100644 --- a/Rendering/VolumeOpenGL2/vtkOpenGLGPUVolumeRayCastMapper.cxx +++ b/Rendering/VolumeOpenGL2/vtkOpenGLGPUVolumeRayCastMapper.cxx @@ -18,6 +18,7 @@ #include "vtkOpenGLVolumeGradientOpacityTable.h" #include "vtkOpenGLVolumeOpacityTable.h" #include "vtkOpenGLVolumeRGBTable.h" +#include "vtkOpenGLTransferFunction2D.h" #include "vtkVolumeShaderComposer.h" #include "vtkVolumeStateRAII.h" @@ -117,6 +118,7 @@ public: this->Mask1RGBTable = 0; this->Mask2RGBTable = 0; this->GradientOpacityTables = 0; + this->TransferFunctions2D = NULL; this->CurrentMask = 0; this->Dimensions[0] = this->Dimensions[1] = this->Dimensions[2] = -1; this->TextureSize[0] = this->TextureSize[1] = this->TextureSize[2] = -1; @@ -238,7 +240,8 @@ public: this->ImageSampleVAO->Delete(); this->ImageSampleVAO = NULL; } - this->DeleteTransferFunctions(); + this->DeleteTransfer1D(); + this->DeleteTransfer2D(); delete this->MaskTextures; @@ -267,9 +270,33 @@ public: template<typename T> static void ToFloat(T (&in)[4][2], float (&out)[4][2]); - void Initialize(vtkRenderer* ren, vtkVolume* vol, + ///@{ + /** + * \brief Setup and clean-up 1D and 2D transfer functions. + */ + void InitializeTransferFunction(vtkRenderer* ren, vtkVolume* vol, int noOfComponents, int independentComponents); + void UpdateTransferFunction(vtkRenderer* ren, vtkVolume* vol, + int noOfComponents, int independentComponents); + + void ActivateTransferFunction(vtkShaderProgram* prog, vtkVolumeProperty* volProp, + int numSamplers); + + void DeactivateTransferFunction(vtkVolumeProperty* volumeProperty, + int numSamplers); + + void SetupTransferFunction1D(vtkRenderer* ren, int noOfComponents, + int independentComponents); + void ReleaseGraphicsTransfer1D(vtkWindow* window); + void DeleteTransfer1D(); + + void SetupTransferFunction2D(vtkRenderer* ren, int noOfComponents, + int independentComponents); + void ReleaseGraphicsTransfer2D(vtkWindow* window); + void DeleteTransfer2D(); + ///@} + bool LoadMask(vtkRenderer* ren, vtkImageData* input, vtkImageData* maskInput, int textureExtent[6], vtkVolume* volume); @@ -277,8 +304,6 @@ public: bool LoadData(vtkRenderer* ren, vtkVolume* vol, vtkVolumeProperty* volProp, vtkImageData* input, vtkDataArray* scalars); - void DeleteTransferFunctions(); - void ComputeBounds(vtkImageData* input); // Update OpenGL volume information @@ -290,16 +315,18 @@ public: vtkVolume* vol, unsigned int component); - // Update opacity transfer function (not gradient opacity) + // Scalar opacity int UpdateOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component); - // Update gradient opacity function int UpdateGradientOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component); + void UpdateTransferFunction2D(vtkRenderer* ren, vtkVolume* vol, + unsigned int component); + // Update noise texture (used to reduce rendering artifacts // specifically banding effects) void CreateNoiseTexture(vtkRenderer* ren); @@ -452,6 +479,10 @@ public: vtkOpenGLVolumeGradientOpacityTables* GradientOpacityTables; std::map<int, std::string> GradientOpacityTablesMap; + vtkOpenGLTransferFunctions2D* TransferFunctions2D; + std::map<int, std::string> TransferFunctions2DMap; + vtkTimeStamp Transfer2DTime; + vtkTimeStamp ShaderBuildTime; vtkNew<vtkMatrix4x4> TextureToDataSetMat; @@ -585,21 +616,26 @@ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ToFloat( } //---------------------------------------------------------------------------- -void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::Initialize( - vtkRenderer* vtkNotUsed(ren), vtkVolume* vol, int - noOfComponents, int independentComponents) +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetupTransferFunction1D( + vtkRenderer* ren, int noOfComponents, int independentComponents) { - this->DeleteTransferFunctions(); + this->ReleaseGraphicsTransfer1D(ren->GetRenderWindow()); + this->DeleteTransfer1D(); - // Create RGB lookup table - if (noOfComponents > 1 && independentComponents) - { - this->RGBTables = new vtkOpenGLVolumeRGBTables(noOfComponents); - } - else - { - this->RGBTables = new vtkOpenGLVolumeRGBTables(1); - } + // Check component mode (independent or dependent) + noOfComponents = noOfComponents > 1 && independentComponents ? + noOfComponents : 1; + + // Create RGB and opacity (scalar and gradient) lookup tables. We support up + // to four components in independentComponents mode. + this->RGBTables = new vtkOpenGLVolumeRGBTables(noOfComponents); + this->OpacityTables = new vtkOpenGLVolumeOpacityTables(noOfComponents); + this->GradientOpacityTables = new vtkOpenGLVolumeGradientOpacityTables( + noOfComponents); + + this->OpacityTablesMap.clear(); + this->RGBTablesMap.clear(); + this->GradientOpacityTablesMap.clear(); if (this->Parent->MaskInput != 0 && this->Parent->MaskType == LabelMapMaskType) @@ -614,35 +650,6 @@ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::Initialize( } } - // We support up to four components - if (noOfComponents > 1 && independentComponents) - { - this->OpacityTables = new vtkOpenGLVolumeOpacityTables(noOfComponents); - } - else - { - this->OpacityTables = new vtkOpenGLVolumeOpacityTables(1); - } - - if (noOfComponents > 1 && independentComponents) - { - // Assuming that all four components has gradient opacity for now - this->GradientOpacityTables = - new vtkOpenGLVolumeGradientOpacityTables(noOfComponents); - } - else - { - if (vol->GetProperty()->HasGradientOpacity()) - { - this->GradientOpacityTables = - new vtkOpenGLVolumeGradientOpacityTables(1); - } - } - - this->OpacityTablesMap.clear(); - this->RGBTablesMap.clear(); - this->GradientOpacityTablesMap.clear(); - std::ostringstream numeric; for (int i = 0; i < noOfComponents; ++i) { @@ -669,6 +676,176 @@ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::Initialize( this->InitializationTime.Modified(); } +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::SetupTransferFunction2D( + vtkRenderer* ren, int noOfComponents, int independentComponents) +{ + this->ReleaseGraphicsTransfer2D(ren->GetRenderWindow()); + this->DeleteTransfer2D(); + + unsigned int const num = (noOfComponents > 1 && independentComponents) ? + noOfComponents : 1; + this->TransferFunctions2D = new vtkOpenGLTransferFunctions2D(num); + + std::ostringstream indexStream; + const std::string baseName = "in_transfer2D"; + for (unsigned int i = 0; i < num; i++) + { + if (i > 0) + { + indexStream << i; + } + this->TransferFunctions2DMap[0] = baseName + indexStream.str(); + indexStream.str(""); + indexStream.clear(); + } + + this->Transfer2DTime.Modified(); +} + +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::InitializeTransferFunction( + vtkRenderer* ren, vtkVolume* vol, int noOfComponents, + int independentComponents) +{ + const int transferMode = vol->GetProperty()->GetTransferFunctionMode(); + switch(transferMode) + { + case vtkVolumeProperty::TF_2D: + this->SetupTransferFunction2D(ren, noOfComponents, independentComponents); + break; + + case vtkVolumeProperty::TF_1D: + default: + this->SetupTransferFunction1D(ren, noOfComponents, independentComponents); + } +} + +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::UpdateTransferFunction( + vtkRenderer* ren, vtkVolume* vol, int noOfComponents, + int independentComponents) +{ + int const transferMode = vol->GetProperty()->GetTransferFunctionMode(); + switch(transferMode) + { + case vtkVolumeProperty::TF_1D: + if (independentComponents) + { + for (int i = 0; i < noOfComponents; ++i) + { + this->UpdateOpacityTransferFunction(ren, vol, i); + this->UpdateGradientOpacityTransferFunction(ren, vol, i); + this->UpdateColorTransferFunction(ren, vol, i); + } + } + else + { + if (noOfComponents == 2 || noOfComponents == 4) + { + this->UpdateOpacityTransferFunction(ren, vol, noOfComponents - 1); + this->UpdateGradientOpacityTransferFunction(ren, vol, + noOfComponents - 1); + this->UpdateColorTransferFunction(ren, vol, 0); + } + } + break; + + case vtkVolumeProperty::TF_2D: + if (independentComponents) + { + for (int i = 0; i < noOfComponents; ++i) + { + this->UpdateTransferFunction2D(ren, vol, i); + } + } + else + { + if (noOfComponents == 2 || noOfComponents == 4) + { + this->UpdateTransferFunction2D(ren, vol, 0); + } + } + break; + } +} + +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ActivateTransferFunction( + vtkShaderProgram* prog, vtkVolumeProperty* volumeProperty, + int numberOfSamplers) +{ + int const transferMode = volumeProperty->GetTransferFunctionMode(); + switch (transferMode) + { + case vtkVolumeProperty::TF_1D: + for (int i = 0; i < numberOfSamplers; ++i) + { + this->OpacityTables->GetTable(i)->Activate(); + prog->SetUniformi( + this->OpacityTablesMap[i].c_str(), + this->OpacityTables->GetTable(i)->GetTextureUnit()); + + if (this->Parent->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) + { + this->RGBTables->GetTable(i)->Activate(); + prog->SetUniformi( + this->RGBTablesMap[i].c_str(), + this->RGBTables->GetTable(i)->GetTextureUnit()); + } + + if (this->GradientOpacityTables) + { + this->GradientOpacityTables->GetTable(i)->Activate(); + prog->SetUniformi( + this->GradientOpacityTablesMap[i].c_str(), + this->GradientOpacityTables->GetTable(i)->GetTextureUnit()); + } + } + break; + case vtkVolumeProperty::TF_2D: + for (int i = 0; i < numberOfSamplers; ++i) + { + vtkOpenGLTransferFunction2D* table = + this->TransferFunctions2D->GetTable(i); + table->Activate(); + prog->SetUniformi(this->TransferFunctions2DMap[i].c_str(), + table->GetTextureUnit()); + } + break; + } +} + +//----------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeactivateTransferFunction( + vtkVolumeProperty* volumeProperty, int numberOfSamplers) +{ + int const transferMode = volumeProperty->GetTransferFunctionMode(); + switch(transferMode) + { + case vtkVolumeProperty::TF_1D: + for (int i = 0; i < numberOfSamplers; ++i) + { + this->OpacityTables->GetTable(i)->Deactivate(); + if (this->Parent->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) + { + this->RGBTables->GetTable(i)->Deactivate(); + } + if (this->GradientOpacityTables) + { + this->GradientOpacityTables->GetTable(i)->Deactivate(); + } + } + break; + case vtkVolumeProperty::TF_2D: + for (int i = 0; i < numberOfSamplers; ++i) + { + this->TransferFunctions2D->GetTable(i)->Deactivate(); + } + break; + } +} + //----------------------------------------------------------------------------- bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::LoadMask(vtkRenderer* ren, vtkImageData* vtkNotUsed(input), vtkImageData* maskInput, @@ -733,7 +910,37 @@ bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::LoadData(vtkRenderer* ren, } //---------------------------------------------------------------------------- -void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransferFunctions() +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ReleaseGraphicsTransfer1D( + vtkWindow* window) +{ + if (this->RGBTables) + { + this->RGBTables->ReleaseGraphicsResources(window); + } + + if (this->Mask1RGBTable) + { + this->Mask1RGBTable->ReleaseGraphicsResources(window); + } + + if (this->Mask2RGBTable) + { + this->Mask2RGBTable->ReleaseGraphicsResources(window); + } + + if (this->OpacityTables) + { + this->OpacityTables->ReleaseGraphicsResources(window); + } + + if (this->GradientOpacityTables) + { + this->GradientOpacityTables->ReleaseGraphicsResources(window); + } +} + +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransfer1D() { delete this->RGBTables; this->RGBTables = NULL; @@ -757,6 +964,23 @@ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransferFunctions() this->GradientOpacityTables = NULL; } +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ReleaseGraphicsTransfer2D( + vtkWindow* window) +{ + if (this->TransferFunctions2D) + { + this->TransferFunctions2D->ReleaseGraphicsResources(window); + } +} + +//---------------------------------------------------------------------------- +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransfer2D() +{ + delete this->TransferFunctions2D; + this->TransferFunctions2D = NULL; +} + //---------------------------------------------------------------------------- void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::ComputeBounds( vtkImageData* input) @@ -894,11 +1118,6 @@ int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal:: UpdateOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component) { - if (!vol) - { - return 1; - } - vtkVolumeProperty* volumeProperty = vol->GetProperty(); // Transfer function table index based on whether independent / dependent @@ -939,25 +1158,46 @@ int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal:: return 0; } +//------------------------------------------------------------------------------ +void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal:: + UpdateTransferFunction2D(vtkRenderer* ren, vtkVolume* vol, + unsigned int component) +{ + vtkVolumeProperty* prop = vol->GetProperty(); + int const transferMode = prop->GetTransferFunctionMode(); + if (transferMode != vtkVolumeProperty::TF_2D) + { + return; + } + + // Use the first LUT when using dependent components + unsigned int const lutIndex = prop->GetIndependentComponents() ? + component : 0; + + vtkImageData* transfer2D = prop->GetTransferFunction2D(lutIndex); +#if GL_ES_VERSION_3_0 != 1 + int const interp = prop->GetInterpolationType() == VTK_LINEAR_INTERPOLATION ? + vtkTextureObject::Linear : vtkTextureObject::Nearest; +#else + int const interp = vtkTextureObject::Nearest; +#endif + + this->TransferFunctions2D->GetTable(lutIndex)->Update(transfer2D, interp, + vtkOpenGLRenderWindow::SafeDownCast(ren->GetRenderWindow())); +} + //---------------------------------------------------------------------------- int vtkOpenGLGPUVolumeRayCastMapper::vtkInternal:: UpdateGradientOpacityTransferFunction(vtkRenderer* ren, vtkVolume* vol, unsigned int component) { - if (!vol) - { - return 1; - } - vtkVolumeProperty* volumeProperty = vol->GetProperty(); // Transfer function table index based on whether independent / dependent // components. If dependent, use the first gradient opacity transfer function unsigned int lookupTableIndex = volumeProperty->GetIndependentComponents() ? component : 0; - // TODO Currently we expect the all of the tables will - // be initialized once and if at that time, the gradient - // opacity was not enabled then it is not used later. + if (!volumeProperty->HasGradientOpacity(lookupTableIndex) || !this->GradientOpacityTables) { @@ -2584,40 +2824,11 @@ void vtkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources( } } - if(this->Impl->RGBTables) - { - this->Impl->RGBTables->ReleaseGraphicsResources(window); - delete this->Impl->RGBTables; - this->Impl->RGBTables = NULL; - } - - if(this->Impl->Mask1RGBTable) - { - this->Impl->Mask1RGBTable->ReleaseGraphicsResources(window); - this->Impl->Mask1RGBTable->Delete(); - this->Impl->Mask1RGBTable = NULL; - } - - if(this->Impl->Mask2RGBTable) - { - this->Impl->Mask2RGBTable->ReleaseGraphicsResources(window); - this->Impl->Mask2RGBTable->Delete(); - this->Impl->Mask2RGBTable = NULL; - } + this->Impl->ReleaseGraphicsTransfer1D(window); + this->Impl->DeleteTransfer1D(); - if(this->Impl->OpacityTables) - { - this->Impl->OpacityTables->ReleaseGraphicsResources(window); - delete this->Impl->OpacityTables; - this->Impl->OpacityTables = NULL; - } - - if (this->Impl->GradientOpacityTables) - { - this->Impl->GradientOpacityTables->ReleaseGraphicsResources(window); - delete this->Impl->GradientOpacityTables; - this->Impl->GradientOpacityTables = NULL; - } + this->Impl->ReleaseGraphicsTransfer2D(window); + this->Impl->DeleteTransfer2D(); this->Impl->ReleaseResourcesTime.Modified(); } @@ -2791,32 +3002,67 @@ void vtkOpenGLGPUVolumeRayCastMapper::BuildShader(vtkRenderer* ren, independentComponents), true); - // Compute methods replacements //-------------------------------------------------------------------------- - fragmentShader = vtkvolume::replace( - fragmentShader, - "//VTK::ComputeOpacity::Dec", - vtkvolume::ComputeOpacityDeclaration(ren, this, vol, noOfComponents, - independentComponents, - this->Impl->OpacityTablesMap), - true); - fragmentShader = vtkvolume::replace( fragmentShader, "//VTK::ComputeGradient::Dec", - vtkvolume::ComputeGradientDeclaration(ren, this, vol, noOfComponents, - independentComponents, - this->Impl->GradientOpacityTablesMap), + vtkvolume::ComputeGradientDeclaration(vol), true); - fragmentShader = vtkvolume::replace( - fragmentShader, - "//VTK::ComputeColor::Dec", - vtkvolume::ComputeColorDeclaration(ren, this, vol, noOfComponents, - independentComponents, - this->Impl->RGBTablesMap), - true); + switch(volumeProperty->GetTransferFunctionMode()) + { + case vtkVolumeProperty::TF_1D: + fragmentShader = vtkvolume::replace( + fragmentShader, + "//VTK::ComputeOpacity::Dec", + vtkvolume::ComputeOpacityDeclaration(ren, this, vol, noOfComponents, + independentComponents, + this->Impl->OpacityTablesMap), + true); + + fragmentShader = vtkvolume::replace( + fragmentShader, + "//VTK::ComputeGradientOpacity1D::Dec", + vtkvolume::ComputeGradientOpacity1DDecl(vol, noOfComponents, + independentComponents, + this->Impl->GradientOpacityTablesMap), + true); + + fragmentShader = vtkvolume::replace( + fragmentShader, + "//VTK::ComputeColor::Dec", + vtkvolume::ComputeColorDeclaration(ren, this, vol, noOfComponents, + independentComponents, + this->Impl->RGBTablesMap), + true); + break; + case vtkVolumeProperty::TF_2D: + fragmentShader = vtkvolume::replace(fragmentShader, + "//VTK::ComputeOpacity::Dec", + vtkvolume::ComputeOpacity2DDeclaration(ren, this, vol, noOfComponents, + independentComponents, this->Impl->TransferFunctions2DMap), + true); + + fragmentShader = vtkvolume::replace(fragmentShader, + "//VTK::ComputeColor::Dec", + vtkvolume::ComputeColor2DDeclaration(ren, this, vol, noOfComponents, + independentComponents, this->Impl->TransferFunctions2DMap), + true); + + fragmentShader = vtkvolume::replace(fragmentShader, + "//VTK::GradientCache::Dec", + vtkvolume::GradientCacheDec(ren, vol, noOfComponents, + independentComponents), + true); + + fragmentShader = vtkvolume::replace(fragmentShader, + "//VTK::PreComputeGradients::Impl", + vtkvolume::PreComputeGradientsImpl(ren, vol, noOfComponents, + independentComponents), + true); + break; + } fragmentShader = vtkvolume::replace( fragmentShader, @@ -3120,18 +3366,14 @@ void vtkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer* ren, // Update in_volume first to make sure states are current vol->Update(); - - // Get the input vtkImageData* input = this->GetTransformedInput(); - // Get the volume property (must have one) + // vtkVolume ensures the property will be valid vtkVolumeProperty* volumeProperty = vol->GetProperty(); - - // Get the camera vtkOpenGLCamera* cam = vtkOpenGLCamera::SafeDownCast(ren->GetActiveCamera()); // Check whether we have independent components or not - int independentComponents = volumeProperty->GetIndependentComponents(); + int const independentComponents = volumeProperty->GetIndependentComponents(); this->Impl->CheckPropertyKeys(vol); @@ -3159,17 +3401,14 @@ void vtkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer* ren, this->ArrayName, this->CellFlag); - // How many components are there? - int noOfComponents = scalars->GetNumberOfComponents(); - // Allocate important variables + int const noOfComponents = scalars->GetNumberOfComponents(); this->Impl->Bias.resize(noOfComponents, 0.0); if (this->Impl->NeedToInitializeResources || - (volumeProperty->GetMTime() > - this->Impl->InitializationTime.GetMTime())) + (volumeProperty->GetMTime() > this->Impl->InitializationTime.GetMTime())) { - this->Impl->Initialize(ren, vol, noOfComponents, + this->Impl->InitializeTransferFunction(ren, vol, noOfComponents, independentComponents); } @@ -3210,27 +3449,8 @@ void vtkOpenGLGPUVolumeRayCastMapper::GPURender(vtkRenderer* ren, this->ComputeReductionFactor(vol->GetAllocatedRenderTime()); this->Impl->UpdateSamplingDistance(input, ren, vol); - - // Update the transfer functions - if (independentComponents) - { - for (int i = 0; i < noOfComponents; ++i) - { - this->Impl->UpdateOpacityTransferFunction(ren, vol, i); - this->Impl->UpdateGradientOpacityTransferFunction(ren, vol, i); - this->Impl->UpdateColorTransferFunction(ren, vol, i); - } - } - else - { - if (noOfComponents == 2 || noOfComponents == 4) - { - this->Impl->UpdateOpacityTransferFunction(ren, vol, noOfComponents - 1); - this->Impl->UpdateGradientOpacityTransferFunction(ren, vol, - noOfComponents - 1); - this->Impl->UpdateColorTransferFunction(ren, vol, 0); - } - } + this->Impl->UpdateTransferFunction(ren, vol, noOfComponents, + independentComponents); // Update noise sampler texture if (this->UseJittering) @@ -3503,31 +3723,8 @@ void vtkOpenGLGPUVolumeRayCastMapper::DoGPURender(vtkRenderer* ren, // Bind textures //-------------------------------------------------------------------------- // Opacity, color, and gradient opacity samplers / textures - int numberOfSamplers = (independentComponents ? noOfComponents : 1); - - for (int i = 0; i < numberOfSamplers; ++i) - { - this->Impl->OpacityTables->GetTable(i)->Activate(); - prog->SetUniformi( - this->Impl->OpacityTablesMap[i].c_str(), - this->Impl->OpacityTables->GetTable(i)->GetTextureUnit()); - - if (this->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) - { - this->Impl->RGBTables->GetTable(i)->Activate(); - prog->SetUniformi( - this->Impl->RGBTablesMap[i].c_str(), - this->Impl->RGBTables->GetTable(i)->GetTextureUnit()); - } - - if (this->Impl->GradientOpacityTables) - { - this->Impl->GradientOpacityTables->GetTable(i)->Activate(); - prog->SetUniformi( - this->Impl->GradientOpacityTablesMap[i].c_str(), - this->Impl->GradientOpacityTables->GetTable(i)->GetTextureUnit()); - } - } + int const numberOfSamplers = (independentComponents ? noOfComponents : 1); + this->Impl->ActivateTransferFunction(prog, volumeProperty, numberOfSamplers); if (this->Impl->NoiseTextureObject) { @@ -3786,18 +3983,7 @@ void vtkOpenGLGPUVolumeRayCastMapper::DoGPURender(vtkRenderer* ren, } #endif - for (int i = 0; i < numberOfSamplers; ++i) - { - this->Impl->OpacityTables->GetTable(i)->Deactivate(); - if (this->BlendMode != vtkGPUVolumeRayCastMapper::ADDITIVE_BLEND) - { - this->Impl->RGBTables->GetTable(i)->Deactivate(); - } - if (this->Impl->GradientOpacityTables) - { - this->Impl->GradientOpacityTables->GetTable(i)->Deactivate(); - } - } + this->Impl->DeactivateTransferFunction(volumeProperty, numberOfSamplers); if (this->Impl->CurrentMask) { diff --git a/Rendering/VolumeOpenGL2/vtkOpenGLTransferFunction2D.h b/Rendering/VolumeOpenGL2/vtkOpenGLTransferFunction2D.h new file mode 100644 index 0000000000000000000000000000000000000000..379c2914147b15ed41de68864192f9c8c6c74699 --- /dev/null +++ b/Rendering/VolumeOpenGL2/vtkOpenGLTransferFunction2D.h @@ -0,0 +1,277 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkOpenGLTransferFunction2D.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. + +=========================================================================*/ + +#ifndef vtkOpenGLTransferFunction2D_h +#define vtkOpenGLTransferFunction2D_h + +#include <vtkDataArray.h> +#include <vtkImageData.h> +#include <vtkImageResize.h> +#include <vtkMath.h> +#include <vtkNew.h> +#include <vtkObjectFactory.h> +#include <vtkPointData.h> +#include <vtkTextureObject.h> +#include <vtk_glew.h> + + +/** + * \brief 2D Transfer function container. + * + * Manages the texture fetched by the fragment shader when TransferFunction2D + * mode is active. Update() assumes the vtkImageData instance used as source + * is of type VTK_FLOAT and has 4 components (vtkVolumeProperty ensures this + * is the case when the function is set). + * + * \sa vtkVolumeProperty::SetTransferFunction2D + */ +class vtkOpenGLTransferFunction2D : public vtkObject +{ +public: + + static vtkOpenGLTransferFunction2D* New(); + + //-------------------------------------------------------------------------- + void Activate() + { + if (!this->TextureObject) + { + return; + } + this->TextureObject->Activate(); + }; + + //-------------------------------------------------------------------------- + void Deactivate() + { + if (!this->TextureObject) + { + return; + } + this->TextureObject->Deactivate(); + }; + + //-------------------------------------------------------------------------- + void Update(vtkImageData* transfer2D, int interpolation, + vtkOpenGLRenderWindow* renWin) + { + if (!this->TextureObject) + { + this->TextureObject = vtkTextureObject::New(); + } + this->TextureObject->SetContext(renWin); + + // Reload texture + if (transfer2D->GetMTime() > this->BuildTime || + this->TextureObject->GetMTime() > this->BuildTime || + !this->TextureObject->GetHandle()) + { + int* dims = transfer2D->GetDimensions(); + int const width = this->GetMaximumSupportedTextureWidth(renWin, dims[0]); + int const height = this->GetMaximumSupportedTextureWidth(renWin, dims[1]); + + // Resample if there is a size restriction + void* data = transfer2D->GetPointData()->GetScalars()->GetVoidPointer(0); + if (dims[0] != width || dims[1] != height) + { + this->ResizeFilter->SetInputData(transfer2D); + this->ResizeFilter->SetResizeMethodToOutputDimensions(); + this->ResizeFilter->SetOutputDimensions(width, height, 1); + this->ResizeFilter->Update(); + data = this->ResizeFilter->GetOutput()->GetPointData()->GetScalars( + )->GetVoidPointer(0); + } + + this->TextureObject->SetWrapS(vtkTextureObject::ClampToEdge); + this->TextureObject->SetWrapT(vtkTextureObject::ClampToEdge); + this->TextureObject->SetMagnificationFilter(interpolation); + this->TextureObject->SetMinificationFilter(interpolation); + this->TextureObject->Create2DFromRaw(width, height, 4, VTK_FLOAT, + data); + this->LastInterpolation = interpolation; + this->BuildTime.Modified(); + } + + // Update filtering + if (this->LastInterpolation != interpolation) + { + this->LastInterpolation = interpolation; + this->TextureObject->SetMagnificationFilter(interpolation); + this->TextureObject->SetMinificationFilter(interpolation); + } + } + + //-------------------------------------------------------------------------- + inline int GetMaximumSupportedTextureWidth(vtkOpenGLRenderWindow* renWin, + int idealWidth) + { + if (!this->TextureObject) + { + vtkErrorMacro("vtkTextureObject not initialized!"); + return -1; + } + + // Try to match the next power of two. + idealWidth = vtkMath::NearestPowerOfTwo(idealWidth); + int const maxWidth = this->TextureObject->GetMaximumTextureSize(renWin); + if (maxWidth < 0) + { + vtkErrorMacro("Failed to query max texture size! using default 1024."); + return 256; + } + + if (maxWidth >= idealWidth) + { + idealWidth = vtkMath::Max(256, idealWidth); + return idealWidth; + } + + vtkWarningMacro("This OpenGL implementation does not support the required " + "texture size of " << idealWidth << ", falling back to maximum allowed, " + << maxWidth << "." << "This may cause an incorrect color table mapping."); + + return maxWidth; + } + + //-------------------------------------------------------------------------- + int GetTextureUnit(void) + { + if (!this->TextureObject) + { + return -1; + } + return this->TextureObject->GetTextureUnit(); + } + + //-------------------------------------------------------------------------- + void ReleaseGraphicsResources(vtkWindow *window) + { + if (this->TextureObject) + { + this->TextureObject->ReleaseGraphicsResources(window); + this->TextureObject->Delete(); + this->TextureObject = NULL; + } + } + +protected: + + //-------------------------------------------------------------------------- + vtkOpenGLTransferFunction2D() + : vtkObject() + , TextureObject(NULL) + , LastInterpolation(-1) + { + } + + //-------------------------------------------------------------------------- + ~vtkOpenGLTransferFunction2D() VTK_OVERRIDE + { + if (this->TextureObject) + { + this->TextureObject->Delete(); + this->TextureObject = NULL; + } + } + + vtkNew<vtkImageResize> ResizeFilter; + vtkTextureObject* TextureObject; + int LastInterpolation; + vtkTimeStamp BuildTime; + +private: + vtkOpenGLTransferFunction2D(const vtkOpenGLTransferFunction2D&) + VTK_DELETE_FUNCTION; + vtkOpenGLTransferFunction2D& operator=(const vtkOpenGLTransferFunction2D&) + VTK_DELETE_FUNCTION; +}; + +vtkStandardNewMacro(vtkOpenGLTransferFunction2D); + +//////////////////////////////////////////////////////////////////////////////// + +/** + * \brief Container for a set of TransferFunction2D instances. + * + * Used as a convenience class to instantiate functions for each component. + * + * \note This class will be merged with other VolumeOpenGL2/vtk*Tables to reduce + * code duplication. + * + * \sa vtkOpenGLVolumeRGBTables + */ +class vtkOpenGLTransferFunctions2D +{ +public: + //-------------------------------------------------------------------------- + vtkOpenGLTransferFunctions2D(unsigned int numberOfTables) + { + this->Tables.reserve(static_cast<size_t>(numberOfTables)); + + for (unsigned int i = 0; i < numberOfTables; i++) + { + vtkOpenGLTransferFunction2D* table = vtkOpenGLTransferFunction2D::New(); + this->Tables.push_back(table); + } + } + + //-------------------------------------------------------------------------- + ~vtkOpenGLTransferFunctions2D() + { + size_t const size = this->Tables.size(); + for (size_t i = 0; i < size; i++) + { + this->Tables[i]->Delete(); + } + } + + //-------------------------------------------------------------------------- + vtkOpenGLTransferFunction2D* GetTable(unsigned int i) + { + if (i >= this->Tables.size()) + { + return NULL; + } + return this->Tables[i]; + } + + //-------------------------------------------------------------------------- + size_t GetNumberOfTables() + { + return this->Tables.size(); + } + + //-------------------------------------------------------------------------- + void ReleaseGraphicsResources(vtkWindow *window) + { + size_t const size = this->Tables.size(); + for (size_t i = 0; i < size; ++i) + { + this->Tables[i]->ReleaseGraphicsResources(window); + } + } + +private: + vtkOpenGLTransferFunctions2D() VTK_DELETE_FUNCTION; + vtkOpenGLTransferFunctions2D(const vtkOpenGLTransferFunctions2D& other) + VTK_DELETE_FUNCTION; + vtkOpenGLTransferFunctions2D& operator=(const vtkOpenGLTransferFunctions2D& other) + VTK_DELETE_FUNCTION; + + std::vector<vtkOpenGLTransferFunction2D*> Tables; +}; + +#endif // vtkOpenGLTransferFunction2D_h +// VTK-HeaderTest-Exclude: vtkOpenGLTransferFunction2D.h diff --git a/Rendering/VolumeOpenGL2/vtkVolumeShaderComposer.h b/Rendering/VolumeOpenGL2/vtkVolumeShaderComposer.h index 6bb1d6a1ad68afdf5c34e830c49a84d11f44058f..dc5e31a790af81381abf453c07d839622a432b51 100644 --- a/Rendering/VolumeOpenGL2/vtkVolumeShaderComposer.h +++ b/Rendering/VolumeOpenGL2/vtkVolumeShaderComposer.h @@ -424,13 +424,9 @@ namespace vtkvolume } //-------------------------------------------------------------------------- - std::string ComputeGradientDeclaration(vtkRenderer* vtkNotUsed(ren), - vtkVolumeMapper* vtkNotUsed(mapper), - vtkVolume* vol, - int noOfComponents, - int independentComponents, - std::map<int, std::string> - gradientTableMap) + std::string ComputeGradientOpacity1DDecl(vtkVolume* vol, + int noOfComponents, int independentComponents, + std::map<int, std::string> gradientTableMap) { std::string shaderStr; if (vol->GetProperty()->HasGradientOpacity() && @@ -479,6 +475,13 @@ namespace vtkvolume \n }"); } + return shaderStr; + } + + //-------------------------------------------------------------------------- + std::string ComputeGradientDeclaration(vtkVolume* vol) + { + std::string shaderStr; if (vol->GetProperty()->GetShade() && !vol->GetProperty()->HasGradientOpacity()) { @@ -579,16 +582,35 @@ namespace vtkvolume ); // Shading for composite blending only - int shadeReqd = volProperty->GetShade() && - (mapper->GetBlendMode() == - vtkVolumeMapper::COMPOSITE_BLEND); + int const shadeReqd = volProperty->GetShade() && + (mapper->GetBlendMode() == vtkVolumeMapper::COMPOSITE_BLEND); + + int const transferMode = volProperty->GetTransferFunctionMode(); if (shadeReqd || volProperty->HasGradientOpacity()) { - shaderStr += std::string("\ - \n // Compute gradient function only once\ - \n vec4 gradient = computeGradient(component);" - ); + switch (transferMode) + { + case vtkVolumeProperty::TF_1D: shaderStr += std::string( + " // Compute gradient function only once\n" + " vec4 gradient = computeGradient(component);\n"); + break; + case vtkVolumeProperty::TF_2D: + shaderStr += std::string( + " // TransferFunction2D is enabled so the gradient for\n" + " // each component has already been cached\n"); + if (independentComponents && noOfComponents > 1) + { + shaderStr += + " vec4 gradient = g_gradients[component];\n"; + } + else + { + shaderStr += + " vec4 gradient = g_gradients;\n"; + } + break; + } } if (shadeReqd) @@ -771,31 +793,35 @@ namespace vtkvolume ); } - if (volProperty->HasGradientOpacity() && - (noOfComponents == 1 || !independentComponents)) + // A 2D transfer function holds scalar and gradient-magnitude combined + if (transferMode == vtkVolumeProperty::TF_1D) { - shaderStr += std::string("\ + if (volProperty->HasGradientOpacity() && + (noOfComponents == 1 || !independentComponents)) + { + shaderStr += std::string("\ + \n if (gradient.w >= 0.0)\ + \n {\ + \n color.a = color.a *\ + \n computeGradientOpacity(gradient);\ + \n }" + ); + } + else if (noOfComponents > 1 && independentComponents && + volProperty->HasGradientOpacity()) + { + shaderStr += std::string("\ \n if (gradient.w >= 0.0)\ \n {\ - \n color.a = color.a *\ - \n computeGradientOpacity(gradient);\ + \n for (int i = 0; i < in_noOfComponents; ++i)\ + \n {\ + \n color.a = color.a *\ + \n computeGradientOpacity(gradient, i) * in_componentWeight[i];\ + \n }\ \n }" - ); + ); + } } - else if (noOfComponents > 1 && independentComponents && - volProperty->HasGradientOpacity()) - { - shaderStr += std::string("\ - \n if (gradient.w >= 0.0)\ - \n {\ - \n for (int i = 0; i < in_noOfComponents; ++i)\ - \n {\ - \n color.a = color.a *\ - \n computeGradientOpacity(gradient, i) * in_componentWeight[i];\ - \n }\ - \n }" - ); - } shaderStr += std::string("\ \n finalColor.a = color.a;\ @@ -928,48 +954,186 @@ namespace vtkvolume } + shaderStr += std::string("\ + \nfloat computeOpacity(vec4 scalar, int component)\ + \n{"); + + for (int i = 0; i < noOfComponents; ++i) + { + toString << i; shaderStr += std::string("\ - \nfloat computeOpacity(vec4 scalar, int component)\ - \n {"); + \n if (component == " + toString.str() + ")"); - for (int i = 0; i < noOfComponents; ++i) - { - toString << i; - shaderStr += std::string("\ - \n if (component == " + toString.str() + ")"); + shaderStr += std::string("\ + \n {\ + \n return texture2D(" + opacityTableMap[i]); - shaderStr += std::string("\ - \n {\ - \n return texture2D(in_opacityTransferFunc"); - shaderStr += (i == 0 ? "" : toString.str()); - shaderStr += std::string(",vec2(scalar[" + toString.str() + "],0)).r;\ - \n }"); + shaderStr += std::string(",vec2(scalar[" + toString.str() + "], 0)).r;\ + \n }"); - // Reset - toString.str(""); - toString.clear(); - } + // Reset + toString.str(""); + toString.clear(); + } - shaderStr += std::string("\n }"); - return shaderStr; + shaderStr += std::string("\n}"); + return shaderStr; } else if (noOfComponents == 2 && !independentComponents) { return std::string("\ - \nuniform sampler2D in_opacityTransferFunc;\ + \nuniform sampler2D " + opacityTableMap[0] + ";\ \nfloat computeOpacity(vec4 scalar)\ - \n {\ - \n return texture2D(in_opacityTransferFunc, vec2(scalar.y, 0)).r;\ - \n }"); + \n{\ + \n return texture2D(" + opacityTableMap[0] + ", vec2(scalar.y, 0)).r;\ + \n}"); } else { return std::string("\ - \nuniform sampler2D in_opacityTransferFunc;\ + \nuniform sampler2D " + opacityTableMap[0] + ";\ \nfloat computeOpacity(vec4 scalar)\ - \n {\ - \n return texture2D(in_opacityTransferFunc, vec2(scalar.w, 0)).r;\ - \n }"); + \n{\ + \n return texture2D(" + opacityTableMap[0] + ", vec2(scalar.w, 0)).r;\ + \n}"); + } + } + + //-------------------------------------------------------------------------- + std::string ComputeColor2DDeclaration(vtkRenderer* vtkNotUsed(ren), + vtkVolumeMapper* vtkNotUsed(mapper), + vtkVolume* vtkNotUsed(vol), + int noOfComponents, + int independentComponents, + std::map<int, std::string> colorTableMap) + { + if (noOfComponents == 1) + { + // Single component + return std::string( + "vec4 computeColor(vec4 scalar, float opacity)\n" + "{\n" + " vec4 color = texture2D(" + colorTableMap[0] + ",\n" + " vec2(scalar.w, g_gradients.w));\n" + " return computeLighting(color, 0);\n" + "}\n"); + } + else if (noOfComponents > 1 && independentComponents) + { + // Multiple independent components + std::string shaderStr; + std::ostringstream toString; + shaderStr += std::string( + "vec4 computeColor(vec4 scalar, float opacity, int component)\n" + "{\n"); + + for (int i = 0; i < noOfComponents; ++i) + { + toString << i; + std::string const num = toString.str(); + shaderStr += std::string( + " if (component == " + num + ")\n" + " {\n" + " vec4 color = texture2D(" + colorTableMap[i] + ",\n" + " vec2(scalar[" + num + "], g_gradients[" + num + "].w));\n" + " return computeLighting(color, " + num + ");\n" + " }\n"); + + toString.str(""); + toString.clear(); + } + shaderStr += std::string("}\n"); + + return shaderStr; + } + else if (noOfComponents == 2 && !independentComponents) + { + // Dependent components (Luminance/ Opacity) + return std::string( + "vec4 computeColor(vec4 scalar, float opacity)\n" + "{\n" + " vec4 color = texture2D(" + colorTableMap[0] + ",\n" + " vec2(scalar.x, g_gradients.w));\n" + " return computeLighting(color, 0);\n" + "}\n"); + } + else + { + return std::string( + "vec4 computeColor(vec4 scalar, float opacity)\n" + "{\n" + " return computeLighting(vec4(scalar.xyz, opacity), 0);\n" + "}\n"); + } + } + + //-------------------------------------------------------------------------- + std::string ComputeOpacity2DDeclaration(vtkRenderer* vtkNotUsed(ren), + vtkVolumeMapper* vtkNotUsed(mapper), + vtkVolume* vtkNotUsed(vol), + int noOfComponents, + int independentComponents, + std::map<int, std::string> opacityTableMap) + { + // Declarations of <uniform sampler2D> variables are also used in + // ComputeColor2DDeclaration(), given that a single texture is fetched + // for RGBA. + if (noOfComponents > 1 && independentComponents) + { + // Multiple independent components + std::string shaderStr; + std::ostringstream toString; + + for (int i = 0; i < noOfComponents; ++i) + { + shaderStr += std::string("\nuniform sampler2D ") + + opacityTableMap[i] + std::string(";"); + + } + + shaderStr += std::string( + "float computeOpacity(vec4 scalar, int component)\n" + "{\n"); + + for (int i = 0; i < noOfComponents; ++i) + { + toString << i; + std::string const num = toString.str(); + shaderStr += std::string( + " if (component == " + num + ")\n" + " {\n" + " return texture2D(" + opacityTableMap[i]+ ",\n" + " vec2(scalar[" + num + "], g_gradients[" + num + "].w)).a;\n" + " }\n"); + + toString.str(""); + toString.clear(); + } + + shaderStr += std::string("}\n"); + return shaderStr; + } + else if (noOfComponents == 2 && !independentComponents) + { + // Dependent components (Luminance/ Opacity) + return std::string( + "uniform sampler2D " + opacityTableMap[0] + ";\n" + "float computeOpacity(vec4 scalar)\n" + "{\n" + " return texture2D(" + opacityTableMap[0] + ",\n" + " vec2(scalar.y, g_gradients.w)).a;\n" + "}\n"); + } + else + { + // Dependent compoennts (RGBA) || Single component + return std::string( + "uniform sampler2D " + opacityTableMap[0] + ";\n" + "float computeOpacity(vec4 scalar)\n" + "{\n" + " return texture2D(" + opacityTableMap[0] + ",\n" + " vec2(scalar.a, g_gradients.w)).a;\n" + "}\n"); } } @@ -1060,6 +1224,80 @@ namespace vtkvolume } } + //-------------------------------------------------------------------------- + std::string GradientCacheDec(vtkRenderer* vtkNotUsed(ren), + vtkVolume* vtkNotUsed(vol), + int noOfComponents, + int independentComponents = 0) + { + std::string shader; + if (independentComponents) + { + if (noOfComponents == 1) + { + shader = std::string("vec4 g_gradients;"); + } + else + { + // Multiple components + std::ostringstream numss; + numss << noOfComponents; + std::string const num = numss.str(); + shader = std::string( + "vec4 g_gradients[" + num + "];"); + } + } + else + { + // Dependent components (only Luminance/Opacity since RGBA does not + // use a look-up-table). + if (noOfComponents == 2) + { + shader = std::string("vec4 g_gradients;"); + } + } + + return shader; + } + + //-------------------------------------------------------------------------- + std::string PreComputeGradientsImpl(vtkRenderer* vtkNotUsed(ren), + vtkVolume* vtkNotUsed(vol), + int noOfComponents, + int independentComponents = 0) + { + std::string shader; + if (independentComponents) + { + if (noOfComponents == 1) + { + shader = std::string( + "g_gradients = computeGradient(0);\n"); + } + else + { + // Multiple components + shader = std::string( + "for (int comp = 0; comp < in_noOfComponents; comp++)\n" + "{\n" + " g_gradients[comp] = computeGradient(comp); \n" + "}\n"); + } + } + else + { + // Dependent components (only Luminance/Opacity since RGBA does not + // use a look-up-table). + if (noOfComponents == 2) + { + shader = std::string( + "g_gradients = computeGradient(0);\n"); + } + } + + return shader; + } + //-------------------------------------------------------------------------- std::string ShadingImplementation(vtkRenderer* vtkNotUsed(ren), vtkVolumeMapper* mapper, diff --git a/Testing/Data/tooth.nhdr.md5 b/Testing/Data/tooth.nhdr.md5 new file mode 100644 index 0000000000000000000000000000000000000000..06c44d1663f724e403f9ff021d569a1d1ead22d0 --- /dev/null +++ b/Testing/Data/tooth.nhdr.md5 @@ -0,0 +1 @@ +7777ca90d9ee0ce9777fadf56d76fbae diff --git a/Testing/Data/tooth.raw.md5 b/Testing/Data/tooth.raw.md5 new file mode 100644 index 0000000000000000000000000000000000000000..765db12750af8417e78f71e2ad555ccf35bbe834 --- /dev/null +++ b/Testing/Data/tooth.raw.md5 @@ -0,0 +1 @@ +3cc1b1d8882ea6b61a4e36c19042a189