[vtkusers] Minecraft-style volume rendering

Tim Hutton tim.hutton at gmail.com
Fri Jun 8 18:29:18 EDT 2012


I've finally found a way to achieve Minecraft-style voxel rendering in
a VTK pipeline. The hard bit is moving the image point data to cell
data. One trick is that vtkMergeFilter won't pass data through unless
the target geometry is the right size, so use vtkRearrangeFields
first. Another trick is that vtkMergeFilter only passes through the
active attributes, so use vtkAssignAttribute to set them.

I'm not sure if it wouldn't be better if vtkMergeFilter was less strict?

Here's the code, for the long tail:

vtkSmartPointer<vtkImageWrapPad> pad = vtkSmartPointer<vtkImageWrapPad>::New();
pad->SetInput(image);
pad->SetOutputWholeExtent(extent[0],extent[1]+1,extent[2],extent[3]+1,extent[4],extent[5]+1);

// move the pixel values (stored in the point data) to cell data
vtkSmartPointer<vtkRearrangeFields> prearrange_fields =
vtkSmartPointer<vtkRearrangeFields>::New();
prearrange_fields->SetInput(image);
prearrange_fields->AddOperation(vtkRearrangeFields::MOVE,vtkDataSetAttributes::SCALARS,
    vtkRearrangeFields::POINT_DATA,vtkRearrangeFields::CELL_DATA);

// mark the new cell data array as the active attribute
vtkSmartPointer<vtkAssignAttribute> assign_attribute =
vtkSmartPointer<vtkAssignAttribute>::New();
assign_attribute->SetInputConnection(prearrange_fields->GetOutputPort());
assign_attribute->Assign("ImageScalars",
vtkDataSetAttributes::SCALARS, vtkAssignAttribute::CELL_DATA);

vtkSmartPointer<vtkMergeFilter> merge_datasets =
vtkSmartPointer<vtkMergeFilter>::New();
merge_datasets->SetGeometryConnection(pad->GetOutputPort());
merge_datasets->SetScalarsConnection(assign_attribute->GetOutputPort());

vtkSmartPointer<vtkThreshold> threshold = vtkSmartPointer<vtkThreshold>::New();
threshold->SetInputConnection(merge_datasets->GetOutputPort());
threshold->SetInputArrayToProcess(0, 0, 0,
    vtkDataObject::FIELD_ASSOCIATION_CELLS,
    vtkDataSetAttributes::SCALARS);
threshold->ThresholdByUpper(contour_level);

vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
transform->Translate (-.5, -.5, -.5);
vtkSmartPointer<vtkTransformFilter> transformModel =
vtkSmartPointer<vtkTransformFilter>::New();
transformModel->SetTransform(transform);
transformModel->SetInputConnection(threshold->GetOutputPort());

vtkSmartPointer<vtkGeometryFilter> geometry =
vtkSmartPointer<vtkGeometryFilter>::New();
geometry->SetInputConnection(transformModel->GetOutputPort());
mapper->SetInputConnection(geometry->GetOutputPort());


On 2 April 2012 11:22, Tim Hutton <tim.hutton at gmail.com> wrote:
> A follow-up question: Is there a way to do this as a VTK pipeline? At
> the moment it needs a manual call to pad->Update() and
> pad->GetOutput()->GetCellData()->SetScalars(image->GetPointData()->GetScalars())
> every time the image changes (full code below). This is a problem for
> my application where I've been keeping the rendering pipeline separate
> from the image-updating code, and where the user can choose how the
> pipeline is set up.
>
> // Adapted from GenerateCubesFromLabels.cxx (can use the same CMakeLists.txt)
> #include <vtkImageWrapPad.h>
> #include <vtkThreshold.h>
> #include <vtkTransformFilter.h>
> #include <vtkGeometryFilter.h>
> #include <vtkSmartPointer.h>
> #include <vtkTransform.h>
> #include <vtkImageData.h>
> #include <vtkPointData.h>
> #include <vtkCellData.h>
> #include <vtkRenderer.h>
> #include <vtkRenderWindow.h>
> #include <vtkRenderWindowInteractor.h>
> #include <vtkPolyDataMapper.h>
>
> int* vtk_at(int* origin,int x,int y,int z,int X,int Y)
> {
>    // single-component vtkImageData scalars are stored as:
> int,int,... for consecutive x, then y, then z
>    return origin + x + X*(y + Y*z);
> }
>
> int main ()
> {
>    const int X=30,Y=25,Z=20;
>    vtkSmartPointer<vtkImageData> image = vtkSmartPointer<vtkImageData>::New();
>    image->SetExtent(0,X-1,0,Y-1,0,Z-1);
>    image->SetScalarTypeToInt();
>    image->SetNumberOfScalarComponents(1);
>    image->AllocateScalars();
>
>    int* image_data = static_cast<int*>(image->GetScalarPointer());
>
>    // add 4 cubes in a simple pattern
>    for(int i=0;i<X*Y*Z;i++) image_data[i] = 0; // (clear the image)
>    *vtk_at(image_data,10,10,10,X,Y) = 1;
>    *vtk_at(image_data,15,10,10,X,Y) = 1;
>    *vtk_at(image_data,10,15,10,X,Y) = 1;
>    *vtk_at(image_data,10,10,15,X,Y) = 1;
>
>    // Pad the volume so that we can change the point data into cell data.
>    int *extent = image->GetExtent();
>    vtkSmartPointer<vtkImageWrapPad> pad =
> vtkSmartPointer<vtkImageWrapPad>::New();
>    pad->SetInput(image);
>    pad->SetOutputWholeExtent(extent[0], extent[1] + 1,
>        extent[2], extent[3] + 1,
>        extent[4], extent[5] + 1);
>    pad->Update();
>
>    // Copy the scalar point data of the volume into the scalar cell data
>    pad->GetOutput()->GetCellData()->SetScalars(image->GetPointData()->GetScalars());
>
>    vtkSmartPointer<vtkThreshold> selector =
> vtkSmartPointer<vtkThreshold>::New();
>    selector->SetInputArrayToProcess(0, 0, 0,
>        vtkDataObject::FIELD_ASSOCIATION_CELLS,
>        vtkDataSetAttributes::SCALARS);
>    selector->SetInputConnection(pad->GetOutputPort());
>    selector->ThresholdByUpper(0.5);
>    selector->Update();
>
>    // Shift the geometry by 1/2
>    vtkSmartPointer<vtkTransform> transform =
> vtkSmartPointer<vtkTransform>::New();
>    transform->Translate (-.5, -.5, -.5);
>
>    vtkSmartPointer<vtkTransformFilter> transformModel =
> vtkSmartPointer<vtkTransformFilter>::New();
>    transformModel->SetTransform(transform);
>    transformModel->SetInputConnection(selector->GetOutputPort());
>
>    vtkSmartPointer<vtkGeometryFilter> geometry =
> vtkSmartPointer<vtkGeometryFilter>::New();
>    geometry->SetInputConnection(transformModel->GetOutputPort());
>
>    vtkSmartPointer<vtkPolyDataMapper> mapper =
> vtkSmartPointer<vtkPolyDataMapper>::New();
>    mapper->SetInputConnection(geometry->GetOutputPort());
>    mapper->ScalarVisibilityOff();
>
>    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
>    actor->SetMapper(mapper);
>
>    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
>    vtkSmartPointer<vtkRenderWindow> renderWindow =
> vtkSmartPointer<vtkRenderWindow>::New();
>    renderWindow->AddRenderer(renderer);
>    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor
> = vtkSmartPointer<vtkRenderWindowInteractor>::New();
>    renderWindowInteractor->SetRenderWindow(renderWindow);
>
>    renderer->AddActor(actor);
>    renderer->SetBackground(.1, .2, .3);
>
>    renderWindow->Render();
>    renderWindowInteractor->Start(); // we'll start the animation
> after the user hits 'q'
>
>    // test whether we can change the underlying image and just update
> the pipeline
>    for(int iFrame=0;iFrame<X;iFrame++)
>    {
>        // add 4 cubes in a simple pattern that moves each frame
>        for(int i=0;i<X*Y*Z;i++) image_data[i] = 0; // (clear the image)
>        *vtk_at(image_data,(10+iFrame)%X,10,10,X,Y) = 1;
>        *vtk_at(image_data,(15+iFrame)%X,10,10,X,Y) = 1;
>        *vtk_at(image_data,(10+iFrame)%X,15,10,X,Y) = 1;
>        *vtk_at(image_data,(10+iFrame)%X,10,15,X,Y) = 1;
>        image->Modified();
>
>        // PROBLEM: need these two lines else the pipeline doesn't work
>        pad->Update();
>        pad->GetOutput()->GetCellData()->SetScalars(image->GetPointData()->GetScalars());
>
>        renderer->ResetCameraClippingRange();
>        renderWindow->Render();
>    }
>
>    renderWindowInteractor->Start();
>    return EXIT_SUCCESS;
> }
>
> On 29 March 2012 18:03, Tim Hutton <tim.hutton at gmail.com> wrote:
>> Thanks Bill,
>>
>> Yes, vtkImageData -> vtkThreshold -> vtkGeometryFilter was what I was
>> looking for.
>>
>> On 29 March 2012 17:15, Bill Lorensen <bill.lorensen at gmail.com> wrote:
>>> Tim,
>>>
>>> Take a look at this example:
>>> http://www.vtk.org/Wiki/VTK/Examples/Cxx/Medical/GenerateCubesFromLabels
>>>
>>> The output of transformModel may be what you want.
>>>
>>> Bill
>>>
>>> On Thu, Mar 29, 2012 at 8:40 AM, Tim Hutton <tim.hutton at gmail.com> wrote:
>>>> Hello,
>>>>
>>>> I've got a 3d images (vtkImageData) containing values 0 and 1. I'd
>>>> like to render this as blocky voxels - rendered with cubes like in
>>>> Minecraft.
>>>>
>>>> I've tried vtkContourFilter and vtkDiscreteMarchingCubes but these
>>>> both show angled surfaces - e.g. an octahedron for an isolated voxel.
>>>>
>>>> I tried vtkGeometryFilter but this just gives me the outer surface of
>>>> the volume. And vtkImageThreshold just changes the pixel values.
>>>>
>>>> I tried vtkImageDataGeometryFilter but this doesn't seem to support
>>>> thresholding in 3D?
>>>>
>>>> Thanks for your help,
>>>>
>>>> Tim
>>>>
>>>> --
>>>> Tim Hutton - http://www.sq3.org.uk - http://profiles.google.com/tim.hutton/
>>>> _______________________________________________
>>>> Powered by www.kitware.com
>>>>
>>>> Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html
>>>>
>>>> Please keep messages on-topic and check the VTK FAQ at: http://www.vtk.org/Wiki/VTK_FAQ
>>>>
>>>> Follow this link to subscribe/unsubscribe:
>>>> http://www.vtk.org/mailman/listinfo/vtkusers
>>>
>>>
>>>
>>> --
>>> Unpaid intern in BillsBasement at noware dot com
>>
>>
>>
>> --
>> Tim Hutton - http://www.sq3.org.uk - http://profiles.google.com/tim.hutton/
>
>
>
> --
> Tim Hutton - http://www.sq3.org.uk - http://profiles.google.com/tim.hutton/



-- 
Tim Hutton - http://www.sq3.org.uk - http://profiles.google.com/tim.hutton/



More information about the vtkusers mailing list