vtkOpenGLRayCastImageDisplayHelper::RenderTextureInternal does not call glEnable(gl_BLEND)
This issue was created automatically from an original Mantis Issue. Further discussion may take place here.
Hello,
I think I found a bug in vtkOpenGLRayCastImageDisplayHelper::RenderTextureInternal. It uses OpenGL blending, but never calls glEnable(GL_BLEND)
This is how I found out about it:
I noticed a problem with volume rendering on windows machines that have the Mobile Intel(R) 4 Series chipset (so no dedicated graphics card).
I use vtkSmartVolumeMapper to allow switching between CPU and GPU raycasting on the fly. This usually works very well, but with the grapics chip mentioned above I get a black background after switching from GPU to CPU. This switch from GPU to CPU is important, if I only use CPU everything is fine. I attached an image demonstrating the effect:
The first rendering is done using CPU (demonstrating that CPU alone works fine), the second is after the switch to GPU (which works fine), and the third is after the switch back to CPU. This is where an unexpected black background appears.
This effect does not happen on any NVIDIA cards I tested. I didn't test ATI because I don't have one. I investigated the issue and discovered that the problem seems to be in vtkOpenGLRayCastImageDisplayHelper.cxx in the function RenderTextureInternal. This is the function the CPU raycaster uses to display the raycasted texture. There the blending function is set up (using glBlendFunc), but blending is never enabled (so glEnable(GL_BLEND) is not called).
This is no problem when only using CPU, because the vtkOpenGLRenderWindow enables blending in its OpenGLInitState method. But the GPU raycaster enables blending, renders, and then disables it again. So when switching back to CPU, blending is disabled, and vtkOpenGLRayCastImageDisplayHelper::RenderTextureInternal never enables it again, resulting in the black background.
I added a glEnable(GL_BLEND) in vtkOpenGLRayCastImageDisplayHelper::RenderTextureInternal right before glBlendFunc is called, and the problem goes away.
I'm not sure why NVIDIA cards (the ones I tested) don't show the black background, but I think this is a bug in VTK. Configuring OpenGL blending without enabling it seems wrong to me.
The problematic chipset is mostly in notebooks, so you might not be able to reproduce it on a desktop machine. Well, maybe the problem exists for all Intel drivers, I don't know. If you have a notebook with this chipset, it is important to disable any additional dedicated graphics card you might have, otherwise the effect might not occur. This is a small program that can be used to demonstrate the effect:
#include "vtkColorTransferFunction.h" #include "vtkCommand.h" #include "vtkPiecewiseFunction.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkSmartPointer.h" #include "vtkSmartVolumeMapper.h" #include "vtkStructuredPointsReader.h" #include "vtkVolume.h" #include "vtkVolumeProperty.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)
{
if (smartMapper->GetRequestedRenderMode() == vtkSmartVolumeMapper::GPURenderMode)
{
smartMapper->SetRequestedRenderMode(vtkSmartVolumeMapper::RayCastRenderMode);
std::cout << "Changed to CPU RayCasting" << std::endl;
}
else if (smartMapper->GetRequestedRenderMode() == vtkSmartVolumeMapper::RayCastRenderMode)
{
smartMapper->SetRequestedRenderMode(vtkSmartVolumeMapper::GPURenderMode);
std::cout << "Changed to GPU RayCasting" << std::endl;
}
renderWindow->Render();
}
}
vtkSmartPointer<vtkSmartVolumeMapper> smartMapper;
vtkSmartPointer<vtkRenderWindow> renderWindow;
};
int main() { vtkSmartPointer pointsReader = vtkSmartPointer::New(); pointsReader->SetFileName("ironProt.vtk");
vtkSmartPointer<vtkSmartVolumeMapper> smartVolumeMapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
smartVolumeMapper->SetInputConnection(pointsReader->GetOutputPort() );
smartVolumeMapper->SetRequestedRenderMode(vtkSmartVolumeMapper::RayCastRenderMode);
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);
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
volume->SetMapper(smartVolumeMapper);
volume->SetProperty(volumeProperty);
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddActor(volume);
renderer->SetBackground(1.0, 1.0, 1.0);
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->smartMapper = smartVolumeMapper;
callback->renderWindow = renderWindow;
interactor->AddObserver("KeyPressEvent", callback);
renderWindow->Render();
interactor->Start();
}
This program renders the ironProt, initially using CPU raycasting. Pressing any key will toggle betweeln CPU raycasting and GPU raycasting.
If I am doing something wrong and this is not a bug, I'm sorry.
Kind Regards, Stephan Rademacher