Problem with Parallel Projection after upgrading to VTK 9.1.0

After upgrading to VTK 9.1.0, we have problems when zooming in (with Parallel Projection on).
image

There are artifacts (“cross-hatching,” “Moire patterns,” “aliasing”) that are extremely bad in some cases.

Is there a setting we can change to make these go away. We did not have this problem with VTK 6.0.0.

Here is a simple example:

// This simple program demonstrates a problem we have with VTK 8.2.0 and VTK 9.1.0
// When we zoom in we get Moire patterns ("cross-hatching" or "aliasing")

// Based on https://kitware.github.io/vtk-examples/site/Cxx/Widgets/ScalarBarWidget/

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkDataSet.h>
#include <vtkDataSetMapper.h>
#include <vtkNew.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkUnstructuredGrid.h>
#include <vtkUnstructuredGridReader.h>

// Note: At the end of the list of points, there are 3 points which define a small triangle that we will zoom in on.
const char* data_string =
"# vtk DataFile Version 1.0\n\
Unstructured Grid Example\n\
ASCII\n\
DATASET UNSTRUCTURED_GRID\n\
POINTS 27 float\n\
0.0 0.0 0.0\n\
1.0 0.0 0.0\n\
2.0 0.0 0.0\n\
0.0 1.0 0.0\n\
1.0 1.0 0.0\n\
2.0 1.0 0.0\n\
0.0 0.0 1.0\n\
1.0 0.0 1.0\n\
2.0 0.0 1.0\n\
0.0 1.0 1.0\n\
1.0 1.0 1.0\n\
2.0 1.0 1.0\n\
0.0 1.0 2.0\n\
1.0 1.0 2.0\n\
2.0 1.0 2.0\n\
0.0 1.0 3.0\n\
1.0 1.0 3.0\n\
2.0 1.0 3.0\n\
0.0 1.0 4.0\n\
1.0 1.0 4.0\n\
2.0 1.0 4.0\n\
0.0 1.0 5.0\n\
1.0 1.0 5.0\n\
2.0 1.0 5.0\n\
0.95 1.0  6.0\n\
1.0  0.95 6.05\n\
1.05 1.05 5.95\n\
CELLS 12 64\n\
8 0 1 4 3 6 7 10 9\n\
8 1 2 5 4 7 8 11 10\n\
4 7 10 9 12\n\
4 8 11 10 14\n\
6 15 16 17 14 13 12\n\
6 18 15 19 16 20 17\n\
4 22 23 20 19\n\
3 21 22 18\n\
3 22 19 18\n\
3 24 25 26\n\
2 21 24\n\
1 25\n\
CELL_TYPES 12\n\
12\n12\n10\n10\n7\n6\n9\n5\n5\n5\n3\n1\n\
POINT_DATA 27\n\
SCALARS scalars float\n\
LOOKUP_TABLE default\n\
0.0\n1.0\n2.0\n3.0\n4.0\n5.0\n6.0\n7.0\n8.0\n9.0\n10.0\n11.0\n12.0\n13.0\n14.0\n15.0\n16.0\n17.0\n18.0\n19.0\n20.0\n21.0\n22.0\n23.0\n24.0\n25.0\n26.0\n";

int main(int argc, char* argv[])
{
  vtkNew<vtkUnstructuredGridReader> reader;
  reader->ReadFromInputStringOn();
  reader->SetInputString(data_string);
  reader->Update();

  vtkNew<vtkDataSetMapper> mapper;
  mapper->SetInputData(reader->GetOutput());

  vtkNew<vtkActor> actor;
  actor->SetMapper(mapper);

  vtkNew<vtkRenderer> renderer;
  renderer->AddActor(actor);
  renderer->SetBackground(0.1,0.4,0.1);

  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->AddRenderer(renderer);
  renderWindow->SetSize(1500, 1000);
  renderWindow->SetWindowName("TestMoirePatterns");

  vtkNew<vtkRenderWindowInteractor> interactor;
  interactor->SetRenderWindow(renderWindow);

  interactor->Initialize();
  renderWindow->Render();
  // The triangle of interest is near the point (1, 1, 6)
  renderer->GetActiveCamera()->SetPosition(-9.0, 11.0, 7.0);
  renderer->GetActiveCamera()->SetFocalPoint(1.0, 1.0, 6.0);
  renderer->GetActiveCamera()->SetViewUp(0.6, 0.4, -0.7);
  renderer->GetActiveCamera()->ParallelProjectionOn();
  renderWindow->Render();
  interactor->Start();

  return EXIT_SUCCESS;
}

Hello,

Maybe using multi-sampling antialiasing (MSAA) can solve it:

If you are using Qt, place this BEFORE creating the QVTKOpenGLWidget:

vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples ( 8 );
QSurfaceFormat::setDefaultFormat ( QVTKOpenGLNativeWidget::defaultFormat() );

Other GUI libraries (e.g. gtk, MFC, GLUT, etc.) may require a similiar protocol.

Enable MSAA on your vtkRenderWindow object:

m_renderWindow->SetMultiSamples( 4 );

regards,

PC

Paulo,

Thank you very much for your response. I will investigate the MSAA.

In aerospace, everyone uses Parallel Projection. I am surprised there is not more discussion of this issue. The issue affects recent versions of ParaView. Note the checkbox in the lower left. You can see the problem with any example if you zoom in on a small part. Do you have any advice for ParaView users?

Thanks,

Matthias

Thanks for the code - could you share the data it produces that you loaded into ParaView?

Looks like two surfaces are coincident, but that’s only a guess without the data on hand.

Here is the data. I call this file uGridExModified.vtk – It is a modification of a standard VTK example file.

# vtk DataFile Version 1.0
Unstructured Grid Example
ASCII

DATASET UNSTRUCTURED_GRID
POINTS 27 float
0.0 0.0 0.0
1.0 0.0 0.0
2.0 0.0 0.0
0.0 1.0 0.0
1.0 1.0 0.0
2.0 1.0 0.0
0.0 0.0 1.0
1.0 0.0 1.0
2.0 0.0 1.0
0.0 1.0 1.0
1.0 1.0 1.0
2.0 1.0 1.0
0.0 1.0 2.0
1.0 1.0 2.0
2.0 1.0 2.0
0.0 1.0 3.0
1.0 1.0 3.0
2.0 1.0 3.0
0.0 1.0 4.0
1.0 1.0 4.0
2.0 1.0 4.0
0.0 1.0 5.0
1.0 1.0 5.0
2.0 1.0 5.0
0.95 1.0  6.0
1.0  0.95 6.05
1.05 1.05 5.95

CELLS 12 64
8 0 1 4 3 6 7 10 9
8 1 2 5 4 7 8 11 10
4 7 10 9 12
4 8 11 10 14
6 15 16 17 14 13 12
6 18 15 19 16 20 17
4 22 23 20 19
3 21 22 18
3 22 19 18
3 24 25 26
2 21 24
1 25

CELL_TYPES 12
12
12
10
10
7
6
9
5
5
5
3
1


POINT_DATA 27
SCALARS scalars float
LOOKUP_TABLE default
0.0
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
10.0
11.0
12.0
13.0
14.0
15.0
16.0
17.0
18.0
19.0
20.0
21.0
22.0
23.0
24.0
25.0
26.0

VECTORS vectors float
1 0 0
1 1 0
0 2 0
1 0 0
1 1 0
0 2 0
1 0 0
1 1 0
0 2 0
1 0 0
1 1 0
0 2 0
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1
0 0 1

If you load this into ParaView, and then rotate so the lighting is like this, then zoom in on the small triangle, then roll the mouse wheel forwards about 3 times, you should see the unwanted artifacts.

If that’s the case, then we’re talking about Z-fighting and not Moiré patterns. Z-fighting or depth-fighting is a symptom of a problem in the model (coplanar surfaces). If this is indeed Z-fighting, then the OP likely needs to fix or adjust his geometry before rendering.

You don’t have to use the vtk file I posted. I included the triangle just for convenience, to show how far you have to zoom in. (For a typical aircraft, if you zoom in on the trailing edge of a wing, the artifacts are horrific). You can use the standard uGridEx.vtk (or any other file) with Parallel Projection, and zoom in. It helps if you rotate the model so the lighting is at an angle, as shown here (note the xyz triad in the lower left).
image

Here is an example of zooming in, but most any other zoom in will also work.

We need to tell wether the artifact is Z-fighting ou Moiré. Can you please enable perspective projection and zoom in where the artifact appear?

Okay, I can reproduce this. I believe the underlying cause is described in this issue: https://gitlab.kitware.com/vtk/vtk/-/issues/17760

A fix was attempted, but it appears to not work perfectly.

A workaround is to add/compute surface normals on the geometry. That uses a different rendering path where the surface normal of the polygons doesn’t need to be calculated in the shader.

Cory, thanks! I have tested your solution using ParaView, and will next test it in our product.

Paulo, thanks also for your feedback – very much appreciated. (If I switch to Perspective, the problem goes away)

Here is an example showing the problem (note in the Pipeline Browser, the visibility of GenerateSurfaceNormals1 is switched off).

Next, I switch off the visibility of the original surfaces, and switch on the visibility of GenerateSurfaceNormals1. We see the problem goes away. I used the default settings shown in the image – is that right?

1 Like

That should be fine. As long as the shader program isn’t performing the normal estimation, you shouldn’t see these issues.

Cory, thanks again for the info. You mentioned “A fix was attempted, but it appears to not work perfectly.” Do you have a reference for that fix? Has anyone else tried to fix this? Currently we use vtkPolyDataNormals as a workaround, but speed is an issue. Has anyone written a version of vtkPolyDataNormals that uses GPU rather than CPU?

Coincidentally, a fix for this issue was just merged to VTK’s master branch: https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11235. We will make sure the fix gets into ParaView 5.13.0.

1 Like

Cory, I got the latest VTK code from the master branch because @jaswantp said that the fix is not in VTK 9.3.1. Unfortunately the problem is still there. You can demonstrate this by using a vtkSphereSource to create a sphere with radius 0.00004 in a scene where there is at least one other object with size 1. I can provide a self-contained simple example if you like, but I thought it would be easier to describe it verbally. Do you have any advice for us? We cannot use vtkPolyDataNormals because it is too slow. I would think this would be a big issue for VTK because everyone in aerospace uses parallel projection, and if you zoom in on a small feature it looks terrible.

The aforementioned fix (11235) was a fix for overflow/underflow. It wasn’t a fix for precision, which seems to be the issue that is reported in this discussion.

Thanks for the info. The problem was not present in VTK 6.0.0. Is there any way to use some code or ideas from VTK 6.0.0 to address the current issue? We don’t want to go back to VTK 6.0.0 because of other performance issues, but at least it did not have this problem.

I’ve tried creating a scene with two spheres one centered at 0,0,0 with a radius of 1.0 and another at 3,0,0 with a radius of 0.00004. However, i’ve to get the smaller sphere in camera view programatically which is a bit awkward.

I understand it can be tiresome putting together a minimal example. It goes a long way in helping us exactly reprod the issue you’re facing. We could also use that code as a unit test to ensure the behaviour doesn’t regress in future versions.

Thank you very much for your response. In the attached example, there are two large cones with their points pointing at each other. You zoom in on that gap and you will see the small sphere there.

#include "vtkVersion.h"

#if VTK_MAJOR_VERSION > 6
#include "vtkAutoInit.h" 
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkInteractionStyle);
#endif

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkDataSetMapper.h>
#include <vtkNew.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkConeSource.h>
#include <vtkPolyDataNormals.h>

int main(int argc, char* argv[])
{
  // Create a sphere
  vtkNew<vtkSphereSource> sphereSource;
  sphereSource->SetThetaResolution(20);
  sphereSource->SetPhiResolution(11);
  sphereSource->SetRadius(0.00004);

  // For VTK 8.2.0 and later, problems with Moire patterns can be removed by adding normals
  // https://discourse.vtk.org/t/problem-with-parallel-projection-after-upgrading-to-vtk-9-1-0/11169
//  vtkNew<vtkPolyDataNormals> normals;
//  normals->SetInputConnection(sphereSource->GetOutputPort());
//  normals->ComputePointNormalsOn();
//  normals->ComputeCellNormalsOn();
//  normals->Update();

  const double coneCenterParam = 0.45;
  const double coneLengthParam = 0.4498;
  const double coneRadius = 0.25;

  // Create cone number 1
  vtkNew<vtkConeSource> cone1Source;
  cone1Source->SetHeight(2.0 * coneLengthParam);
  cone1Source->SetRadius(coneRadius);
  cone1Source->SetCenter(0.0, 0.0, coneCenterParam);
  cone1Source->SetDirection(0.0, 0.0, -1.0);
  cone1Source->Update();

  // Create cone number 2
  vtkNew<vtkConeSource> cone2Source;
  cone2Source->SetHeight(2.0 * coneLengthParam);
  cone2Source->SetRadius(coneRadius);
  cone2Source->SetCenter(0.0, 0.0, -coneCenterParam);
  cone2Source->SetDirection(0.0, 0.0, 1.0);
  cone2Source->Update();

  vtkNew<vtkDataSetMapper> sphereMapper;
//  sphereMapper->SetInputData(normals->GetOutput());
  sphereMapper->SetInputConnection(sphereSource->GetOutputPort());
  vtkNew<vtkDataSetMapper> cone1Mapper;
  cone1Mapper->SetInputData(cone1Source->GetOutput());
  vtkNew<vtkDataSetMapper> cone2Mapper;
  cone2Mapper->SetInputData(cone2Source->GetOutput());

  vtkNew<vtkActor> sphereActor;
  sphereActor->SetMapper(sphereMapper);
  sphereActor->GetProperty()->SetInterpolationToFlat();
  sphereActor->GetProperty()->EdgeVisibilityOn();
  vtkNew<vtkActor> cone1Actor;
  cone1Actor->SetMapper(cone1Mapper);
  cone1Actor->GetProperty()->SetInterpolationToFlat();
  cone1Actor->GetProperty()->EdgeVisibilityOn();
  vtkNew<vtkActor> cone2Actor;
  cone2Actor->SetMapper(cone2Mapper);
  cone2Actor->GetProperty()->SetInterpolationToFlat();
  cone2Actor->GetProperty()->EdgeVisibilityOn();

  vtkNew<vtkRenderer> renderer;
  renderer->GetActiveCamera()->ParallelProjectionOn();

  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->AddRenderer(renderer);
  renderWindow->SetSize(640, 480);

  vtkNew<vtkRenderWindowInteractor> interactor;
  interactor->SetRenderWindow(renderWindow);

  renderer->AddActor(sphereActor);
  renderer->AddActor(cone1Actor);
  renderer->AddActor(cone2Actor);

  renderer->ResetCameraClippingRange();

  renderWindow->SetWindowName("Demo");
  renderWindow->Render();

  int testInt = renderWindow->GetDepthBufferSize();
  std::cout << "renderWindow->GetDepthBufferSize() = " << testInt << std::endl;

  interactor->Start();

  return EXIT_SUCCESS;
}

You can also demonstrate the problem using ParaView and, for example, the standard example weldedSpheres.vtk (or any other example with some small details), with “Camera Parallel Projection” checked. I find it useful to view “Surface With Edges” as shown in these two images.

Here is ParaView 5.13.0-RC1 looking at the ParaView example bluntfin