VTK Wasm volume rendering error when using gradient opacity

Hi,

I’m using vtk-wasm-sdk docker to compile my own wasm bundle to use in my web app.
This is my render volume code:

void renderLoadedVolume() {

  std::cout << "=== renderLoadedVolume called ===" << std::endl;

  

  if (!g_renderWindow) {

    std::cerr << "Error: g_renderWindow is null!" << std::endl;

    return;

  }

  std::cout << "RenderWindow OK" << std::endl;

  

  if (!g_renderer) {

    std::cerr << "Error: g_renderer is null!" << std::endl;

    return;

  }

  std::cout << "Renderer OK" << std::endl;

  

  if (!g_imageData) {

    std::cerr << "Error: g_imageData is null!" << std::endl;

    return;

  }

  std::cout << "ImageData OK" << std::endl;

  // Check image data properties

  int* dims = g_imageData->GetDimensions();

  std::cout << "Dimensions: " << dims[0] << " x " << dims[1] << " x " << dims[2] << std::endl;

  

  double* spacing = g_imageData->GetSpacing();

  std::cout << "Spacing: " << spacing[0] << ", " << spacing[1] << ", " << spacing[2] << std::endl;

  

  double* origin = g_imageData->GetOrigin();

  std::cout << "Origin: " << origin[0] << ", " << origin[1] << ", " << origin[2] << std::endl;

  

  double scalarRange[2];

  g_imageData->GetScalarRange(scalarRange);

  std::cout << "Scalar range: " << scalarRange[0] << " to " << scalarRange[1] << std::endl;

  

  int scalarType = g_imageData->GetScalarType();

  std::cout << "Scalar type: " << scalarType << " (VTK_SHORT=" << VTK_SHORT << ", VTK_UNSIGNED_SHORT=" << VTK_UNSIGNED_SHORT << ")" << std::endl;

  

  vtkIdType numPoints = g_imageData->GetNumberOfPoints();

  std::cout << "Number of points: " << numPoints << std::endl;

  if (numPoints == 0) {

    std::cerr << "Error: No data points in image!" << std::endl;

    return;

  }

  // Check if dimensions are valid

  if (dims[0] <= 1 || dims[1] <= 1 || dims[2] <= 1) {

    std::cerr << "Error: Invalid dimensions - need 3D volume!" << std::endl;

    return;

  }

  std::cout << "Creating volume mapper..." << std::endl;

  g_volumeMapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();

  if (!g_volumeMapper) {

    std::cerr << "Error: Failed to create volume mapper!" << std::endl;

    return;

  }

  auto cast = vtkSmartPointer<vtkImageCast>::New();

  cast->SetInputData(g_imageData);

  cast->SetOutputScalarTypeToFloat();

  cast->Update();




  g_volumeMapper->SetInputData(cast->GetOutput());

  g_volumeMapper->SetBlendModeToComposite();

  // Disable auto-adjust so we control sample distance on interaction start/stop

  g_volumeMapper->SetAutoAdjustSampleDistances(false);

  // Compute fine/coarse sample distances from voxel spacing

  g_fineSampleDistance = std::min({spacing[0], spacing[1], spacing[2]}) * 0.5;

  g_coarseSampleDistance = g_fineSampleDistance * 4.0;

  g_volumeMapper->SetSampleDistance(g_fineSampleDistance);

  std::cout << "Volume mapper created (fine=" << g_fineSampleDistance

            << " coarse=" << g_coarseSampleDistance << ")" << std::endl;

  std::cout << "Creating color transfer function (Bone preset)..." << std::endl;

  g_colorFunc = vtkSmartPointer<vtkColorTransferFunction>::New();

  // AddRGBPoint(scalar, R, G, B, midpoint, sharpness)

  g_colorFunc->AddRGBPoint(-3024.0, 0.0, 0.0, 0.0, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(-2800.0, 0.0, 0.0, 0.0, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(90.0, 1.0, 1.0, 0.0, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(150.0, 1.0, 0.1, 0.0, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(280.0, 1.0, 0.77, 0.73, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(520.0, 1.0, 0.95, 0.6, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(700.0, 1.0, 1.0, 1.0, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(2000.0, 1.0, 1.0, 1.0, 0.5, 0.0);

  g_colorFunc->AddRGBPoint(3071.0, 0.0, 0.0, 1.0, 0.5, 0.0);

  std::cout << "Color transfer function created" << std::endl;




  std::cout << "Creating opacity transfer function (Bone preset)..." << std::endl;

  g_opacityFunc = vtkSmartPointer<vtkPiecewiseFunction>::New();

  // AddPoint(scalar, opacity, midpoint, sharpness)

  g_opacityFunc->AddPoint(-3024.0, 0.0, 0.5, 0.0);

  g_opacityFunc->AddPoint(100.0, 0.0, 0.5, 0.0);

  g_opacityFunc->AddPoint(250.0, 0.2, 0.5, 0.0);

  g_opacityFunc->AddPoint(430.0, 0.5, 0.5, 0.0);

  g_opacityFunc->AddPoint(580.0, 0.8, 0.5, 0.0);

  g_opacityFunc->AddPoint(1500.0, 1.0, 0.5, 0.0);

  g_opacityFunc->AddPoint(3071.0, 0.6, 0.5, 0.0);

  std::cout << "Opacity transfer function created" << std::endl;

  std::cout << "Creating volume property..." << std::endl;

  g_gradientOpacityFunc = vtkSmartPointer<vtkPiecewiseFunction>::New();

  g_gradientOpacityFunc->AddPoint(0, 1);

  g_gradientOpacityFunc->AddPoint(255, 1);

  std::cout << "Gradient opacity transfer function created" << std::endl;




  g_volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();

  g_volumeProperty->SetIndependentComponents(true);

  g_volumeProperty->SetColor(g_colorFunc);

  g_volumeProperty->SetScalarOpacity(g_opacityFunc);

  // Toggle gradient rendering

  g_volumeProperty->SetGradientOpacity(g_gradientOpacityFunc);

  g_volumeProperty->SetAmbient(0.1);

  g_volumeProperty->SetDiffuse(0.9);

  g_volumeProperty->SetSpecular(0.2);

  g_volumeProperty->SetSpecularPower(10);

  g_volumeProperty->SetInterpolationTypeToLinear();

  // Toggle shade rendering

  g_volumeProperty->ShadeOff();

  

  double minSpacing = std::min({spacing[0], spacing[1], spacing[2]});

  g_volumeProperty->SetScalarOpacityUnitDistance(minSpacing);

  std::cout << "Volume property created, unit distance: " << minSpacing << std::endl;

  std::cout << "Creating volume actor..." << std::endl;

  g_actor = vtkSmartPointer<vtkVolume>::New();

  g_actor->SetMapper(g_volumeMapper);

  g_actor->SetProperty(g_volumeProperty);

  std::cout << "Volume actor created" << std::endl;

  std::cout << "Adding volume to renderer..." << std::endl;

  g_renderer->AddActor(g_actor);

  std::cout << "Volume added to renderer" << std::endl;

  std::cout << "Resetting camera..." << std::endl;

  g_renderer->ResetCamera();

  // Face-on (coronal) view: camera at anterior (+Y in RAS), head up (-Z = superior)

  {

    auto cam = g_renderer->GetActiveCamera();

    double* fp = cam->GetFocalPoint();

    double dist = cam->GetDistance();

    cam->SetPosition(fp[0], fp[1] + dist, fp[2]);

    cam->SetViewUp(0, 0, -1);

  }

  std::cout << "Camera reset" << std::endl;

  std::cout << "Rendering..." << std::endl;

  g_renderWindow->Render();

  std::cout << "Render complete" << std::endl;

  std::cout << "Starting interactor..." << std::endl;

  g_interactor->SetStillUpdateRate(0);

  g_interactor->SetDesiredUpdateRate(40);




  // Observers: immediately switch sample distance on interaction start/stop

  auto startCb = vtkSmartPointer<vtkCallbackCommand>::New();

  startCb->SetCallback([](vtkObject*, unsigned long, void*, void*) {

    if (g_volumeMapper) {

      g_volumeMapper->SetSampleDistance(g_coarseSampleDistance);

      std::cout << "Interaction start -> coarse (" << g_coarseSampleDistance << ")" << std::endl;

    }

  });

  g_interactor->AddObserver(vtkCommand::StartInteractionEvent, startCb);




  auto endCb = vtkSmartPointer<vtkCallbackCommand>::New();

  endCb->SetCallback([](vtkObject*, unsigned long, void*, void*) {

    if (g_volumeMapper) {

      g_volumeMapper->SetSampleDistance(g_fineSampleDistance);

      std::cout << "Interaction end -> fine (" << g_fineSampleDistance << ")" << std::endl;

      if (g_renderWindow) g_renderWindow->Render();

    }

  });

  g_interactor->AddObserver(vtkCommand::EndInteractionEvent, endCb);




  g_interactor->Start();

  std::cout << "Interactor started" << std::endl;

  std::cout << "=== renderLoadedVolume finished ===" << std::endl;

}

The moment I set gradient opacity to volume property I get this error


I’m not sure what this error is