VTK volume rendering problem
I am using the vtk library from C ++ to generate and render some synthetic voxel data with a given display of color and transparency. An example is shown below:
As the picture shows, the data is 3D in general and it works great. However, in special cases where the data becomes 2D, the visualization windows do not show anything .
I am posting a few lines of my code that might be helpful.
imageData = vtkSmartPointer<vtkImageData>::New();
imageData->SetDimensions(X1, X2, X3); //For 2D, one of X1,X2 & X3=1
imageData->AllocateScalars(VTK_INT, 1);
int* I = new int[X1X2X3](); //int X1X2X3 = X1*X2*X3
I = static_cast<int*>(imageData->GetScalarPointer());
Note that for 2D, either X1 = 1 or X2 = 1 or X3 = 1. Any suggestions?
EDIT: I am adding an equivalent code that will demonstrate the exact problem I am facing:
main.cpp
//#include <vtkAutoInit.h> // if not using CMake to compile, necessary to use this macro
//#define vtkRenderingCore_AUTOINIT 3(vtkInteractionStyle, vtkRenderingFreeType, vtkRenderingOpenGL2)
//#define vtkRenderingVolume_AUTOINIT 1(vtkRenderingVolumeOpenGL2)
//#define vtkRenderingContext2D_AUTOINIT 1(vtkRenderingContextOpenGL2)
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartVolumeMapper.h>
#include <vtkColorTransferFunction.h>
#include <vtkVolumeProperty.h>
#include <vtkSampleFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkImageData.h>
#include <stdlib.h>
using namespace std;
int main()
{
//Declaring Variables
vtkSmartPointer<vtkImageData> imageData;
vtkSmartPointer<vtkVolumeProperty> volumeProperty;
vtkSmartPointer<vtkPiecewiseFunction> compositeOpacity;
vtkSmartPointer<vtkColorTransferFunction> color;
vtkSmartPointer<vtkVolume> volume;
vtkSmartPointer<vtkSmartVolumeMapper> mapper;
vtkSmartPointer<vtkActor> actor;
vtkSmartPointer<vtkRenderer> renderer;
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor;
vtkSmartPointer<vtkRenderWindow> renderWindow;
int* I;
int X1, X2, X3, X1X2X3;
//Assigning Values , Allocating Memory
X1 = 10;
X2 = 10;
X3 = 10;
X1X2X3 = X1*X2*X3;
I = new int[X1X2X3]();
imageData = vtkSmartPointer<vtkImageData>::New();
volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
compositeOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
color = vtkSmartPointer<vtkColorTransferFunction>::New();
volume = vtkSmartPointer<vtkVolume>::New();
mapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
actor = vtkSmartPointer<vtkActor>::New();
renderer = vtkSmartPointer<vtkRenderer>::New();
renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
volumeProperty->ShadeOff();
volumeProperty->SetInterpolationType(0);
volumeProperty->SetColor(color);
volumeProperty->SetScalarOpacity(compositeOpacity);
imageData->SetDimensions(X1, X2, X3);
imageData->AllocateScalars(VTK_INT, 1);
I = static_cast<int*>(imageData->GetScalarPointer());
renderWindow->AddRenderer(renderer);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->SetBackground(0.5, 0.5, 0.5);
renderWindow->SetSize(800, 800);
mapper->SetBlendModeToComposite();
imageData->UpdateCellGhostArrayCache();
mapper->SetRequestedRenderModeToRayCast();
mapper->SetInputData(imageData);
volume->SetMapper(mapper);
volume->SetProperty(volumeProperty);
renderer->AddViewProp(volume);
volumeProperty->ShadeOff();
//Setting Voxel Data and Its Properties
for (int i = 0; i < X1X2X3; i++)
{
I[i] = i;
compositeOpacity->AddPoint(i, 1);
color->AddRGBPoint(i, double( rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX);
}
renderer->ResetCamera();
renderWindow->Render();
renderWindowInteractor->Start();
getchar();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(EvoSim)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_USE_RELATIVE_PATHS ON)
#GRABBING VTK
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
add_executable(MAIN main.cpp)
target_link_libraries(MAIN ${VTK_LIBRARIES})
This results in an output like below (for, X1 = X2 = X3 = 10)
However, if I do X1 = 1, the output window is empty.
EDIT:
I just noticed that the number of voxels by a certain size displayed on the screen is always less than the maximum number of voxels in those dimensions. For example, if X1 = X2 = X3 = 10, the number of voxels in each dimension displayed on vtkwindow is 9. This is not what I would expect. I think this is a problem with X1 = 1 making 1-1 = 0 voxel display. Any suggestions ??
This remained unanswered. So I am adding a solution / workaround. I had to add an extra dummy layer in each imagedata dimension. [Cm. This line in the code imageData-> SetDimensions (X1 +1, X2 + 1, X3 + 1); ]. Rest is self-evident.
#pragma once
//#include <vtkAutoInit.h> // if not using CMake to compile, necessary to use this macro
//#define vtkRenderingCore_AUTOINIT 3(vtkInteractionStyle, vtkRenderingFreeType, vtkRenderingOpenGL2)
//#define vtkRenderingVolume_AUTOINIT 1(vtkRenderingVolumeOpenGL2)
//#define vtkRenderingContext2D_AUTOINIT 1(vtkRenderingContextOpenGL2)
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartVolumeMapper.h>
#include <vtkColorTransferFunction.h>
#include <vtkVolumeProperty.h>
#include <vtkSampleFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkImageData.h>
#include <stdlib.h>
#include <numeric> // std::iota
using namespace std;
int main()
{
//Declaring Variables
vtkSmartPointer<vtkImageData> imageData;
vtkSmartPointer<vtkVolumeProperty> volumeProperty;
vtkSmartPointer<vtkPiecewiseFunction> compositeOpacity;
vtkSmartPointer<vtkColorTransferFunction> color;
vtkSmartPointer<vtkVolume> volume;
vtkSmartPointer<vtkSmartVolumeMapper> mapper;
vtkSmartPointer<vtkActor> actor;
vtkSmartPointer<vtkRenderer> renderer;
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor;
vtkSmartPointer<vtkRenderWindow> renderWindow;
int X1, X2, X3, X1X2X3;
//Assigning Values , Allocating Memory
X1 = 10;
X2 = 10;
X3 = 10;
X1X2X3 = X1*X2*X3;
imageData = vtkSmartPointer<vtkImageData>::New();
imageData->SetDimensions(X1 + 1, X2 + 1, X3 + 1);
imageData->AllocateScalars(VTK_INT, 1);
volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
compositeOpacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
color = vtkSmartPointer<vtkColorTransferFunction>::New();
volume = vtkSmartPointer<vtkVolume>::New();
mapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
actor = vtkSmartPointer<vtkActor>::New();
renderer = vtkSmartPointer<vtkRenderer>::New();
renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
volumeProperty->ShadeOff();
volumeProperty->SetInterpolationType(0);
volumeProperty->SetColor(color);
volumeProperty->SetScalarOpacity(compositeOpacity);
imageData->AllocateScalars(VTK_INT, 1);
renderWindow->AddRenderer(renderer);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->SetBackground(0.5, 0.5, 0.5);
renderWindow->SetSize(800, 800);
mapper->SetBlendModeToComposite();
imageData->UpdateCellGhostArrayCache();
mapper->SetRequestedRenderModeToRayCast();
mapper->SetInputData(imageData);
volume->SetMapper(mapper);
volume->SetProperty(volumeProperty);
renderer->AddViewProp(volume);
volumeProperty->ShadeOff();
//I is supposed to store the 3D data which has to be shown as volume visualization. This 3D data is stored
//as a 1D array in which the order of iteration over 3 dimensions is x->y->z, this leads to the following
//3D to 1D index conversion farmula index1D = i + X1*j + X1*X2*k
vector<int> I(X1X2X3,0); // No need to use int* I = new int[X1X2X3] //Vectors are good
std::iota(&I[0], &I[0] + X1X2X3, 1); //Creating dummy data as 1,2,3...X1X2X3
//Setting Voxel Data and Its Properties
for (int k = 0; k < X3 + 1 ; k++)
{
for (int j = 0; j < X2 + 1 ; j++)
{
for (int i = 0; i < X1 + 1 ; i++)
{
int* voxel = static_cast<int*>(imageData->GetScalarPointer(i, j, k));
if (i==X1 || j== X2 || k==X3)
{
//Assigning zeros to dummy voxels, these will not be displayed anyways
voxel[0] = 0;
}
else
{
//copying data from I to imagedata voxel
voxel[0] = I[i + X1*j + X1*X2*k];
}
}
}
}
//Setting Up Display Properties
for (int i = 1; i < X1X2X3; i++)
{
compositeOpacity->AddPoint(i, 1);
color->AddRGBPoint(i, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX, double(rand()) / RAND_MAX);
}
renderer->ResetCamera();
renderWindow->Render();
renderWindowInteractor->Start();
getchar();
return 0;
}
Now the expected number of voxels in each dimension (10 according to the above code) are visible correctly