VTK 9.6.1 regression in edges appearance

After updating the VTK library in my C++ application (from v. 9.4.2 to .9.6.1), the rendering of some Surfaces shows some unexpected edge artifacts. All is compiled in Release mode. This image is with VTK 9.4.2:

This with VTK 9.6.1:

This does not happen on all the surfaces I still haven’t understood the reason. Here is the method with the pipeline that I use for drawing the surface (with some accessory classes/code lines)

	std::tuple<int, vtkSmartPointer<vtkActor>> Geo3DGrid::draw(int sourceId, const GridProperties& properties)
	{
		auto sourceData = m_sourceDataCollector->getItem(sourceId);

		if (sourceData.get() == nullptr)
		{
			char buff[128];
			snprintf(buff, sizeof(buff), "Source data for id: %d does not exists", sourceId);
			throw std::runtime_error{ buff };
		}

		auto* gridData = static_cast<GridSourceData*>(sourceData.get());
		auto* imageData = vtkImageData::SafeDownCast(gridData->getData()); // Do not get ownership

		vtkNew<vtkExtractVOI> extract_voi;
		extract_voi->SetInputData(imageData);
		extract_voi->SetSampleRate(properties.decimation, properties.decimation, 1);
		extract_voi->UpdateWholeExtent();

		vtkNew<vtkWarpScalar> warp;
		warp->SetInputConnection(extract_voi->GetOutputPort());

		vtkNew<vtkStructuredGridGeometryFilter> geo_filter;
		geo_filter->SetInputConnection(warp->GetOutputPort());

		vtkNew<vtkTriangleFilter> triangle_filter;
		triangle_filter->SetInputConnection(geo_filter->GetOutputPort());

		vtkNew<vtkTriangleMeshPointNormals> poly_data_norm;
		poly_data_norm->SetInputConnection(triangle_filter->GetOutputPort());

		auto* scalarRange = imageData->GetScalarRange();

		vtkNew<vtkPolyDataMapper> mapper;
		mapper->SetInputConnection(poly_data_norm->GetOutputPort());
		mapper->SetScalarRange(scalarRange);

		auto actor = vtkSmartPointer<vtkActor>::New();
		actor->SetMapper(mapper);
		actor->SetUserTransform(m_zTransform);
		actor->GetProperty()->SetOpacity(properties.opacity);
		actor->GetProperty()->SetLighting(properties.lighting);

		colorizeGrid(mapper.GetPointer(), actor.GetPointer(), properties.colorByElevation, properties.colorScaleId, properties.color);

		auto elem = std::make_shared<GridVisualElement>(sourceId, properties, extract_voi.GetPointer());
		elem->addActor(ActorIDs::Grid2D, actor);

		auto id = m_visualElementCollector->addItem(elem);
		elem->setId(id); // Store Visual element id

		return { id, actor };
	}

Thank you,

Carlo

I have found the cause of the problem. The black edge is visible only if:

auto* imageData = vtkImageData::SafeDownCast(gridData->getData());

is created passing scalars that contain some values set to double.NAN. Otherwise it behaves as before without the black edges.

So it seems that it is not supported anymore or it is a regression in the new VTK .

Carlo

The behavior of vtkGeometryFilter has changed in v9.6.1. Try toggling the RemoveGhostInterfaces i.e. in your code → geo_filter->SetRemoveGhostInterfaces(false);.

Thank you Jhaveri

However I tried it but It seems not to work, either setting SetRemoveGhostInterfaces(false) or SetRemoveGhostInterfaces(true).

In any case I’ve already changed my code and now I use a vtkUnsignedCharArray (I called it “ValidMask”) to keep a mask value for each NAN. I call a method that replace NAN to 0 and the pipeline code is filtering these values using the mask array and removing cells that are invalid (if any masked value is present):

		auto* gridData = static_cast<GridSourceData*>(sourceData.get());
		auto* imageData = vtkImageData::SafeDownCast(gridData->getData()); // Do not get ownership

		vtkNew<vtkExtractVOI> extract_voi;
		extract_voi->SetInputData(imageData);
		extract_voi->SetSampleRate(properties.decimation, properties.decimation, 1);
		extract_voi->UpdateWholeExtent();

		vtkNew<vtkWarpScalar> warp;
		warp->SetInputConnection(extract_voi->GetOutputPort());

		vtkNew<vtkGeometryFilter> geom;
		geom->SetInputConnection(warp->GetOutputPort());

		// Remove invalid geometry using the ValidMask array
		vtkNew<vtkThreshold> threshold;
		threshold->SetInputConnection(geom->GetOutputPort());
		threshold->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "ValidMask");
		threshold->SetUpperThreshold(0.5);
		threshold->SetThresholdFunction(vtkThreshold::THRESHOLD_UPPER);
		threshold->SetAllScalars(1); // do not keep cells where any of the point is invalid

		// threshold -> unstructured grid -> back to polydata
		vtkNew<vtkGeometryFilter> geom2;
		geom2->SetInputConnection(threshold->GetOutputPort());

		vtkNew<vtkTriangleFilter> triangle_filter;
		triangle_filter->SetInputConnection(geom2->GetOutputPort());

		vtkNew<vtkTriangleMeshPointNormals> poly_data_norm;
		poly_data_norm->SetInputConnection(triangle_filter->GetOutputPort());

		vtkNew<vtkPolyDataMapper> mapper;
		mapper->SetInputConnection(poly_data_norm->GetOutputPort());
		mapper->SetScalarRange(gridData->getZMin(), gridData->getZMax());
		mapper->SetScalarModeToUsePointData();

		auto actor = vtkSmartPointer<vtkActor>::New();
		actor->SetMapper(mapper);
		actor->SetUserTransform(m_zTransform);
		actor->GetProperty()->SetOpacity(properties.opacity);
		actor->GetProperty()->SetLighting(properties.lighting);

It is working, although it may be not very optimized because I am duplicating the geometry (geom2).