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;
}