vtkPointGaussianMapper with Shader replacement

am using ActiViz / VTK 9.5 in a .NET desktop application for large point-cloud visualization. I am trying to implement fast class visibility toggling for LAS-style classification data.

Current data model:

  • vtkPolyData

  • vtkPointGaussianMapper

  • point arrays:

    • Classification as vtkUnsignedCharArray

    • RGB as vtkUnsignedCharArray

    • Elevation as vtkFloatArray

    • Intensity

  • display modes: Classification, RGB, Elevation, Intensity

The goal is to toggle visibility by class without rebuilding a per-point visibility mask for large datasets.

What Works

This works visually, but appears to take time on large datasets because VTK likely remaps opacity per point:

mapper.SetOpacityArray(“Classification”)

mapper.SetOpacityArrayComponent(0)

mapper.SetOpacityTableSize(256)

Dim pwf = vtkPiecewiseFunction.[New]()

pwf.ClampingOn()

For i = 0 To 255

Dim alpha As Double = If(classVisible(i), 1.0, 0.0)

pwf.AddPoint(CDbl(i), alpha)

Next

mapper.SetScalarOpacityFunction(pwf)

Dim mi = mapper.GetType().GetMethod(“SetUseOpacityArray”)

If mi IsNot Nothing Then mi.Invoke(mapper, New Object() {True})

This correctly preserves RGB/elevation/intensity coloring while using Classification as opacity. However, with large datasets, class toggling is not instant.

Attempt 1: Classification LUT Alpha

This worked only when display mode was Classification:

Dim lut = vtkLookupTable.[New]()

lut.SetNumberOfTableValues(256)

lut.SetTableRange(0.0, 255.0)

lut.Build()

For cid = 0 To 255

Dim alpha = If(classVisible(cid), 1.0, 0.0)

lut.SetTableValue(cid, r, g, b, alpha)

Next

mapper.SetScalarModeToUsePointFieldData()

mapper.SetColorModeToMapScalars()

mapper.ColorByArrayComponent(“Classification”, 0)

mapper.SetLookupTable(lut)

This does not help RGB/elevation/intensity modes because color scalar is no longer Classification.

Attempt 2: Shader Replacement

I tried mapping Classification as a vertex attribute and using a fragment shader uniform table:

mapper.MapDataArrayToVertexAttribute(

"aplcClassAttr",

"Classification",

vtkDataObject.FieldAssociations.FIELD_ASSOCIATION_POINTS,

-1)

Dim sp = actor.GetShaderProperty()

sp.AddVertexShaderReplacement(“//VTK::System::Dec”, False,

"in float aplcClassAttr;

out float aplcClassId;", False)

sp.AddVertexShaderReplacement(“//VTK::Normal::Impl”, False,

" aplcClassId = aplcClassAttr;", False)

sp.AddFragmentShaderReplacement(“//VTK::System::Dec”, False,

"in float aplcClassId;

uniform int uAplcClassVisEnabled;

uniform float uAplcClassVis[256];", False)

sp.AddFragmentShaderReplacement(“//VTK::Color::Impl”, False,

" if (uAplcClassVisEnabled != 0) {

int cid = int(clamp(floor(aplcClassId + 0.5), 0.0, 255.0));

if (uAplcClassVis\[cid\] < 0.5) { discard; }

}", False)

Uniform upload:

Dim values(255) As Single

For i = 0 To 255

values(i) = If(classVisible(i), 1.0F, 0.0F)

Next

Dim handle = GCHandle.Alloc(values, GCHandleType.Pinned)

Try

Dim fu = sp.GetFragmentCustomUniforms()

fu.SetUniformi("uAplcClassVisEnabled", 1)

fu.SetUniform1fv("uAplcClassVis", values.Length, handle.AddrOfPinnedObject())

fu.Modified()

sp.Modified()

Finally

handle.Free()

End Try

This compiles, but the render view becomes blank.

FYI @LucasGandel