Skip to content

Commit 3273e25

Browse files
authored
Merge pull request #2449 from tbirdso/webxr-volume-example
docs(XR): add WebXR volume rendering example
2 parents 43432a9 + 87bcb85 commit 3273e25

File tree

6 files changed

+181
-0
lines changed

6 files changed

+181
-0
lines changed

Documentation/content/docs/develop_webxr.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ vtk.js supports virtual and augmented reality rendering via the [WebXR device AP
4040

4141
[![VR Cone Example][VrCone]](../examples/VR.html)
4242
[![SkyboxViewer Example][SkyboxViewerVR]](../examples/SkyboxViewer.html?fileURL=https://data.kitware.com/api/v1/file/5ae8a89c8d777f0685796bae/download)
43+
[![XR Volume Example][WebXRVolume]](../examples/WebXRVolume.html)
4344

4445
</div>
4546

@@ -51,6 +52,7 @@ vtk.js supports virtual and augmented reality rendering via the [WebXR device AP
5152
[![GeometryViewer Example][GeometryViewer]](../examples/GeometryViewer.html?fileURL=https://data.kitware.com/api/v1/item/59de9de58d777f31ac641dc5/download)
5253
[![GeometryViewer Brain Blood Vessels][GeometryViewerBrainBloodVessels]](../examples/GeometryViewer/GeometryViewer.html?fileURL=[https://data.kitware.com/api/v1/file/61f041f14acac99f42c2ff9a/download,https://data.kitware.com/api/v1/file/61f042024acac99f42c2ffa6/download,https://data.kitware.com/api/v1/file/61f042b74acac99f42c30079/download])
5354
[![GeometryViewer Chest CT][GeometryViewerchestCT]](../examples/GeometryViewer/GeometryViewer.html?fileURL=[https://data.kitware.com/api/v1/file/61f044354acac99f42c30276/download,https://data.kitware.com/api/v1/file/61f0440f4acac99f42c30191/download,https://data.kitware.com/api/v1/file/61f044204acac99f42c30267/download])
55+
[![XR Volume Example][WebXRVolume]](../examples/WebXRVolume.html)
5456

5557
</div>
5658

225 KB
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
body {
2+
display: flex;
3+
align-items: center;
4+
justify-content: center;
5+
}
6+
7+
button {
8+
position: absolute;
9+
left: 10px;
10+
top: 10px;
11+
width: 200px;
12+
z-index: 2;
13+
cursor: pointer;
14+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import 'vtk.js/Sources/favicon';
2+
3+
// Load the rendering pieces we want to use (for both WebGL and WebGPU)
4+
import 'vtk.js/Sources/Rendering/Profiles/Volume';
5+
6+
// Force DataAccessHelper to have access to various data source
7+
import 'vtk.js/Sources/IO/Core/DataAccessHelper/HtmlDataAccessHelper';
8+
import 'vtk.js/Sources/IO/Core/DataAccessHelper/JSZipDataAccessHelper';
9+
10+
import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction';
11+
import vtkFullScreenRenderWindow from 'vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow';
12+
import HttpDataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper';
13+
import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction';
14+
import vtkURLExtract from 'vtk.js/Sources/Common/Core/URLExtract';
15+
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
16+
import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
17+
import vtkXMLImageDataReader from 'vtk.js/Sources/IO/XML/XMLImageDataReader';
18+
19+
import './WebXRVolume.module.css';
20+
21+
// ----------------------------------------------------------------------------
22+
// Standard rendering code setup
23+
// ----------------------------------------------------------------------------
24+
25+
const background = [0, 0, 0];
26+
const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
27+
background,
28+
});
29+
const renderer = fullScreenRenderer.getRenderer();
30+
const renderWindow = fullScreenRenderer.getRenderWindow();
31+
32+
// ----------------------------------------------------------------------------
33+
// Set up pipeline objects
34+
// ----------------------------------------------------------------------------
35+
36+
const vtiReader = vtkXMLImageDataReader.newInstance();
37+
const actor = vtkVolume.newInstance();
38+
const mapper = vtkVolumeMapper.newInstance();
39+
mapper.setInputConnection(vtiReader.getOutputPort());
40+
actor.setMapper(mapper);
41+
renderer.addVolume(actor);
42+
43+
// create color and opacity transfer functions
44+
const ctfun = vtkColorTransferFunction.newInstance();
45+
const ofun = vtkPiecewiseFunction.newInstance();
46+
47+
// ----------------------------------------------------------------------------
48+
// Example code
49+
// ----------------------------------------------------------------------------
50+
51+
const {
52+
fileURL = 'https://data.kitware.com/api/v1/file/59de9dca8d777f31ac641dc2/download',
53+
} = vtkURLExtract.extractURLParameters();
54+
55+
HttpDataAccessHelper.fetchBinary(fileURL).then((fileContents) => {
56+
// Read data
57+
vtiReader.parseAsArrayBuffer(fileContents);
58+
const data = vtiReader.getOutputData(0);
59+
const dataArray =
60+
data.getPointData().getScalars() || data.getPointData().getArrays()[0];
61+
const dataRange = dataArray.getRange();
62+
63+
// Restyle visual appearance
64+
const sampleDistance =
65+
0.7 *
66+
Math.sqrt(
67+
data
68+
.getSpacing()
69+
.map((v) => v * v)
70+
.reduce((a, b) => a + b, 0)
71+
);
72+
mapper.setSampleDistance(sampleDistance);
73+
74+
ctfun.addRGBPoint(dataRange[0], 0.0, 0.3, 0.3);
75+
ctfun.addRGBPoint(dataRange[1], 1.0, 1.0, 1.0);
76+
ofun.addPoint(dataRange[0], 0.0);
77+
ofun.addPoint((dataRange[1] - dataRange[0]) / 4, 0.0);
78+
ofun.addPoint(dataRange[1], 0.5);
79+
actor.getProperty().setRGBTransferFunction(0, ctfun);
80+
actor.getProperty().setScalarOpacity(0, ofun);
81+
actor.getProperty().setInterpolationTypeToLinear();
82+
83+
// Set up rendering
84+
renderer.resetCamera();
85+
renderWindow.render();
86+
87+
// Add button to launch AR (default) or VR scene
88+
const VR = 1;
89+
const AR = 2;
90+
let xrSessionType = 0;
91+
const xrButton = document.createElement('button');
92+
let enterText = 'XR not available!';
93+
const exitText = 'Exit XR';
94+
xrButton.textContent = enterText;
95+
if (
96+
navigator.xr !== undefined &&
97+
fullScreenRenderer.getApiSpecificRenderWindow().getXrSupported()
98+
) {
99+
navigator.xr.isSessionSupported('immersive-ar').then((arSupported) => {
100+
if (arSupported) {
101+
xrSessionType = AR;
102+
enterText = 'Start AR';
103+
xrButton.textContent = enterText;
104+
} else {
105+
navigator.xr.isSessionSupported('immersive-vr').then((vrSupported) => {
106+
if (vrSupported) {
107+
xrSessionType = VR;
108+
enterText = 'Start VR';
109+
xrButton.textContent = enterText;
110+
}
111+
});
112+
}
113+
});
114+
}
115+
xrButton.addEventListener('click', () => {
116+
if (xrButton.textContent === enterText) {
117+
if (xrSessionType === AR) {
118+
fullScreenRenderer.setBackground([0, 0, 0, 0]);
119+
}
120+
fullScreenRenderer
121+
.getApiSpecificRenderWindow()
122+
.startXR(xrSessionType === AR);
123+
xrButton.textContent = exitText;
124+
} else {
125+
fullScreenRenderer.setBackground([...background, 255]);
126+
fullScreenRenderer
127+
.getApiSpecificRenderWindow()
128+
.stopXR(xrSessionType === AR);
129+
xrButton.textContent = enterText;
130+
}
131+
});
132+
document.querySelector('.content').appendChild(xrButton);
133+
});
134+
135+
// -----------------------------------------------------------
136+
// Make some variables global so that you can inspect and
137+
// modify objects in your browser's developer console:
138+
// -----------------------------------------------------------
139+
140+
global.source = vtiReader;
141+
global.mapper = mapper;
142+
global.actor = actor;
143+
global.ctfun = ctfun;
144+
global.ofun = ofun;
145+
global.renderer = renderer;
146+
global.renderWindow = renderWindow;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
### WebXR Volume Viewer
3+
4+
vtk.js supports volume rendering in virtual and augmented reality environments for WebXR compatible systems.
5+
6+
This example loads and renders a .vti volume file found [here](https://data.kitware.com/api/v1/file/59de9dca8d777f31ac641dc2/download). Press the "Enter VR" or "Enter AR" button to launch an XR rendering on a WebXR-compatible system. To specify other .vti files, provide the `?fileURL=<link>` URL parameter.
7+
8+
Volume rendering can be processor-intensive. Smaller volumes may give better performance on mobile devices.
9+
10+
- [binary-head.vti](https://kitware.github.io/vtk-js/examples/WebXRVolume/WebXRVolume.html?fileURL=https://data.kitware.com/api/v1/file/59de9dca8d777f31ac641dc2/download) 15.6 MB
11+
- [binary-head-2.vti](https://kitware.github.io/vtk-js/examples/WebXRVolume/WebXRVolume.html?fileURL=https://data.kitware.com/api/v1/file/629921a64acac99f429a45a7/download) 361 kB
12+
- [tiny-image.vti](https://kitware.github.io/vtk-js/examples/WebXRVolume/WebXRVolume.html?fileURL=https://data.kitware.com/api/v1/file/624320e74acac99f42254a25/download) 1.6 kB
13+
14+
### See Also
15+
16+
[Full list of WebXR Examples](https://kitware.github.io/vtk-js/docs/develop_webxr.html)
17+
[VolumeViewer Example](https://kitware.github.io/vtk-js/examples/VolumeViewer.html)

Sources/Rendering/OpenGL/RenderWindow/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,8 @@ function vtkOpenGLRenderWindow(publicAPI, model) {
380380

381381
camera.setPhysicalScale(physicalScale);
382382
camera.setPhysicalTranslation(physicalTranslation);
383+
// Clip at 0.1m, 100.0m in physical space by default
384+
camera.setClippingRange(0.1 * physicalScale, 100.0 * physicalScale);
383385
};
384386

385387
publicAPI.stopXR = async () => {

0 commit comments

Comments
 (0)