Skip to content

Commit d891e25

Browse files
committed
feat(reslicecursorwidget): add GPU rendering to ResliceCursorWidget
Some features are still not yet supported: - display bug when no opaque actors (showDebugActors=false) are rendered. - volume mapper should write the plane in depth buffer. - volume mapper should render BEFORE the handles
1 parent d31feab commit d891e25

File tree

2 files changed

+86
-10
lines changed

2 files changed

+86
-10
lines changed

Sources/Widgets/Widgets3D/ResliceCursorWidget/example/controlPanel.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
<input type="checkbox" id="checkboxScaleInPixels" checked>
2424
</td>
2525
</tr>
26+
<tr>
27+
<td>Render with GPU:</td>
28+
<td>
29+
<input type="checkbox" id="renderWithGPU" checked>
30+
</td>
31+
</tr>
2632
<tr>
2733
<td>Slab Mode :</td>
2834
<td>

Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';
1818
import vtkOutlineFilter from 'vtk.js/Sources/Filters/General/OutlineFilter';
1919
import vtkOrientationMarkerWidget from 'vtk.js/Sources/Interaction/Widgets/OrientationMarkerWidget';
2020
import vtkResliceCursorWidget from 'vtk.js/Sources/Widgets/Widgets3D/ResliceCursorWidget';
21+
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
22+
import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
2123
import vtkWidgetManager from 'vtk.js/Sources/Widgets/Core/WidgetManager';
2224

2325
import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource';
@@ -31,6 +33,7 @@ import controlPanel from './controlPanel.html';
3133

3234
// Force the loading of HttpDataAccessHelper to support gzip decompression
3335
import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper';
36+
import { BlendMode } from '../../../../Rendering/Core/VolumeMapper/Constants';
3437

3538
// ----------------------------------------------------------------------------
3639
// Define main attributes
@@ -53,6 +56,8 @@ widgetState.setSphereRadius(10 * window.devicePixelRatio);
5356
widgetState.setLineThickness(5);
5457

5558
const showDebugActors = true;
59+
const debugPlanes = false;
60+
let renderWithGPU = true;
5661

5762
// ----------------------------------------------------------------------------
5863
// Define html structure
@@ -142,7 +147,11 @@ for (let i = 0; i < 4; i++) {
142147
obj.interactor.bindEvents(element);
143148
obj.widgetManager.setRenderer(obj.renderer);
144149
if (i < 3) {
145-
obj.interactor.setInteractorStyle(vtkInteractorStyleImage.newInstance());
150+
obj.interactor.setInteractorStyle(
151+
debugPlanes
152+
? vtkInteractorStyleTrackballCamera.newInstance()
153+
: vtkInteractorStyleImage.newInstance()
154+
);
146155
obj.widgetInstance = obj.widgetManager.addWidget(widget, xyzToViewType[i]);
147156
obj.widgetInstance.setScaleInPixels(true);
148157
obj.widgetInstance.setRotationHandlePosition(0.75);
@@ -155,6 +164,7 @@ for (let i = 0; i < 4; i++) {
155164
);
156165
}
157166

167+
// CPU reslice
158168
obj.reslice = vtkImageReslice.newInstance();
159169
obj.reslice.setSlabMode(SlabMode.MEAN);
160170
obj.reslice.setSlabNumberOfSlices(1);
@@ -165,6 +175,18 @@ for (let i = 0; i < 4; i++) {
165175
obj.resliceMapper.setInputConnection(obj.reslice.getOutputPort());
166176
obj.resliceActor = vtkImageSlice.newInstance();
167177
obj.resliceActor.setMapper(obj.resliceMapper);
178+
179+
// GPU reslice
180+
obj.volume = vtkVolume.newInstance();
181+
obj.volumeMapper = vtkVolumeMapper.newInstance();
182+
// obj.volumeMapper.setMaximumSamplesPerRay(1);
183+
obj.volume.setMapper(obj.volumeMapper);
184+
185+
obj.sliceHelper = vtkVolumeMapper.vtkSliceHelper.newInstance({
186+
thickness: 1,
187+
});
188+
obj.sliceHelper.registerClipPlanesToMapper(obj.volumeMapper);
189+
168190
obj.sphereActors = [];
169191
obj.sphereSources = [];
170192

@@ -253,6 +275,7 @@ function updateReslice(
253275
viewType: '',
254276
reslice: null,
255277
actor: null,
278+
sliceHelper: null,
256279
renderer: null,
257280
resetFocalPoint: false, // Reset the focal point to the center of the display image
258281
keepFocalPointPosition: false, // Defines if the focal point position is kepts (same display distance from reslice cursor center)
@@ -267,9 +290,20 @@ function updateReslice(
267290
);
268291
if (obj.modified) {
269292
// Get returned modified from setter to know if we have to render
293+
// CPU
270294
interactionContext.actor.setUserMatrix(
271295
interactionContext.reslice.getResliceAxes()
272296
);
297+
// GPU
298+
interactionContext.sliceHelper.setNormal(
299+
interactionContext.reslice.getResliceAxes()[8],
300+
interactionContext.reslice.getResliceAxes()[9],
301+
interactionContext.reslice.getResliceAxes()[10]
302+
);
303+
interactionContext.sliceHelper.setOrigin(
304+
widget.getWidgetState().getCenter()
305+
);
306+
273307
interactionContext.sphereSources[0].setCenter(...obj.origin);
274308
interactionContext.sphereSources[1].setCenter(...obj.point1);
275309
interactionContext.sphereSources[2].setCenter(...obj.point2);
@@ -301,8 +335,17 @@ reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`).then(() => {
301335
view3D.renderer.addActor(outlineActor);
302336

303337
viewAttributes.forEach((obj, i) => {
304-
obj.reslice.setInputData(image);
305-
obj.renderer.addActor(obj.resliceActor);
338+
obj.reslice.setInputData(image); // CPU
339+
obj.volumeMapper.setInputData(image); // GPU
340+
341+
obj.renderer.addActor(obj.resliceActor); // CPU
342+
obj.renderer.addVolume(obj.volume); // GPU
343+
344+
obj.volume
345+
.getProperty()
346+
.getRGBTransferFunction(0)
347+
.setRange(...image.getPointData().getScalars().getRange());
348+
306349
view3D.renderer.addActor(obj.resliceActor);
307350
obj.sphereActors.forEach((actor) => {
308351
obj.renderer.addActor(actor);
@@ -333,7 +376,8 @@ reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`).then(() => {
333376
updateReslice({
334377
viewType,
335378
reslice,
336-
actor: obj.resliceActor,
379+
actor: obj.resliceActor, // CPU
380+
sliceHelper: obj.sliceHelper, // GPU
337381
renderer: obj.renderer,
338382
resetFocalPoint: false,
339383
keepFocalPointPosition,
@@ -347,7 +391,8 @@ reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`).then(() => {
347391
updateReslice({
348392
viewType,
349393
reslice,
350-
actor: obj.resliceActor,
394+
actor: obj.resliceActor, // CPU
395+
sliceHelper: obj.sliceHelper, // GPU
351396
renderer: obj.renderer,
352397
resetFocalPoint: true, // At first initilization, center the focal point to the image center
353398
keepFocalPointPosition: false, // Don't update the focal point as we already set it to the center of the image
@@ -375,6 +420,7 @@ function updateViews() {
375420
viewType: xyzToViewType[i],
376421
reslice: obj.reslice,
377422
actor: obj.resliceActor,
423+
sliceHelper: obj.sliceHelper,
378424
renderer: obj.renderer,
379425
resetFocalPoint: true,
380426
keepFocalPointPosition: false,
@@ -411,6 +457,20 @@ checkboxScaleInPixels.addEventListener('change', (ev) => {
411457
});
412458
});
413459

460+
document.getElementById('renderWithGPU').addEventListener('change', (ev) => {
461+
renderWithGPU = ev.target.checked;
462+
viewAttributes.forEach((obj, i) => {
463+
obj.resliceActor.setVisibility(!renderWithGPU);
464+
obj.volume.setVisibility(renderWithGPU);
465+
obj.interactor.render();
466+
});
467+
});
468+
viewAttributes.forEach((obj, i) => {
469+
obj.resliceActor.setVisibility(!renderWithGPU);
470+
obj.volume.setVisibility(renderWithGPU);
471+
obj.interactor.render();
472+
});
473+
414474
const optionSlabModeMin = document.getElementById('slabModeMin');
415475
optionSlabModeMin.value = SlabMode.MIN;
416476
const optionSlabModeMax = document.getElementById('slabModeMax');
@@ -419,10 +479,16 @@ const optionSlabModeMean = document.getElementById('slabModeMean');
419479
optionSlabModeMean.value = SlabMode.MEAN;
420480
const optionSlabModeSum = document.getElementById('slabModeSum');
421481
optionSlabModeSum.value = SlabMode.SUM;
422-
const selectSlabMode = document.getElementById('slabMode');
423-
selectSlabMode.addEventListener('change', (ev) => {
482+
const slabModeToBlendMode = {
483+
[SlabMode.MIN]: BlendMode.MINIMUM_INTENSITY_BLEND,
484+
[SlabMode.MAX]: BlendMode.MAXIMUM_INTENSITY_BLEND,
485+
[SlabMode.MEAN]: BlendMode.AVERAGE_INTENSITY_BLEND,
486+
[SlabMode.SUM]: BlendMode.ADDITIVE_INTENSITY_BLEND,
487+
};
488+
document.getElementById('slabMode').addEventListener('change', (ev) => {
424489
viewAttributes.forEach((obj) => {
425-
obj.reslice.setSlabMode(Number(ev.target.value));
490+
obj.reslice.setSlabMode(Number(ev.target.value)); // CPU
491+
obj.volumeMapper.setBlendMode(slabModeToBlendMode[ev.target.value]); // GPU
426492
});
427493
updateViews();
428494
});
@@ -432,7 +498,8 @@ sliderSlabNumberofSlices.addEventListener('change', (ev) => {
432498
const trSlabNumberValue = document.getElementById('slabNumberValue');
433499
trSlabNumberValue.innerHTML = ev.target.value;
434500
viewAttributes.forEach((obj) => {
435-
obj.reslice.setSlabNumberOfSlices(ev.target.value);
501+
obj.reslice.setSlabNumberOfSlices(ev.target.value); // CPU
502+
obj.sliceHelper.setThickness(Number(ev.target.value)); // GPU
436503
});
437504
updateViews();
438505
});
@@ -447,7 +514,10 @@ buttonReset.addEventListener('click', () => {
447514
const selectInterpolationMode = document.getElementById('selectInterpolation');
448515
selectInterpolationMode.addEventListener('change', (ev) => {
449516
viewAttributes.forEach((obj) => {
450-
obj.reslice.setInterpolationMode(Number(ev.target.selectedIndex));
517+
obj.reslice.setInterpolationMode(Number(ev.target.selectedIndex)); // CPU
518+
obj.volume
519+
.getProperty()
520+
.setInterpolationType(Number(ev.target.selectedIndex)); // GPU
451521
});
452522
updateViews();
453523
});

0 commit comments

Comments
 (0)