Open
Description
Bug description
When i try to render a image and a polydata with vtkjs i find that the part of the image inside the cube disappear! The cube is transparent and its opacity is 0.3. I use vtkVolumeMapper for image data and vtkMapper for polydata.
Can anyone help me with these issue? How can i blend the volume and mesh data in 3D. Many thanks!
Steps to reproduce
Using the following script can reproduce the issue. Just open the html file with web browser and load an *.vti file.
This is the html code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VTK.js Volume Rendering</title>
<script src="https://unpkg.com/vtk.js"></script>
<script src="main.js" defer></script>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#vtkContainer {
width: 100vw;
height: 100vh;
}
#fileInput {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
}
</style>
</head>
<body>
<input type="file" id="fileInput" accept=".vti" />
<div id="vtkContainer"></div>
</body>
</html>
This is the js code. Save it as main.js.
document.addEventListener('DOMContentLoaded', function () {
const vtkContainer = document.getElementById('vtkContainer');
const fileInput = document.getElementById('fileInput');
// Create the VTK.js full screen renderer
const fullScreenRenderer = vtk.Rendering.Misc.vtkFullScreenRenderWindow.newInstance({
rootContainer: vtkContainer,
background: [0, 0, 0],
});
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();
// Listen for file input changes
fileInput.addEventListener('change', function (event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function (e) {
const arrayBuffer = e.target.result;
// Create the reader for the .vti file
const vtkReader = vtk.IO.XML.vtkXMLImageDataReader.newInstance();
vtkReader.parseAsArrayBuffer(arrayBuffer);
const imageData = vtkReader.getOutputData();
// Print image data information for debugging
console.log('Image Data:', imageData);
console.log('Bounds:', imageData.getBounds());
console.log('Dimensions:', imageData.getDimensions());
console.log('Scalar Range:', imageData.getPointData().getScalars().getRange());
// Create the volume mapper and actor
const volumeMapper = vtk.Rendering.Core.vtkVolumeMapper.newInstance();
volumeMapper.setInputData(imageData);
volumeMapper.setSampleDistance(0.2)
const volumeActor = vtk.Rendering.Core.vtkVolume.newInstance();
volumeActor.setMapper(volumeMapper);
//volumeActor.getProperty().setBlendModeToComposite(); // 合成模式
// volumeActor.getProperty().setBlendModeToMaximumIntensity(); // MIP模式
// Set up color transfer function
const colorTransferFunction = vtk.Rendering.Core.vtkColorTransferFunction.newInstance();
colorTransferFunction.addRGBPoint(0, 1, 1, 1); // Black for low values
colorTransferFunction.addRGBPoint(3000, 1, 1, 1); // White for high values
// Set up opacity transfer function
const piecewiseFunction = vtk.Common.DataModel.vtkPiecewiseFunction.newInstance();
piecewiseFunction.addPoint(-467, 0); // Fully transparent for low values
piecewiseFunction.addPoint(221, 0); // Fully opaque for high values
piecewiseFunction.addPoint(537, 0);
piecewiseFunction.addPoint(2190, 0.873);
piecewiseFunction.addPoint(3000, 1);
// Assign transfer functions to the volume property
const volumeProperty = volumeActor.getProperty();
volumeProperty.setRGBTransferFunction(0, colorTransferFunction);
volumeProperty.setScalarOpacity(0, piecewiseFunction);
volumeProperty.setInterpolationTypeToLinear(); // Smooth rendering
// Add the volume actor to the renderer
renderer.addVolume(volumeActor);
// Create a transparent bounding box
const bounds = imageData.getBounds(); // Get the bounds of the volume
const center = [
(bounds[0] + bounds[1]) / 2, // X center
(bounds[2] + bounds[3]) / 2, // Y center
(bounds[4] + bounds[5]) / 2, // Z center
];
const size = [
bounds[1] - bounds[0], // X size
bounds[3] - bounds[2], // Y size
bounds[5] - bounds[4] // Z size
];
bounds[0] = bounds[0]+size[0]*0.25
bounds[1] = bounds[1]-size[0]*0.25
bounds[2] = bounds[2]+size[1]*0.25
bounds[3] = bounds[3]-size[1]*0.25
bounds[4] = bounds[4]+size[2]*0.25
bounds[5] = bounds[5]-size[2]*0.25
const cubeSource = vtk.Filters.Sources.vtkCubeSource.newInstance();
cubeSource.setBounds(bounds); // Set the cube bounds to match the volume
const cubeMapper = vtk.Rendering.Core.vtkMapper.newInstance();
cubeMapper.setInputConnection(cubeSource.getOutputPort());
const cubeActor = vtk.Rendering.Core.vtkActor.newInstance();
cubeActor.setMapper(cubeMapper);
// Set the cube properties to make it transparent
const cubeProperty = cubeActor.getProperty();
cubeProperty.setColor(1, 1, 1); // White color
cubeProperty.setOpacity(0.2); // 20% opacity
cubeProperty.setEdgeVisibility(true); // Show edges
cubeProperty.setEdgeColor(1, 1, 1); // White edges
// Add the cube actor to the renderer
renderer.addActor(cubeActor);
// Reset the camera and render
renderer.resetCamera();
renderWindow.render();
};
reader.readAsArrayBuffer(file);
});
});
Detailed Behavior
No response
Expected Behavior
The volume inside polydata can also be rendered
Environment
- vtk.js version:
- Browsers: Chrome, Edge
- OS: Windows or Linux