View Issue Details Jump to Notes ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0014750VTK(No Category)public2014-05-22 08:492016-07-06 12:48
ReporterStephan Rademacher 
Assigned ToAashish Chaudhary 
PrioritynormalSeverityminorReproducibilityhave not tried
StatusclosedResolutionfixed 
PlatformOSOS Version
Product Version5.10.1 
Target VersionFixed in Version7.0.0 
Summary0014750: vtkGPUVolumeRayCastMapper offsets the near plane too much
DescriptionIn vtkOpenGLGPUVolumeRayCastMapper::ClipBoundingBox the near plane is offset by a small value. The comment says this is done in order to avoid hardware clipping of the near plane due to floating-point precision.

This offset is 0.001 times the camera plane normal. 0.001 was arbitrarily chosen (according to the comment). There is a check afterwards to make sure 0.001 is not larger than the distance between near and far plane. That is not sufficient however:

The datasets we are rendering are all very small in real world dimensions, mostly around 500 micrometers in all dimensions. This results in a very
small spacing, which leads to a small distance between near and far plane. 0.001 times the camera plane normal is a very large chunk of that distance, meaning that a large part of the dataset is clipped away.

Ironically, rendering even smaller datasets (100 micrometers for example) works fine, because then 0.001 is larger than the distance between near and far plane, and the check sets the offset to (far - near) / 1000.0.

Rendering datasets larger than 1000 micrometers works fine, because 0.001 is a small enough value. So our datasets just happen to be in the "bad" region :)

I propose changing the code from:
double offset=0.001; // some arbitrary small value.
if(offset>=distNearFar)
{
    offset=distNearFar/1000.0;
}

to:

offset = distNearFar/1000.0;
if (0.001 < offset)
{
    // 0.001 is enough to avoid floating point precision problems.
    offset = 0.001;
}

or something better.

I wrote a small program to demonstrate the effect. IronProt.vtk is rendered, but with a modified spacing, so that its real world dimensions are 500 micro meters. At startup the program uses vtkVolumeRayCastMapper, showing that CPU raycasting works fine. Pressing "9" switches to vtkGPUVolumeRayCastMapper, demonstrating the bug. Pressing "8" switches back to the CPU raycaster.

#include "vtkCamera.h"
#include "vtkColorTransferFunction.h"
#include "vtkCommand.h"
#include "vtkGPUVolumeRayCastMapper.h"
#include "vtkImageChangeInformation.h"
#include "vtkPiecewiseFunction.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkStructuredPoints.h"
#include "vtkStructuredPointsReader.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"
#include "vtkVolumeRayCastCompositeFunction.h"
#include "vtkVolumeRayCastMapper.h"

class MyCallBack: public vtkCommand
{
public:

    static MyCallBack *New()
    {
        return new MyCallBack;
    }

    virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData)
    {
        if (eventId != vtkCommand::KeyPressEvent) return;

        vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
        if (interactor == NULL) return;
        
        char* pressedKey = interactor->GetKeySym();

        if (strcmp(pressedKey, "8") == 0)
        {
            volume->SetMapper(cpuRayCaster);
            renderWindow->Render();
            std::cout << "Changed to CPU RayCasting" << std::endl;
        }
        else if (strcmp(pressedKey, "9") == 0)
        {
            volume->SetMapper(gpuRayCaster);
            renderWindow->Render();
            std::cout << "Changed to GPU RayCasting" << std::endl;
        }
    }

    vtkSmartPointer<vtkVolume> volume;
    vtkSmartPointer<vtkVolumeRayCastMapper> cpuRayCaster;
    vtkSmartPointer<vtkGPUVolumeRayCastMapper> gpuRayCaster;
    vtkSmartPointer<vtkRenderWindow> renderWindow;
};

int main()
{
    vtkSmartPointer<vtkStructuredPointsReader> pointsReader = vtkSmartPointer<vtkStructuredPointsReader>::New();
    pointsReader->SetFileName("ironProt.vtk");
    pointsReader->Update();

    int dims[3];
    pointsReader->GetOutput()->GetDimensions(dims);

    double desiredBounds = 0.0005;
    double desiredSpacing[3];
    desiredSpacing[0] = desiredBounds / (double)dims[0];
    desiredSpacing[1] = desiredBounds / (double)dims[1];
    desiredSpacing[2] = desiredBounds / (double)dims[2];

    vtkSmartPointer<vtkImageChangeInformation> imageChangeInfo = vtkSmartPointer<vtkImageChangeInformation>::New();
    imageChangeInfo->SetInputConnection(pointsReader->GetOutputPort() );
    imageChangeInfo->SetOutputSpacing(desiredSpacing);

    double sampleDistance = 1e-6;
    double scalarOpacityUnitDistance = 1e-6;

    vtkSmartPointer<vtkVolumeRayCastMapper> cpuRayCaster = vtkSmartPointer<vtkVolumeRayCastMapper>::New();
    cpuRayCaster->SetInputConnection(imageChangeInfo->GetOutputPort() );
    cpuRayCaster->SetSampleDistance(sampleDistance);
    cpuRayCaster->SetAutoAdjustSampleDistances(1);

    vtkSmartPointer<vtkVolumeRayCastCompositeFunction> compFunction = vtkSmartPointer<vtkVolumeRayCastCompositeFunction>::New();
    cpuRayCaster->SetVolumeRayCastFunction(compFunction);

    vtkSmartPointer<vtkGPUVolumeRayCastMapper> gpuRayCaster = vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
    gpuRayCaster->SetInputConnection(imageChangeInfo->GetOutputPort() );
    gpuRayCaster->SetSampleDistance(sampleDistance);
    gpuRayCaster->SetAutoAdjustSampleDistances(1);
    
    vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction = vtkSmartPointer<vtkColorTransferFunction>::New();
    colorTransferFunction->AddRGBPoint( 0.0, 0.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint( 64.0, 1.0, 0.0, 0.0);
    colorTransferFunction->AddRGBPoint(128.0, 0.0, 0.0, 1.0);
    colorTransferFunction->AddRGBPoint(192.0, 0.0, 1.0, 0.0);
    colorTransferFunction->AddRGBPoint(255.0, 0.0, 0.2, 0.0);

    vtkSmartPointer<vtkPiecewiseFunction> opacityTransferFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
    opacityTransferFunction->AddPoint(0, 0.0);
    opacityTransferFunction->AddPoint(255, 1.0);

    vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
    volumeProperty->SetColor(colorTransferFunction);
    volumeProperty->SetScalarOpacity(opacityTransferFunction);
    volumeProperty->SetScalarOpacityUnitDistance(scalarOpacityUnitDistance);
    
    vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
    volume->SetMapper(cpuRayCaster);
    volume->SetProperty(volumeProperty);
 
    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
    renderer->AddActor(volume);
    renderer->SetBackground(1.0, 1.0, 1.0);
    
    // Set the camera to a position demonstrating the effect.
    vtkSmartPointer<vtkCamera> camera = renderer->GetActiveCamera();
    double viewUp[3] = {-1.0, -1.0, -1.0};
    double position[3] = {1000.0, 1000.0, -1000.0};
    double focalPoint[3] = {0.0, 0.0, 0.0};
    camera->SetPosition(position);
    camera->SetViewUp(viewUp);
    camera->SetFocalPoint(focalPoint);
    renderer->ResetCamera();
    
    vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->SetSize(800, 600);
    renderWindow->AddRenderer(renderer);

    vtkSmartPointer<vtkRenderWindowInteractor> interactor= vtkSmartPointer<vtkRenderWindowInteractor>::New();
    interactor->SetRenderWindow(renderWindow);
    vtkSmartPointer<MyCallBack> callback = vtkSmartPointer<MyCallBack>::New();
    callback->cpuRayCaster = cpuRayCaster;
    callback->gpuRayCaster = gpuRayCaster;
    callback->renderWindow = renderWindow;
    callback->volume = volume;
    interactor->AddObserver("KeyPressEvent", callback);

    renderWindow->Render();
    interactor->Start();
}

I have attached the result of the program.
TagsNo tags attached.
ProjectTBD
Typeincorrect functionality
Attached Filesjpg file icon ClippingProblem.jpg [^] (54,611 bytes) 2014-05-22 08:49

 Relationships

  Notes
(0033408)
Berk Geveci (administrator)
2014-10-01 20:26

Aashish: please verify that this is not an issue with the new mapper.
(0033416)
Aashish Chaudhary (developer)
2014-10-02 08:34

Nice.. as this explains one of the issues we found yesterday and I was looking at the same exact code to understand the logistics. The code and the explanation here is very useful. I will push a fix.
(0033592)
Stephan Rademacher (reporter)
2014-10-06 05:25

Thanks for taking time to look at this issue!

Could you please clarify what this "new mapper" is? Is vtkGPUVolumeRaycastMapper going to be replaced/made obsolete in 6.x?

And will this fix only be for the new mapper, or also for vtkGPUVolumeRaycastMapper?
(0034573)
Sankhesh Jhaveri (manager)
2015-06-17 15:56

Hi Stephan,

The new mapper that Berk referred to is the same vtkGPUVolumeRayCastMapper but using the OpenGL2 backend.

I've pushed a fix for this issue here: https://gitlab.kitware.com/vtk/vtk/merge_requests/320 [^]
(0034736)
Stephan Rademacher (reporter)
2015-07-10 09:35

Hi Sankhesh,

thanks for the reply. So if understood correctly, the fix will be for vtkGPUVolumeRayCastMapper, but it will only be in VTK 6.x.

We are using VTK 5.10 at the moment (and upgrading to 6.x will probably not happen in the near future because of the code breaking changes in 6.x), so we will not immediately benefit from the fix. Thanks to VTK being open source, this is not too bad though. We manage our own version of 5.10 with a few bug fixes (including this one). I'd prefer an unmodified version of VTK, but this is working fine.

I read on the mailing list that you're considering maintaining VTK 6.3 for a longer period of time (meaning bug fixes are merged into it), which I think is a great idea. Late adopters like us will surely benefit.
(0036202)
Alvaro Sanchez (developer)
2016-07-06 12:48

The fix was merged into master.

[https://gitlab.kitware.com/vtk/vtk/merge_requests/320/commits [^]]

 Issue History
Date Modified Username Field Change
2014-05-22 08:49 Stephan Rademacher New Issue
2014-05-22 08:49 Stephan Rademacher File Added: ClippingProblem.jpg
2014-10-01 20:26 Berk Geveci Assigned To => Aashish Chaudhary
2014-10-01 20:26 Berk Geveci Note Added: 0033408
2014-10-02 08:34 Aashish Chaudhary Note Added: 0033416
2014-10-06 05:25 Stephan Rademacher Note Added: 0033592
2015-06-17 15:56 Sankhesh Jhaveri Note Added: 0034573
2015-07-10 09:35 Stephan Rademacher Note Added: 0034736
2016-07-06 12:48 Alvaro Sanchez Note Added: 0036202
2016-07-06 12:48 Alvaro Sanchez Status backlog => closed
2016-07-06 12:48 Alvaro Sanchez Resolution open => fixed
2016-07-06 12:48 Alvaro Sanchez Fixed in Version => 7.0.0


Copyright © 2000 - 2018 MantisBT Team