Using IndexedLookUp color in volume rendering

Hi there,

I’m working on a volume rendering task in a 3D dataset. Points in the dataset have already been clustered as individual objects, and my work is to visualize individual objects with different colors.

Since I need different colors, I tried to use the VTKLookUpTable. Unfortunately, the VTKVolumeMapper seems not provide SetLookUpTable() method so I turn to the vtkDiscretizableColorTransferFunction and try to make it work in the volumeproperty. However, the output is different with what I expect. Here’s what I did so far:

        vtkNew<vtkDiscretizableColorTransferFunction> discreteColorTransferFunction2;

        discreteColorTransferFunction2->SetColorSpaceToRGB();
        discreteColorTransferFunction2->SetScaleToLinear();
        discreteColorTransferFunction2->SetNanColor(0, 0, 0);
        discreteColorTransferFunction2->SetAboveRangeColor(0, 0, 0);
        discreteColorTransferFunction2->UseAboveRangeColorOn();
        discreteColorTransferFunction2->SetBelowRangeColor(0, 0, 0);
        discreteColorTransferFunction2->UseBelowRangeColorOn();


        for(int colorIndex = 0; colorIndex<Num; colorIndex++){
            discreteColorTransferFunction2->AddRGBPoint(colorIndex, colorIndex%256, colorIndex/256, 0);
        }

        discreteColorTransferFunction2->SetNumberOfValues(Num);
        discreteColorTransferFunction2->SetNumberOfIndexedColors(Num);
        discreteColorTransferFunction2->DiscretizeOn();
        discreteColorTransferFunction2->IndexedLookupOn();

        discreteColorTransferFunction2->Build();

        vtkNew<vtkVolumeProperty> volumeProperty2;
        volumeProperty2->SetColor(discreteColorTransferFunction2);
        volumeProperty2->SetInterpolationTypeToLinear();


        vtkNew<vtkVolume> volume2;
        volume2->SetProperty(volumeProperty2);

The output figure only has solid colors instead of the expected gradient color. Can anyone give me some advice?

Thanks,

Currently, VTK volume renderers only only support “interpolate first” compositing mode (scalar value is interpolated at the sampling position before color lookup), which are responsible for the interpolation artifacts that you show in the screenshot above. “Classify first” (lookup color based on scalar value and then interpolate) mode was available in VTK8 and earlier but it did not survive VTK rendering backend refactorings and the focus shift to GPU-accelerated rendering.

This is not a big issue though, as full-color RGBA volume rendering works well in recent VTK versions. Therefore, you can implement the “classify first” strategy by performing the color lookup yourself (generating an RGBA volume from the single-component scalar volume) and pass the RGBA volume to the renderer.

Hi Andras,

Thanks for your help! I’m actually asking about the gradient color but I figured out that was because I’m not correctly using the double type for RGB value.

Although the result works as expected, I noticed the issue you mentioned here. Some structures are incomplete, and I didn’t know what happened until I got your answer. So, thanks so much for that!

I’m using VTK 8.2.0 right now, and that’s probably why it still works with me. This is actually a MIP volume rendering, so I thought it’d be fine with the “classify-first,” but it actually doesn’t work well.

One more question about the “full-color RGBA volume rendering”. I’m not sure what you mean by “directly uses a scalar value as the color volume rendering”? Right now, I set the scalar value in a scalar dataArray under the vtkImageData and then use the LuT for the color. Is there something that I can “pass the scalar to the color” to skip the LuT?

Thank you!

Yes, you can create an RGBA volume (scalar volume with 4 components) and use SetIndependentComponents to specify that the 4 components belong together. Then coloring is based on the voxel RGBA values, and the color transfer function will be ignored.

Yeah, I just found that. Thanks so much!

Hi Andras,

Sorry to bother you again. I found some interesting things about the RGBA volume rendering and can’t figure out the reason.

Right now, I have around two modes to achieve the task. Mode 1 uses the regular color transfer function with one channel and Mode 2 uses the RGBA volume rendering without independentcomponentoff(). The task actually requires colorful rendering, but I only use (255,255,255) here for convenience.


    // Data generation
    maskgrid->SetDimensions( dims ); // dims = 500x500x50
    vtkNew<vtkInformation> info;
    maskgrid->SetScalarType(VTK_UNSIGNED_CHAR, info);
    maskgrid->SetSpacing(1.0, 1.0, 1.0);
    maskgrid->SetOrigin(0.0, 0.0, 0.0);


    vtkNew<vtkUnsignedCharArray> scalars;
    scalars->SetNumberOfComponents(1); //mode 1
    scalars->SetNumberOfComponents(4); //mode 2
    scalars->SetNumberOfTuples( dims[0] * dims[1] * dims[2] );
    scalars->Fill(0);

    // ---------Set Data---------
    // scalars.setTuple1(pointIdx, 255); //mode 1
    // scalars.setTuple4(pointIdx, 255, 255, 255, 1); //mode 2
    // .....
    // ---------Set Data---------
    maskgrid->GetPointData()->SetScalars(scalars);

    // Rendering

    vtkNew<vtkPiecewiseFunction> opacityFunc;
    opacityFunc2->AddPoint(0, 0);
    opacityFunc2->AddPoint(0.1, 1);

    vtkNew<vtkColorTransferFunction> colorTransferFunction;
    colorTransferFunction2->AddRGBPoint(0.1, 0.0, 0.0, 0.0);
    colorTransferFunction2->AddRGBPoint(255, 1.0, 1.0, 1.0);

    vtkNew<vtkVolumeProperty> volumeProperty; 
    volumeProperty2->SetColor(colorTransferFunction); // only for mode 1 with independent components on 
    volumeProperty2->SetScalarOpacity(opacityFunc); // for both mode 1&2 
    volumeProperty2-> IndependentComponentsOff(); // only for mode 2

    // An extra sphere with a radius of 100 and an outline are rendered for reference.

    //The rest of the pipeline is the same, including actor and renderer


All the above are the only difference between the two modes. I suppose the results from the two paths are mostly the same, but it actually differs a lot.

The result from mode 1 looks as I expected:


However, the result from mode 2 looks weird:


A lot of structures below the surface disappear except the one in the corner (and even that one seems incomplete). Also, if I look from bottom to top, most of the structures disappear.

This looks like an out-of-range error in the data array to me, but I don’t see any issue here. I’ve checked the number of tuples. It is dim[0] x dim[1] x dim[2], and the number of components is 4. Do you know if you have any other clues about the issue?

Thanks.

=====================================================
Update:

Something weird happened again. I switch the vtkFixedPointVolumeRayCastMapper to vtkGPUVolumeRayCastMapper and it comes up with a very light white, just like with a very low alpha value. Then I change the alpha value to 255 and everything is fixed up.

I don’t understand why the alpha value would change that, as my opacity transfer function should already reach 1. Also, I thought the vtkFixedPointVolumeRayCastMapper should have a similar performance as the vtkGPUVolumeRayCastMapper, but obviously, there’s some difference.

I’ll keep working with real RGBA value instead of all-white color.

vtkFixedPointVolumeRaycastMapper is mostly a legacy, it is just maintained but lacks many features. When I talked about volume rendering I only meant vtkGPUVolumeRaycastMapper.

The GPU volume raycast mapper interprets sampling distances differently, so you may need to adjust your transfer functions to get similar results as with the CPU mapper.

Ok, that actually makes sense. I guess I should only use the GPU in the future. Thanks so much!