vtkOpenGLGPUVolumeRayCastMapper crashes when providing 2D TFs with MultiVolume
Hi all, I think there is a small issue with vtkOpenGLGPUVolumeRayCastMapper. When providing a MultiVolume with 2D TransferFunctions, the mapper doesn't detect any issue, but the built shader seems to have some. Below is a code to reproduce a crash (which is very inspired by TestGPURayCastTransfer2D but adding MultiVolume).
#include <vtk-9.1/vtkNew.h>
#include <vtk-9.1/vtkMultiVolume.h>
#include <vtk-9.1/vtkGPUVolumeRayCastMapper.h>
#include <vtk-9.1/vtkRenderer.h>
#include <vtk-9.1/vtkRenderWindow.h>
#include <vtk-9.1/vtkInteractorStyleTrackballCamera.h>
#include <vtk-9.1/vtkRenderWindowInteractor.h>
#include <vtk-9.1/vtkDataArray.h>
#include <vtk-9.1/vtkFloatArray.h>
#include <vtk-9.1/vtkVolumeProperty.h>
#include <vtk-9.1/vtkPiecewiseFunction.h>
#include <vtk-9.1/vtkColorTransferFunction.h>
#include <vtk-9.1/vtkPointData.h>
#include <vtk-9.1/vtkPolyDataMapper.h>
#include <vtk-9.1/vtkPolyData.h>
#include <vtk-9.1/vtkActor.h>
#include <vtk-9.1/vtkPoints.h>
#include <vtk-9.1/vtkCellArray.h>
#include <vtk-9.1/vtkVertex.h>
#include <vtk-9.1/vtkActor.h>
#include <vtk-9.1/vtkLight.h>
#include <vtk-9.1/vtkProperty.h>
#include <vtk-9.1/vtkUniformGrid.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.35 * jFactor * static_cast<double>(j) / bins[0];
double color[4] = { red, green, blue, alpha };
arr->SetTuple(index, color);
}
}
return image;
}
int main(int argc, char *argv[])
{
//
double origin1[3] = {0, 0, 0};
double origin2[3] = {3, 0, 0};
double spacing[3] = {0.1, 0.1, 0.1};
int dimension[3] = {50, 50, 50};
//
int number_tuples = dimension[0] * dimension[1] * dimension[2];
vtkNew<vtkUniformGrid> grid1;
vtkNew<vtkUniformGrid> grid2;
grid1->SetDimensions(dimension);
grid1->SetSpacing(spacing);
grid1->SetOrigin(origin1);
grid2->SetDimensions(dimension);
grid2->SetSpacing(spacing);
grid2->SetOrigin(origin2);
vtkNew<vtkFloatArray> scalar1;
scalar1->SetNumberOfComponents(1);
scalar1->SetNumberOfTuples(number_tuples);
vtkNew<vtkFloatArray> scalar2;
scalar2->SetNumberOfComponents(1);
scalar2->SetNumberOfTuples(number_tuples);
for(int x = 0; x < number_tuples; x++)
{
scalar1->SetTuple1(x, 0);
scalar2->SetTuple1(x, 0);
}
for(int i=0; i<dimension[0]; i++)
{
for(int j=0; j<dimension[1]; j++)
{
for(int k=0; k<dimension[2]; k++)
{
int array_idx = k * dimension[0] * dimension[1] + j * dimension[0] + i;
scalar1->SetTuple1(array_idx, 1.0);
scalar2->SetTuple1(array_idx, 2.0);
}
}
}
grid1->GetPointData()->SetScalars(scalar1);
grid2->GetPointData()->SetScalars(scalar2);
// now the color function and all
Transfer2DPtr tf2d = Create2DTransfer();
vtkNew<vtkVolume> vol1;
vol1->GetProperty()->SetTransferFunction2D(tf2d);
vol1->GetProperty()->SetTransferFunctionModeTo2D();
vol1->GetProperty()->SetInterpolationTypeToLinear();
vol1->GetProperty()->ShadeOn();
vol1->GetProperty()->SetDiffuse(1.0);
vol1->GetProperty()->SetSpecular(1.0);
vtkNew<vtkVolume> vol2;
vol2->GetProperty()->SetTransferFunction2D(tf2d);
vol2->GetProperty()->SetTransferFunctionModeTo2D();
vol2->GetProperty()->SetInterpolationTypeToLinear();
vol2->GetProperty()->ShadeOn();
vol2->GetProperty()->SetDiffuse(1.0);
vol2->GetProperty()->SetSpecular(1.0);
// now mapper and volumes etc
vtkNew<vtkMultiVolume> multivolume;
vtkNew<vtkGPUVolumeRayCastMapper> mapper;
mapper->SetUseJittering(true);
multivolume->SetMapper(mapper);
mapper->SetInputDataObject(0, grid1);
multivolume->SetVolume(vol1, 0);
mapper->SetInputDataObject(2, grid2);
multivolume->SetVolume(vol2, 2);
// now rendering pipeline
vtkNew<vtkRenderWindow> renWin;
renWin->SetSize(800, 800);
renWin->SetMultiSamples(0);
vtkNew<vtkRenderer> ren;
renWin->AddRenderer(ren);
ren->SetBackground(0.3, 0.3, 0.3);
ren->SetTwoSidedLighting(false);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
vtkNew<vtkInteractorStyleTrackballCamera> style;
iren->SetInteractorStyle(style);
ren->AddVolume(multivolume);
renWin->Render();
iren->Start();
return 0;
}
When running this code, I get a crash of VTK due to compilation errors of the shader (there is a variable which is called but not defined, but I'm not sure it would be easily possible to fix it nicely). Am I doing something wrong ? Otherwise, a simple fix would be to add a check and a warning in vtkGPUVolumeRayCastMapper's ValidateInput, and to abort the rendering in case of 2D TF and multivolumes. Thanks !
Fix : !9116 (merged)