rendering problems when volume and surface source are added in the same scene

The yellow part is from the volume source, the black axes actor are AxesActor set with transparent to 0.01, still, it completely blocks the volume part.
Also you can see the white line within the black axes so the transparence works for surface rendering objects, but didn’t work well with volume objects.

Also, normally the white line is blocked by volume object, which is correct. But it is not blocked anymore when another surface object overlaps, as you can see the white line within the black axes.

image

Can you try enabling depth peeling on your vtkRenderer object using UseDepthPeelingOn(), and also UseDepthPeelingForVolumesOn()?

There is no depth peeling in vtk.js.

What happens if you call setForceTranslucent() on AxesActor ?

Haven’t looked at the translucency support in vtk-js but its possible that the mapper doesn’t support translucent polygonal data with volumes. The idea is to mix the two actors in the translucent pass based on the volume fragment’s final opacity value.

actor.setForceTranslucent() doesn’t work either.

It seems when rendered with translucency the volume behind is somehow completely lost.

I’m not seeing this. Can you replace the AxisActor with a simple sphere or cone and see if you have the same problem? I tried a sphere both in the renderer and in a layer above and in both cases it blended. It will not intermix them, but it should blend them one in front of the other based on how you set it up.

I tried with a cone and its the same issue. Do you have an example showing the correct rendering? you need to have a volume data and a surface data in a scene and set transparency on the surface data. Thanks.

Here is the example I’ve been testing that works for me

/* eslint-disable import/prefer-default-export */
/* eslint-disable import/no-extraneous-dependencies */

import 'vtk.js/Sources/favicon';

// Load the rendering pieces we want to use (for both WebGL and WebGPU)
import 'vtk.js/Sources/Rendering/Profiles/All';

import 'vtk.js/Sources/Rendering/Misc/RenderingAPIs';
import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction';
import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader';
import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction';
import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource';
import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';
import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow';
import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor';
import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer';
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
import vtkInteractorStyleTrackballCamera from 'vtk.js/Sources/Interaction/Style/InteractorStyleTrackballCamera';

// Force the loading of HttpDataAccessHelper to support gzip decompression
import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper';

// Create some control UI
const container = document.querySelector('body');
const renderWindowContainer = document.createElement('div');
container.appendChild(renderWindowContainer);

// create what we will view
const renderWindow = vtkRenderWindow.newInstance();
const renderer = vtkRenderer.newInstance();
renderWindow.addRenderer(renderer);
renderer.setBackground(0.32, 0.3, 0.43);

const volume = vtkVolume.newInstance();

const vmapper = vtkVolumeMapper.newInstance();
vmapper.setSampleDistance(0.7);
volume.setMapper(vmapper);

const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true });
// create color and opacity transfer functions
const ctfun = vtkColorTransferFunction.newInstance();
ctfun.addRGBPoint(0, 85 / 255.0, 0, 0);
ctfun.addRGBPoint(95, 1.0, 1.0, 1.0);
ctfun.addRGBPoint(225, 0.66, 0.66, 0.5);
ctfun.addRGBPoint(255, 0.3, 1.0, 0.5);
const ofun = vtkPiecewiseFunction.newInstance();
ofun.addPoint(0.0, 0.0);
ofun.addPoint(255.0, 1.0);
volume.getProperty().setRGBTransferFunction(0, ctfun);
volume.getProperty().setScalarOpacity(0, ofun);
volume.getProperty().setScalarOpacityUnitDistance(0, 3.0);
volume.getProperty().setInterpolationTypeToFastLinear();

vmapper.setInputConnection(reader.getOutputPort());

// now create something to view it
const glwindow = renderWindow.newAPISpecificView();
glwindow.setContainer(renderWindowContainer);
renderWindow.addView(glwindow);
glwindow.setSize(400, 400);

// const renderer2 = vtkRenderer.newInstance();
// renderWindow.addRenderer(renderer2);
// renderer2.setLayer(0);
// renderer2.setViewport(0.0, 0.0, 0.5, 0.5);
// renderer2.setBackground(0.8, 0.4, 0.2);
const actor = vtkActor.newInstance();
renderer.addActor(actor);
// renderer2.setPreserveColorBuffer(true);
// renderer2.setPreserveDepthBuffer(true);

const mapper = vtkMapper.newInstance();
actor.setMapper(mapper);
actor.setPosition(300, 200, 200);
actor.getProperty().setOpacity(0.1);

const sphereSource = vtkSphereSource.newInstance({
  radius: 100,
  thetaResolution: 36,
  phiResolution: 18,
});
mapper.setInputConnection(sphereSource.getOutputPort());
// renderer2.resetCamera();

// Interactor
const interactor = vtkRenderWindowInteractor.newInstance();
interactor.setStillUpdateRate(0.01);
interactor.setView(glwindow);
interactor.initialize();
interactor.bindEvents(renderWindowContainer);

interactor.setInteractorStyle(vtkInteractorStyleTrackballCamera.newInstance());

reader.setUrl(`${__BASE_PATH__}/Data/volume/LIDC2.vti`).then(() => {
  reader.loadData().then(() => {
    renderer.addVolume(volume);
    renderer.resetCamera();
    renderer.getActiveCamera().zoom(1.5);
    renderer.getActiveCamera().elevation(70);
    renderer.getActiveCamera().orthogonalizeViewUp();
    renderer.getActiveCamera().azimuth(-20);
    renderer.resetCameraClippingRange();
    renderWindow.render();
  });
  renderWindow.render();
});

renderWindow.render();

Ken, Thanks for the example.

I borrowed from another one’s sandbox setup for ‘volume-rendering-bounds’ and put your code in with slight modification on the imports (using @kit) and the BASE_PATH variable.

As you can see from the sandbox rendering, in your case the volume always show but the ball gets clipped instead, so it is still wrong.

in this picture the ball is actually placed in front of the volume so it should show. instead it was blocked by the volume. If transparency is removed it renders fine.

That is expected. Volumes get drawn on top of transparent objects, not intermixed with them. The odd black rendering was suspect but what you see above is expected.

Intermixing of transparent surfaces with volume rendering works great with VTK proper.

Probably limitations of VTK.js are obvious for those who only use JavaScript but may be unexpected for those who use C++ and Python as well.

Sorry, let me clarify, the behavior described above matches what I would expect from the current implementation in vtk.js. The black rendering did not.

VTK C++ does have a very nice DualDepthPeeling class that can be enabled/added and can be set to intermix with volumes.

1 Like

Could this issue be fixed as soon as possible? It is extremely useful to allow the transparency of surface rendering objects when both volume rendering and surface rendering objects are in the same scene. Thanks!

Hi @gzt036 , unfortunately, the Depth Peeling technique (to correctly support translucent geometry with volumes) is not on our roadmap as of now.

You may wan to contribute it to VTK.js. Alternatively, it is something we can work on if you have funding.
Regards.