Bug with depth peeling and offscreen rendering to an image

I’m currently using VTK 8.1.1.
My renderer has depth peeling enabled.
When I render translucent objects to an image using vtkPNGWriter and offscreen rendering turned on, the renderer only succeeds the first time it’s rendered to an image. The second time, nothing is rendered in the image. Other rendered in the window aren’t affected.
It works if I turn off offscreen rendering but my window flashes and I change my background, so this doesn’t look good.
It also works if I turn off depth peeling and turn it back on after the image is created.
This is on Windows 10 64-bit using VS2015.

Has anyone else had this problem?
Is this a bug or is this normal because depth peeling can’t be used?

Thanks.

I was able to reproduce the problem with the Screenshot VTK sample. Here’s the code:

#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkPolyData.h>
#include <vtkSphereSource.h>
#include <vtkWindowToImageFilter.h>
#include <vtkPNGWriter.h>
#include <vtkAxesActor.h>
#include <vtkProperty.h>
#include <vtkTransform.h>

int main(int, char *[])
{
  const double ONE_THIRD= 1.0/3;
  const double TWO_THIRDS= 2.0/3;
  const double ORIGIN_LENGTH= 40;
  const double X_AXIS_SHAFT_COLOR[3]= { 199.0/255, 25.0/255, 25.0/255 }; 
  const double Y_AXIS_SHAFT_COLOR[3]= { 25.0/255, 115.0/255, 25.0/255 }; 
  const double Z_AXIS_SHAFT_COLOR[3]= { 25.0/255, 25.0/255, 161.0/255 };

  vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New();
  sphereSource->SetCenter(0.0, 0.0, 0.0);
  sphereSource->SetRadius(5.0);
  sphereSource->Update();

  // Visualize
  vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper->SetInputConnection(sphereSource->GetOutputPort());

  vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
  actor->SetMapper(mapper);
  actor->GetProperty()->SetOpacity(0.5);

  vtkSmartPointer<vtkAxesActor> origin_axes= vtkSmartPointer<vtkAxesActor>::New();
  origin_axes->SetShaftTypeToCylinder();
  origin_axes->SetXAxisLabelText("");
  origin_axes->SetYAxisLabelText("");
  origin_axes->SetZAxisLabelText("");
  origin_axes->SetTotalLength(1, 1, 1); // This length doesn't matter because it will get recalculated later before each render.
  origin_axes->SetNormalizedShaftLength(TWO_THIRDS, TWO_THIRDS, TWO_THIRDS);
  origin_axes->SetNormalizedTipLength(ONE_THIRD, ONE_THIRD, ONE_THIRD);
  origin_axes->SetCylinderRadius(2*origin_axes->GetCylinderRadius());
  origin_axes->SetConeRadius(1.0*origin_axes->GetConeRadius());
  origin_axes->SetVisibility(false);
  origin_axes->GetXAxisShaftProperty()->SetColor(const_cast<double *>(X_AXIS_SHAFT_COLOR));
  origin_axes->GetYAxisShaftProperty()->SetColor(const_cast<double *>(Y_AXIS_SHAFT_COLOR));
  origin_axes->GetZAxisShaftProperty()->SetColor(const_cast<double *>(Z_AXIS_SHAFT_COLOR));
  origin_axes->SetVisibility(true);

  vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
  renderer->SetLayer(0);
  vtkSmartPointer<vtkRenderer> origin_axes_renderer = vtkSmartPointer<vtkRenderer>::New();
  origin_axes_renderer->SetLayer(1);
  origin_axes_renderer->InteractiveOff();
  vtkTransform *user_transform= vtkTransform::New();

  //user_transform->Scale(0.1, 0.1, 0.1);
  user_transform->Translate(-2, -2, 0);
  origin_axes->SetUserTransform(user_transform);
  user_transform->Delete();

  vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->AddRenderer(renderer);
  renderWindow->AddRenderer(origin_axes_renderer);
  renderWindow->SetAlphaBitPlanes(1); //enable usage of alpha channel
  renderWindow->SetNumberOfLayers(2);
  renderWindow->SetMultiSamples(0);

  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
  renderWindowInteractor->SetRenderWindow(renderWindow);

  renderer->AddActor(actor);
  renderer->SetBackground(1,1,1); // Background color white

  renderer->SetUseDepthPeeling(true);
  // Set depth peeling parameters
  // - Set the maximum number of rendering passes (initial value is 4):
  renderer->SetMaximumNumberOfPeels(100);
  // - Set the occlusion ratio (initial value is 0.0, exact image):
  renderer->SetOcclusionRatio(0.1);

  origin_axes_renderer->SetActiveCamera(renderer->GetActiveCamera());
  origin_axes_renderer->AddActor(origin_axes);

  renderer->ResetCamera();
  renderWindow->Render();

  // Screenshots
  for (int i= 1; i <= 2; ++i)
  {
     vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
     std::string filepath;
     bool use_depth_peeling= renderer->GetUseDepthPeeling() != 0;

     filepath= "C:\\Users\\slalonde\\Desktop\\Screenshot\\build\\Debug\\screenshot-";
     filepath+= std::to_string(i);
     filepath+= ".png";

     renderer->SetGradientBackground(true);
     renderer->SetBackground(1.0, 0.0, 0.0);
     renderer->SetBackground2(1.0, 1.0, 1.0);
     renderer->SetUseDepthPeeling(false); // Doesn't work in offscreen rendering. Actually, only works the first time in VTK 8.1.1.
     renderWindow->OffScreenRenderingOn(); // To avoid flashing.
     renderWindow->Render();

     windowToImageFilter->SetInput(renderWindow);
     //#if VTK_MAJOR_VERSION > 8 || VTK_MAJOR_VERSION == 8 && VTK_MINOR_VERSION >= 1
     //  windowToImageFilter->SetScale(2); //image quality
     //#else
     //  windowToImageFilter->SetMagnification(2); //image quality
     //#endif
     windowToImageFilter->SetInputBufferTypeToRGBA(); //also record the alpha (transparency) channel
     windowToImageFilter->ReadFrontBufferOff(); // read from the back buffer
     windowToImageFilter->Update();

     vtkSmartPointer<vtkPNGWriter> writer = vtkSmartPointer<vtkPNGWriter>::New();
     writer->SetInputConnection(windowToImageFilter->GetOutputPort());
     writer->SetFileName(filepath.c_str());
     writer->Write();

     renderer->SetUseDepthPeeling(use_depth_peeling);
     renderer->SetBackground(1.0, 1.0, 1.0);   
     renderer->SetBackground2(1.0, 1.0, 1.0);   
     renderer->SetGradientBackground(false);
     renderWindow->OffScreenRenderingOff();
     renderWindow->Render();  
  }
  
  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}

Maybe the problems have been fixed in VTK master version. Have you tried that?

I’ve downloaded 8.2 and I’m testing that now. If the new transparency handling that was mentioned in the release notes is indeed better than before, I might just switch to that and stop using depth peeling altogether. But I’ll test both. I’'ll report back my results.

1 Like

VTK 8.2 is badly broken when using offscreen rendering and transparency (at least on Windows 64-bit with VS2015). Does anybody know?

I tested with the Screenshot sample, making 4 changes (setting the opacity, turning offscreen rendering on and back off) , and it generates a slew of OpenGL errors and OpenGL is left in a bad state.

I’ve attached the modified Screenshot sample to illustrate the bug. Hopefully, someone else can corroborate. The compiler include, libreary and linker settings may need to be updated to your local settings, but the EXE is included in the 7-Zip file attachment.

Screenshot-8.2.7z (536.4 KB)