@kitware/vtk.js 22.0.2 → 22.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -92,7 +92,7 @@ function convertItkToVtkImage(itkImage) {
|
|
|
92
92
|
imageData.getPointData().setScalars(pointData); // Associate the point data that are 3D vectors / tensors
|
|
93
93
|
// Refer to itk-js/src/PixelTypes.js for numerical values
|
|
94
94
|
|
|
95
|
-
switch (ITKPixelTypes[itkImage.imageType.pixelType]) {
|
|
95
|
+
switch (isITKWasm ? ITKPixelTypes[itkImage.imageType.pixelType] : itkImage.imageType.pixelType) {
|
|
96
96
|
case ITKPixelTypes.Scalar:
|
|
97
97
|
break;
|
|
98
98
|
|
|
@@ -10,15 +10,16 @@ export interface IStyle {
|
|
|
10
10
|
fontSizeScale?: (res: number) => number;
|
|
11
11
|
edgeThickness?: number;
|
|
12
12
|
edgeColor?: string;
|
|
13
|
+
resolution?: number;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export interface IFaceProperty {
|
|
16
|
+
export interface IFaceProperty extends IStyle {
|
|
16
17
|
text?: string;
|
|
17
18
|
faceRotation?: number;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
+
*
|
|
22
23
|
*/
|
|
23
24
|
export interface IAnnotatedCubeActorInitialValues extends IActorInitialValues {
|
|
24
25
|
}
|
|
@@ -18,7 +18,7 @@ var vtkWarningMacro = macro.vtkWarningMacro,
|
|
|
18
18
|
var deviceInputMap = {
|
|
19
19
|
'xr-standard': [Input.Trigger, Input.Grip, Input.TrackPad, Input.Thumbstick, Input.A, Input.B]
|
|
20
20
|
};
|
|
21
|
-
var handledEvents = ['StartAnimation', 'Animation', 'EndAnimation', 'MouseEnter', 'MouseLeave', 'StartMouseMove', 'MouseMove', 'EndMouseMove', 'LeftButtonPress', 'LeftButtonRelease', 'MiddleButtonPress', 'MiddleButtonRelease', 'RightButtonPress', 'RightButtonRelease', 'KeyPress', 'KeyDown', 'KeyUp', 'StartMouseWheel', 'MouseWheel', 'EndMouseWheel', 'StartPinch', 'Pinch', 'EndPinch', 'StartPan', 'Pan', 'EndPan', 'StartRotate', 'Rotate', 'EndRotate', 'Button3D', 'Move3D', 'StartPointerLock', 'EndPointerLock', 'StartInteraction', 'Interaction', 'EndInteraction'];
|
|
21
|
+
var handledEvents = ['StartAnimation', 'Animation', 'EndAnimation', 'MouseEnter', 'MouseLeave', 'StartMouseMove', 'MouseMove', 'EndMouseMove', 'LeftButtonPress', 'LeftButtonRelease', 'MiddleButtonPress', 'MiddleButtonRelease', 'RightButtonPress', 'RightButtonRelease', 'KeyPress', 'KeyDown', 'KeyUp', 'StartMouseWheel', 'MouseWheel', 'EndMouseWheel', 'StartPinch', 'Pinch', 'EndPinch', 'StartPan', 'Pan', 'EndPan', 'StartRotate', 'Rotate', 'EndRotate', 'Button3D', 'Move3D', 'StartPointerLock', 'EndPointerLock', 'StartInteraction', 'Interaction', 'EndInteraction', 'AnimationFrameRateUpdate'];
|
|
22
22
|
|
|
23
23
|
function preventDefault(event) {
|
|
24
24
|
if (event.cancelable) {
|
|
@@ -326,6 +326,8 @@ function vtkRenderWindowInteractor(publicAPI, model) {
|
|
|
326
326
|
animationRequesters.add(requestor);
|
|
327
327
|
|
|
328
328
|
if (animationRequesters.size === 1 && !model.xrAnimation) {
|
|
329
|
+
model._animationStartTime = Date.now();
|
|
330
|
+
model._animationFrameCount = 0;
|
|
329
331
|
model.lastFrameTime = 0.1;
|
|
330
332
|
model.lastFrameStart = Date.now();
|
|
331
333
|
model.animationRequest = requestAnimationFrame(publicAPI.handleAnimation);
|
|
@@ -450,6 +452,14 @@ function vtkRenderWindowInteractor(publicAPI, model) {
|
|
|
450
452
|
|
|
451
453
|
publicAPI.handleAnimation = function () {
|
|
452
454
|
var currTime = Date.now();
|
|
455
|
+
model._animationFrameCount++;
|
|
456
|
+
|
|
457
|
+
if (currTime - model._animationStartTime > 1000.0 && model._animationFrameCount > 1) {
|
|
458
|
+
model.recentAnimationFrameRate = 1000.0 * (model._animationFrameCount - 1) / (currTime - model._animationStartTime);
|
|
459
|
+
publicAPI.animationFrameRateUpdateEvent();
|
|
460
|
+
model._animationStartTime = currTime;
|
|
461
|
+
model._animationFrameCount = 1;
|
|
462
|
+
}
|
|
453
463
|
|
|
454
464
|
if (model.FrameTime === -1.0) {
|
|
455
465
|
model.lastFrameTime = 0.1;
|
|
@@ -498,7 +508,7 @@ function vtkRenderWindowInteractor(publicAPI, model) {
|
|
|
498
508
|
model.wheelTimeoutID = setTimeout(function () {
|
|
499
509
|
publicAPI.endMouseWheelEvent();
|
|
500
510
|
model.wheelTimeoutID = 0;
|
|
501
|
-
},
|
|
511
|
+
}, 800);
|
|
502
512
|
};
|
|
503
513
|
|
|
504
514
|
publicAPI.handleMouseEnter = function (event) {
|
|
@@ -924,6 +934,8 @@ function vtkRenderWindowInteractor(publicAPI, model) {
|
|
|
924
934
|
|
|
925
935
|
publicAPI.handleVisibilityChange = function () {
|
|
926
936
|
model.lastFrameStart = Date.now();
|
|
937
|
+
model._animationStartTime = Date.now();
|
|
938
|
+
model._animationFrameCount = 0;
|
|
927
939
|
};
|
|
928
940
|
|
|
929
941
|
publicAPI.setCurrentRenderer = function (r) {
|
|
@@ -975,6 +987,7 @@ var DEFAULT_VALUES = {
|
|
|
975
987
|
currentGesture: 'Start',
|
|
976
988
|
animationRequest: null,
|
|
977
989
|
lastFrameTime: 0.1,
|
|
990
|
+
recentAnimationFrameRate: 10.0,
|
|
978
991
|
wheelTimeoutID: 0,
|
|
979
992
|
moveTimeoutID: 0,
|
|
980
993
|
lastGamepadValues: {}
|
|
@@ -990,7 +1003,7 @@ function extend(publicAPI, model) {
|
|
|
990
1003
|
return macro.event(publicAPI, model, eventName);
|
|
991
1004
|
}); // Create get-only macros
|
|
992
1005
|
|
|
993
|
-
macro.get(publicAPI, model, ['initialized', 'container', 'interactorStyle', 'lastFrameTime', 'view']); // Create get-set macros
|
|
1006
|
+
macro.get(publicAPI, model, ['initialized', 'container', 'interactorStyle', 'lastFrameTime', 'recentAnimationFrameRate', 'view']); // Create get-set macros
|
|
994
1007
|
|
|
995
1008
|
macro.setGet(publicAPI, model, ['lightFollowCamera', 'enabled', 'enableRender', 'recognizeGestures', 'desiredUpdateRate', 'stillUpdateRate', 'picker']); // For more macro methods, see "Sources/macros.js"
|
|
996
1009
|
// Object specific methods
|
|
@@ -8,6 +8,8 @@ import vtkWebGPUMapperHelper from './MapperHelper.js';
|
|
|
8
8
|
import vtkWebGPURenderEncoder from './RenderEncoder.js';
|
|
9
9
|
import vtkWebGPUShaderCache from './ShaderCache.js';
|
|
10
10
|
import vtkWebGPUTexture from './Texture.js';
|
|
11
|
+
import vtkWebGPUUniformBuffer from './UniformBuffer.js';
|
|
12
|
+
import vtkWebGPUFullScreenQuad from './FullScreenQuad.js';
|
|
11
13
|
import vtkWebGPUVolumePassFSQ from './VolumePassFSQ.js';
|
|
12
14
|
import { f as distance2BetweenPoints } from '../../Common/Core/Math/index.js';
|
|
13
15
|
|
|
@@ -32,6 +34,7 @@ var BufferUsage = vtkWebGPUBufferManager.BufferUsage,
|
|
|
32
34
|
|
|
33
35
|
var cubeFaceTriangles = [[0, 4, 6], [0, 6, 2], [1, 3, 7], [1, 7, 5], [0, 5, 4], [0, 1, 5], [2, 6, 7], [2, 7, 3], [0, 3, 1], [0, 2, 3], [4, 5, 7], [4, 7, 6]];
|
|
34
36
|
var DepthBoundsFS = "\n//VTK::Renderer::Dec\n\n//VTK::Select::Dec\n\n//VTK::VolumePass::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : fragmentOutput;\n\n //VTK::Select::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::VolumePass::Impl\n\n // use the maximum (closest) of the current value and the zbuffer\n // the blend func will then take the min to find the farthest stop value\n var stopval: f32 = max(input.fragPos.z, textureLoad(opaquePassDepthTexture, vec2<i32>(i32(input.fragPos.x), i32(input.fragPos.y)), 0));\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
|
|
37
|
+
var volumeCopyFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = textureSample(volumePassSmallColorTexture,\n volumePassSmallColorTextureSampler, mapperUBO.tscale*input.tcoordVS);\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
|
|
35
38
|
/* eslint-disable no-undef */
|
|
36
39
|
|
|
37
40
|
/* eslint-disable no-bitwise */
|
|
@@ -39,7 +42,51 @@ var DepthBoundsFS = "\n//VTK::Renderer::Dec\n\n//VTK::Select::Dec\n\n//VTK::Volu
|
|
|
39
42
|
|
|
40
43
|
function vtkWebGPUVolumePass(publicAPI, model) {
|
|
41
44
|
// Set our className
|
|
42
|
-
model.classHierarchy.push('vtkWebGPUVolumePass');
|
|
45
|
+
model.classHierarchy.push('vtkWebGPUVolumePass'); // create the required textures, encoders, FSQ etc
|
|
46
|
+
|
|
47
|
+
publicAPI.initialize = function (viewNode) {
|
|
48
|
+
if (!model._clearEncoder) {
|
|
49
|
+
publicAPI.createClearEncoder(viewNode);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!model._mergeEncoder) {
|
|
53
|
+
publicAPI.createMergeEncoder(viewNode);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!model._copyEncoder) {
|
|
57
|
+
publicAPI.createCopyEncoder(viewNode);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!model._depthRangeEncoder) {
|
|
61
|
+
publicAPI.createDepthRangeEncoder(viewNode);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!model.fullScreenQuad) {
|
|
65
|
+
model.fullScreenQuad = vtkWebGPUVolumePassFSQ.newInstance();
|
|
66
|
+
model.fullScreenQuad.setDevice(viewNode.getDevice());
|
|
67
|
+
model.fullScreenQuad.setTextureViews(_toConsumableArray(model._depthRangeEncoder.getColorTextureViews()));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!model._volumeCopyQuadQuad) {
|
|
71
|
+
model._volumeCopyQuad = vtkWebGPUFullScreenQuad.newInstance();
|
|
72
|
+
|
|
73
|
+
model._volumeCopyQuad.setPipelineHash('volpassfsq');
|
|
74
|
+
|
|
75
|
+
model._volumeCopyQuad.setDevice(viewNode.getDevice());
|
|
76
|
+
|
|
77
|
+
model._volumeCopyQuad.setFragmentShaderTemplate(volumeCopyFragTemplate);
|
|
78
|
+
|
|
79
|
+
model._copyUBO = vtkWebGPUUniformBuffer.newInstance();
|
|
80
|
+
|
|
81
|
+
model._copyUBO.setName('mapperUBO');
|
|
82
|
+
|
|
83
|
+
model._copyUBO.addEntry('tscale', 'vec2<f32>');
|
|
84
|
+
|
|
85
|
+
model._volumeCopyQuad.setUBO(model._copyUBO);
|
|
86
|
+
|
|
87
|
+
model._volumeCopyQuad.setTextureViews([model._colorTextureView]);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
43
90
|
|
|
44
91
|
publicAPI.traverse = function (renNode, viewNode) {
|
|
45
92
|
if (model.deleted) {
|
|
@@ -47,29 +94,25 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
47
94
|
} // we just render our delegates in order
|
|
48
95
|
|
|
49
96
|
|
|
50
|
-
model.currentParent = viewNode; //
|
|
97
|
+
model.currentParent = viewNode; // create stuff we need
|
|
51
98
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
99
|
+
publicAPI.initialize(viewNode); // determine if we are rendering a small size
|
|
100
|
+
|
|
101
|
+
publicAPI.computeTiming(viewNode); // first render the boxes to generate a min max depth
|
|
55
102
|
// map for all the volumes
|
|
56
103
|
|
|
104
|
+
publicAPI.renderDepthBounds(renNode, viewNode); // always mark true
|
|
57
105
|
|
|
58
|
-
|
|
106
|
+
model._firstGroup = true;
|
|
107
|
+
var device = viewNode.getDevice(); // determine how many volumes we can render at a time. We subtract
|
|
108
|
+
// 4 because we use know we use textures for min, max, ofun and tfun
|
|
59
109
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
model.fullScreenQuad.setDevice(viewNode.getDevice());
|
|
63
|
-
model.fullScreenQuad.setTextureViews(_toConsumableArray(model.depthRangeEncoder.getColorTextureViews()));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
var device = viewNode.getDevice(); // -4 because we use know we use textures for min, max, ofun and tfun
|
|
67
|
-
|
|
68
|
-
var maxVolumes = device.getHandle().limits.maxSampledTexturesPerShaderStage - 4;
|
|
69
|
-
var cameraPos = renNode.getRenderable().getActiveCamera().getPosition();
|
|
110
|
+
var maxVolumes = device.getHandle().limits.maxSampledTexturesPerShaderStage - 4; // if we have to make multiple passes then break the volumes up into groups
|
|
111
|
+
// rendered from farthest to closest
|
|
70
112
|
|
|
71
113
|
if (model.volumes.length > maxVolumes) {
|
|
72
|
-
// sort from back to front based on volume centroid
|
|
114
|
+
var cameraPos = renNode.getRenderable().getActiveCamera().getPosition(); // sort from back to front based on volume centroid
|
|
115
|
+
|
|
73
116
|
var distances = [];
|
|
74
117
|
|
|
75
118
|
for (var v = 0; v < model.volumes.length; v++) {
|
|
@@ -94,26 +137,117 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
94
137
|
volumesToRender.push(model.volumes[volumeOrder[_v]]);
|
|
95
138
|
|
|
96
139
|
if (volumesToRender.length >= chunkSize) {
|
|
97
|
-
publicAPI.
|
|
140
|
+
publicAPI.rayCastPass(viewNode, renNode, volumesToRender);
|
|
98
141
|
volumesToRender = [];
|
|
99
142
|
chunkSize = maxVolumes;
|
|
143
|
+
model._firstGroup = false;
|
|
100
144
|
}
|
|
101
145
|
}
|
|
102
146
|
} else {
|
|
103
|
-
|
|
147
|
+
// if not rendering in chunks then just draw all of them at once
|
|
148
|
+
publicAPI.rayCastPass(viewNode, renNode, model.volumes);
|
|
149
|
+
} // copy back to the original color buffer
|
|
150
|
+
// final composite
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
model._copyEncoder.setColorTextureView(0, model.colorTextureView);
|
|
154
|
+
|
|
155
|
+
model._copyEncoder.attachTextureViews();
|
|
156
|
+
|
|
157
|
+
renNode.setRenderEncoder(model._copyEncoder);
|
|
158
|
+
|
|
159
|
+
model._copyEncoder.begin(viewNode.getCommandEncoder());
|
|
160
|
+
|
|
161
|
+
renNode.scissorAndViewport(model._copyEncoder);
|
|
162
|
+
|
|
163
|
+
model._volumeCopyQuad.setWebGPURenderer(renNode);
|
|
164
|
+
|
|
165
|
+
if (model._useSmallViewport) {
|
|
166
|
+
var width = model._colorTextureView.getTexture().getWidth();
|
|
167
|
+
|
|
168
|
+
var height = model._colorTextureView.getTexture().getHeight();
|
|
169
|
+
|
|
170
|
+
model._copyUBO.setArray('tscale', [model._smallViewportWidth / width, model._smallViewportHeight / height]);
|
|
171
|
+
} else {
|
|
172
|
+
model._copyUBO.setArray('tscale', [1.0, 1.0]);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
model._copyUBO.sendIfNeeded(device);
|
|
176
|
+
|
|
177
|
+
model._volumeCopyQuad.render(model._copyEncoder, viewNode.getDevice());
|
|
178
|
+
|
|
179
|
+
model._copyEncoder.end();
|
|
180
|
+
}; // unsubscribe from our listeners
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
publicAPI.delete = macro.chain(function () {
|
|
184
|
+
if (model._animationRateSubscription) {
|
|
185
|
+
model._animationRateSubscription.unsubscribe();
|
|
186
|
+
|
|
187
|
+
model._animationRateSubscription = null;
|
|
188
|
+
}
|
|
189
|
+
}, publicAPI.delete);
|
|
190
|
+
|
|
191
|
+
publicAPI.computeTiming = function (viewNode) {
|
|
192
|
+
model._useSmallViewport = false;
|
|
193
|
+
var rwi = viewNode.getRenderable().getInteractor();
|
|
194
|
+
|
|
195
|
+
if (rwi.isAnimating() && model._lastScale > 1.5) {
|
|
196
|
+
if (!model._smallViewportHeight) {
|
|
197
|
+
model._smallViewportWidth = Math.ceil(viewNode.getCanvas().width / Math.sqrt(model._lastScale));
|
|
198
|
+
model._smallViewportHeight = Math.ceil(viewNode.getCanvas().height / Math.sqrt(model._lastScale));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
model._useSmallViewport = true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
model._colorTexture.resize(viewNode.getCanvas().width, viewNode.getCanvas().height);
|
|
205
|
+
|
|
206
|
+
if (!model._animationRateSubscription) {
|
|
207
|
+
// when the animation frame rate changes recompute the scale factor
|
|
208
|
+
model._animationRateSubscription = rwi.onAnimationFrameRateUpdate(function () {
|
|
209
|
+
var frate = rwi.getRecentAnimationFrameRate();
|
|
210
|
+
var targetScale = model._lastScale * rwi.getDesiredUpdateRate() / frate;
|
|
211
|
+
model._lastScale = targetScale; // clamp scale to some reasonable values.
|
|
212
|
+
// Below 1.5 we will just be using full resolution as that is close enough
|
|
213
|
+
// Above 400 seems like a lot so we limit to that 1/20th per axis
|
|
214
|
+
|
|
215
|
+
if (model._lastScale > 400) {
|
|
216
|
+
model._lastScale = 400;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (model._lastScale < 1.5) {
|
|
220
|
+
model._lastScale = 1.5;
|
|
221
|
+
} else {
|
|
222
|
+
model._smallViewportWidth = Math.ceil(viewNode.getCanvas().width / Math.sqrt(model._lastScale));
|
|
223
|
+
model._smallViewportHeight = Math.ceil(viewNode.getCanvas().height / Math.sqrt(model._lastScale));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
104
226
|
}
|
|
105
227
|
};
|
|
106
228
|
|
|
107
|
-
publicAPI.
|
|
108
|
-
model.
|
|
109
|
-
|
|
110
|
-
renNode.setRenderEncoder(
|
|
111
|
-
|
|
112
|
-
|
|
229
|
+
publicAPI.rayCastPass = function (viewNode, renNode, volumes) {
|
|
230
|
+
var encoder = model._firstGroup ? model._clearEncoder : model._mergeEncoder;
|
|
231
|
+
encoder.attachTextureViews();
|
|
232
|
+
renNode.setRenderEncoder(encoder);
|
|
233
|
+
encoder.begin(viewNode.getCommandEncoder());
|
|
234
|
+
|
|
235
|
+
var width = model._colorTextureView.getTexture().getWidth();
|
|
236
|
+
|
|
237
|
+
var height = model._colorTextureView.getTexture().getHeight();
|
|
238
|
+
|
|
239
|
+
if (model._useSmallViewport) {
|
|
240
|
+
width = model._smallViewportWidth;
|
|
241
|
+
height = model._smallViewportHeight;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
encoder.getHandle().setViewport(0, 0, width, height, 0.0, 1.0); // set scissor
|
|
245
|
+
|
|
246
|
+
encoder.getHandle().setScissorRect(0, 0, width, height);
|
|
113
247
|
model.fullScreenQuad.setWebGPURenderer(renNode);
|
|
114
248
|
model.fullScreenQuad.setVolumes(volumes);
|
|
115
|
-
model.fullScreenQuad.render(
|
|
116
|
-
|
|
249
|
+
model.fullScreenQuad.render(encoder, viewNode.getDevice());
|
|
250
|
+
encoder.end();
|
|
117
251
|
};
|
|
118
252
|
|
|
119
253
|
publicAPI.renderDepthBounds = function (renNode, viewNode) {
|
|
@@ -211,55 +345,32 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
211
345
|
publicAPI.drawDepthRange = function (renNode, viewNode) {
|
|
212
346
|
var device = viewNode.getDevice(); // copy current depth buffer to
|
|
213
347
|
|
|
214
|
-
|
|
215
|
-
publicAPI.createDepthRangeEncoder();
|
|
216
|
-
model.depthRangeTexture = vtkWebGPUTexture.newInstance();
|
|
217
|
-
model.depthRangeTexture.create(device, {
|
|
218
|
-
width: viewNode.getCanvas().width,
|
|
219
|
-
height: viewNode.getCanvas().height,
|
|
220
|
-
format: 'r16float',
|
|
221
|
-
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
222
|
-
});
|
|
223
|
-
var maxView = model.depthRangeTexture.createView();
|
|
224
|
-
maxView.setName('maxTexture');
|
|
225
|
-
model.depthRangeEncoder.setColorTextureView(0, maxView);
|
|
226
|
-
model.depthRangeTexture2 = vtkWebGPUTexture.newInstance();
|
|
227
|
-
model.depthRangeTexture2.create(device, {
|
|
228
|
-
width: viewNode.getCanvas().width,
|
|
229
|
-
height: viewNode.getCanvas().height,
|
|
230
|
-
format: 'r16float',
|
|
231
|
-
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
232
|
-
});
|
|
233
|
-
var minView = model.depthRangeTexture2.createView();
|
|
234
|
-
minView.setName('minTexture');
|
|
235
|
-
model.depthRangeEncoder.setColorTextureView(1, minView);
|
|
348
|
+
model._depthRangeTexture.resizeToMatch(model.colorTextureView.getTexture());
|
|
236
349
|
|
|
237
|
-
|
|
350
|
+
model._depthRangeTexture2.resizeToMatch(model.colorTextureView.getTexture());
|
|
238
351
|
|
|
239
|
-
|
|
240
|
-
} else {
|
|
241
|
-
model.depthRangeTexture.resizeToMatch(model.colorTextureView.getTexture());
|
|
242
|
-
model.depthRangeTexture2.resizeToMatch(model.colorTextureView.getTexture());
|
|
243
|
-
}
|
|
352
|
+
model._depthRangeEncoder.attachTextureViews();
|
|
244
353
|
|
|
245
|
-
model.depthRangeEncoder.attachTextureViews();
|
|
246
354
|
publicAPI.setCurrentOperation('volumeDepthRangePass');
|
|
247
|
-
renNode.setRenderEncoder(model.
|
|
355
|
+
renNode.setRenderEncoder(model._depthRangeEncoder);
|
|
248
356
|
renNode.volumeDepthRangePass(true);
|
|
249
357
|
|
|
250
358
|
model._mapper.setWebGPURenderer(renNode);
|
|
251
359
|
|
|
252
|
-
model._mapper.build(model.
|
|
360
|
+
model._mapper.build(model._depthRangeEncoder, device);
|
|
253
361
|
|
|
254
362
|
model._mapper.registerToDraw();
|
|
255
363
|
|
|
256
364
|
renNode.volumeDepthRangePass(false);
|
|
257
365
|
};
|
|
258
366
|
|
|
259
|
-
publicAPI.createDepthRangeEncoder = function () {
|
|
260
|
-
|
|
261
|
-
model.
|
|
262
|
-
|
|
367
|
+
publicAPI.createDepthRangeEncoder = function (viewNode) {
|
|
368
|
+
var device = viewNode.getDevice();
|
|
369
|
+
model._depthRangeEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
370
|
+
|
|
371
|
+
model._depthRangeEncoder.setPipelineHash('volr');
|
|
372
|
+
|
|
373
|
+
model._depthRangeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
|
|
263
374
|
var fDesc = pipeline.getShaderDescription('fragment');
|
|
264
375
|
fDesc.addOutput('vec4<f32>', 'outColor1');
|
|
265
376
|
fDesc.addOutput('vec4<f32>', 'outColor2');
|
|
@@ -267,7 +378,8 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
267
378
|
code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor1 = vec4<f32>(input.fragPos.z, 0.0, 0.0, 0.0);', 'output.outColor2 = vec4<f32>(stopval, 0.0, 0.0, 0.0);']).result;
|
|
268
379
|
fDesc.setCode(code);
|
|
269
380
|
});
|
|
270
|
-
|
|
381
|
+
|
|
382
|
+
model._depthRangeEncoder.setDescription({
|
|
271
383
|
colorAttachments: [{
|
|
272
384
|
view: null,
|
|
273
385
|
loadValue: [0.0, 0.0, 0.0, 0.0],
|
|
@@ -278,7 +390,8 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
278
390
|
storeOp: 'store'
|
|
279
391
|
}]
|
|
280
392
|
});
|
|
281
|
-
|
|
393
|
+
|
|
394
|
+
model._depthRangeEncoder.setPipelineSettings({
|
|
282
395
|
primitive: {
|
|
283
396
|
cullMode: 'none'
|
|
284
397
|
},
|
|
@@ -313,27 +426,162 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
313
426
|
}
|
|
314
427
|
}]
|
|
315
428
|
}
|
|
429
|
+
}); // and the textures it needs
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
model._depthRangeTexture = vtkWebGPUTexture.newInstance();
|
|
433
|
+
|
|
434
|
+
model._depthRangeTexture.create(device, {
|
|
435
|
+
width: viewNode.getCanvas().width,
|
|
436
|
+
height: viewNode.getCanvas().height,
|
|
437
|
+
format: 'r16float',
|
|
438
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
var maxView = model._depthRangeTexture.createView();
|
|
442
|
+
|
|
443
|
+
maxView.setName('maxTexture');
|
|
444
|
+
|
|
445
|
+
model._depthRangeEncoder.setColorTextureView(0, maxView);
|
|
446
|
+
|
|
447
|
+
model._depthRangeTexture2 = vtkWebGPUTexture.newInstance();
|
|
448
|
+
|
|
449
|
+
model._depthRangeTexture2.create(device, {
|
|
450
|
+
width: viewNode.getCanvas().width,
|
|
451
|
+
height: viewNode.getCanvas().height,
|
|
452
|
+
format: 'r16float',
|
|
453
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
var minView = model._depthRangeTexture2.createView();
|
|
457
|
+
|
|
458
|
+
minView.setName('minTexture');
|
|
459
|
+
|
|
460
|
+
model._depthRangeEncoder.setColorTextureView(1, minView);
|
|
461
|
+
|
|
462
|
+
model._mapper.setDevice(viewNode.getDevice());
|
|
463
|
+
|
|
464
|
+
model._mapper.setTextureViews([model.depthTextureView]);
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
publicAPI.createClearEncoder = function (viewNode) {
|
|
468
|
+
model._colorTexture = vtkWebGPUTexture.newInstance();
|
|
469
|
+
|
|
470
|
+
model._colorTexture.create(viewNode.getDevice(), {
|
|
471
|
+
width: viewNode.getCanvas().width,
|
|
472
|
+
height: viewNode.getCanvas().height,
|
|
473
|
+
format: 'bgra8unorm',
|
|
474
|
+
|
|
475
|
+
/* eslint-disable no-undef */
|
|
476
|
+
|
|
477
|
+
/* eslint-disable no-bitwise */
|
|
478
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
model._colorTextureView = model._colorTexture.createView();
|
|
482
|
+
|
|
483
|
+
model._colorTextureView.setName('volumePassSmallColorTexture');
|
|
484
|
+
|
|
485
|
+
model._colorTextureView.addSampler(viewNode.getDevice(), {
|
|
486
|
+
minFilter: 'linear',
|
|
487
|
+
magFilter: 'linear'
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
model._clearEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
491
|
+
|
|
492
|
+
model._clearEncoder.setColorTextureView(0, model._colorTextureView);
|
|
493
|
+
|
|
494
|
+
model._clearEncoder.setDescription({
|
|
495
|
+
colorAttachments: [{
|
|
496
|
+
view: null,
|
|
497
|
+
loadValue: [0.0, 0.0, 0.0, 0.0],
|
|
498
|
+
storeOp: 'store'
|
|
499
|
+
}]
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
model._clearEncoder.setPipelineHash('volpf');
|
|
503
|
+
|
|
504
|
+
model._clearEncoder.setPipelineSettings({
|
|
505
|
+
primitive: {
|
|
506
|
+
cullMode: 'none'
|
|
507
|
+
},
|
|
508
|
+
fragment: {
|
|
509
|
+
targets: [{
|
|
510
|
+
format: 'bgra8unorm',
|
|
511
|
+
blend: {
|
|
512
|
+
color: {
|
|
513
|
+
srcFactor: 'src-alpha',
|
|
514
|
+
dstFactor: 'one-minus-src-alpha'
|
|
515
|
+
},
|
|
516
|
+
alpha: {
|
|
517
|
+
srcfactor: 'one',
|
|
518
|
+
dstFactor: 'one-minus-src-alpha'
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}]
|
|
522
|
+
}
|
|
316
523
|
});
|
|
317
524
|
};
|
|
318
525
|
|
|
319
|
-
publicAPI.
|
|
320
|
-
model.
|
|
321
|
-
|
|
526
|
+
publicAPI.createCopyEncoder = function (viewNode) {
|
|
527
|
+
model._copyEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
528
|
+
|
|
529
|
+
model._copyEncoder.setDescription({
|
|
322
530
|
colorAttachments: [{
|
|
323
531
|
view: null,
|
|
324
532
|
loadValue: 'load',
|
|
325
533
|
storeOp: 'store'
|
|
326
534
|
}]
|
|
327
535
|
});
|
|
328
|
-
|
|
536
|
+
|
|
537
|
+
model._copyEncoder.setPipelineHash('volcopypf');
|
|
538
|
+
|
|
539
|
+
model._copyEncoder.setPipelineSettings({
|
|
540
|
+
primitive: {
|
|
541
|
+
cullMode: 'none'
|
|
542
|
+
},
|
|
543
|
+
fragment: {
|
|
544
|
+
targets: [{
|
|
545
|
+
format: 'bgra8unorm',
|
|
546
|
+
blend: {
|
|
547
|
+
color: {
|
|
548
|
+
srcFactor: 'one',
|
|
549
|
+
dstFactor: 'one-minus-src-alpha'
|
|
550
|
+
},
|
|
551
|
+
alpha: {
|
|
552
|
+
srcfactor: 'one',
|
|
553
|
+
dstFactor: 'one-minus-src-alpha'
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}]
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
publicAPI.createMergeEncoder = function (viewNode) {
|
|
562
|
+
model._mergeEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
563
|
+
|
|
564
|
+
model._mergeEncoder.setColorTextureView(0, model._colorTextureView);
|
|
565
|
+
|
|
566
|
+
model._mergeEncoder.setDescription({
|
|
567
|
+
colorAttachments: [{
|
|
568
|
+
view: null,
|
|
569
|
+
loadValue: 'load',
|
|
570
|
+
storeOp: 'store'
|
|
571
|
+
}]
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
model._mergeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
|
|
329
575
|
var fDesc = pipeline.getShaderDescription('fragment');
|
|
330
576
|
fDesc.addOutput('vec4<f32>', 'outColor');
|
|
331
577
|
var code = fDesc.getCode();
|
|
332
|
-
code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb
|
|
578
|
+
code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb, computedColor.a);']).result;
|
|
333
579
|
fDesc.setCode(code);
|
|
334
580
|
});
|
|
335
|
-
|
|
336
|
-
model.
|
|
581
|
+
|
|
582
|
+
model._mergeEncoder.setPipelineHash('volpf');
|
|
583
|
+
|
|
584
|
+
model._mergeEncoder.setPipelineSettings({
|
|
337
585
|
primitive: {
|
|
338
586
|
cullMode: 'none'
|
|
339
587
|
},
|
|
@@ -342,7 +590,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
342
590
|
format: 'bgra8unorm',
|
|
343
591
|
blend: {
|
|
344
592
|
color: {
|
|
345
|
-
srcFactor: '
|
|
593
|
+
srcFactor: 'src-alpha',
|
|
346
594
|
dstFactor: 'one-minus-src-alpha'
|
|
347
595
|
},
|
|
348
596
|
alpha: {
|
|
@@ -387,6 +635,7 @@ function extend(publicAPI, model) {
|
|
|
387
635
|
Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API
|
|
388
636
|
|
|
389
637
|
vtkRenderPass.extend(publicAPI, model, initialValues);
|
|
638
|
+
model._lastScale = 2.0;
|
|
390
639
|
model._mapper = vtkWebGPUMapperHelper.newInstance();
|
|
391
640
|
|
|
392
641
|
model._mapper.setFragmentShaderTemplate(DepthBoundsFS);
|
|
@@ -9,7 +9,7 @@ import vtkWebGPUSampler from './Sampler.js';
|
|
|
9
9
|
import vtkWebGPUTypes from './Types.js';
|
|
10
10
|
import { BlendMode } from '../Core/VolumeMapper/Constants.js';
|
|
11
11
|
|
|
12
|
-
var volFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Volume::TraverseDec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\nfn getTextureValue(vTex: texture_3d<f32>, tpos: vec4<f32>) -> f32\n{\n // todo multicomponent support\n return textureSampleLevel(vTex, clampSampler, tpos.xyz, 0.0).r;\n}\n\nfn getGradient(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32, scalar: f32) -> vec4<f32>\n{\n var result: vec4<f32>;\n\n var tstep: vec4<f32> = volumeSSBO.values[vNum].tstep;\n result.x = getTextureValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0)) - scalar;\n result.y = getTextureValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0)) - scalar;\n result.z = getTextureValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0)) - scalar;\n\n // divide by spacing\n result = result / volumeSSBO.values[vNum].spacing;\n\n var grad: f32 = length(result.xyz);\n\n // // rotate to View Coords, needed for lighting and shading\n // result.xyz =\n // result.x * vPlaneNormal0 +\n // result.y * vPlaneNormal2 +\n // result.z * vPlaneNormal4;\n\n if (grad > 0.0)\n {\n result = result * (1.0 / grad);\n }\n\n result.w = grad;\n\n return result;\n}\n\nfn processVolume(vTex: texture_3d<f32>, vNum: i32, cNum: i32, posSC: vec4<f32>, tfunRows: f32) -> vec4<f32>\n{\n var outColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n\n // convert to tcoords and reject if outside the volume\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*posSC;\n if (tpos.x < 0.0 || tpos.y < 0.0 || tpos.z < 0.0 ||\n tpos.x > 1.0 || tpos.y > 1.0 || tpos.z > 1.0) { return outColor; }\n\n var scalar: f32 = getTextureValue(vTex, tpos);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n\n var gofactor: f32 = 1.0;\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\n var normal: vec4<f32> = getGradient(vTex, tpos, vNum, scalar);\n gofactor = clamp(normal.a*componentSSBO.values[cNum].goScale + componentSSBO.values[cNum].goShift,\n componentSSBO.values[cNum].gomin, componentSSBO.values[cNum].gomax);\n }\n\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n\n outColor = vec4<f32>(color.rgb, gofactor * opacity);\n\n//VTK::Volume::Process\n\n return outColor;\n}\n\n// adjust the start and end point of a raycast such that it intersects the unit cube.\n// This function is used to take a raycast starting point and step vector\n// and numSteps and return the startijng and ending steps for intersecting the\n// unit cube. Recall for a 3D texture, the unit cube is the range of texture coordsinates\n// that have valid values. So this funtion can be used to take a ray in texture coordinates\n// and bound it to intersecting the texture.\n//\nfn adjustBounds(tpos: vec4<f32>, tstep: vec4<f32>, numSteps: f32) -> vec2<f32>\n{\n var result: vec2<f32> = vec2<f32>(0.0, numSteps);\n var tpos2: vec4<f32> = tpos + tstep*numSteps;\n\n // move tpos to the start of the volume\n var adjust: f32 =\n min(\n max(tpos.x/tstep.x, (tpos.x - 1.0)/tstep.x),\n min(\n max((tpos.y - 1.0)/tstep.y, tpos.y/tstep.y),\n max((tpos.z - 1.0)/tstep.z, tpos.z/tstep.z)));\n if (adjust < 0.0)\n {\n result.x = result.x - adjust;\n }\n\n // adjust length to the end\n adjust =\n max(\n min(tpos2.x/tstep.x, (tpos2.x - 1.0)/tstep.x),\n max(\n min((tpos2.y - 1.0)/tstep.y, tpos2.y/tstep.y),\n min((tpos2.z - 1.0)/tstep.z, tpos2.z/tstep.z)));\n if (adjust > 0.0)\n {\n result.y = result.y - adjust;\n }\n\n return result;\n}\n\nfn getSimpleColor(scalar: f32, vNum: i32, cNum: i32) -> vec4<f32>\n{\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n return vec4<f32>(color.rgb, opacity);\n}\n\nfn traverseMax(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var maxVal: f32 = -1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar > maxVal)\n {\n maxVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(maxVal, vNum, cNum);\n}\n\nfn traverseMin(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var minVal: f32 = 1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar < minVal)\n {\n minVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(minVal, vNum, cNum);\n}\n\nfn traverseAverage(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var avgVal: f32 = 0.0;\n var sampleCount: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n avgVal = avgVal + sample;\n sampleCount = sampleCount + 1.0;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n if (sampleCount <= 0.0)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(avgVal/sampleCount, vNum, cNum);\n}\n\nfn traverseAdditive(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var sumVal: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n sumVal = sumVal + sample;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(sumVal, vNum, cNum);\n}\n\nfn composite(rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>) -> vec4<f32>\n{\n // initial ray position is at the beginning\n var rayPosSC: vec4<f32> = minPosSC;\n\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var curDist: f32 = 0.0;\n var computedColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n var sampleColor: vec4<f32>;\n//VTK::Volume::TraverseCalls\n\n loop\n {\n // for each volume, sample and accumulate color\n//VTK::Volume::CompositeCalls\n\n // increment position\n curDist = curDist + mapperUBO.SampleDistance;\n rayPosSC = rayPosSC + rayStepSC;\n\n // check if we have reached a terminating condition\n if (curDist > rayLengthSC) { break; }\n if (computedColor.a > 0.98) { break; }\n }\n return computedColor;\n}\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var rayMax: f32 = textureSampleLevel(maxTexture, clampSampler, input.tcoordVS, 0.0).r;\n var rayMin: f32 = textureSampleLevel(minTexture, clampSampler, input.tcoordVS, 0.0).r;\n\n // discard empty rays\n if (rayMax <= rayMin) { discard; }\n else\n {\n var winDimsI32: vec2<i32> = textureDimensions(minTexture);\n var winDims: vec2<f32> = vec2<f32>(f32(winDimsI32.x), f32(winDimsI32.y));\n\n // compute start and end ray positions in view coordinates\n var minPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMax, 1.0);\n minPosSC = minPosSC * (1.0 / minPosSC.w);\n var maxPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0*input.fragPos.x/winDims.x - 1.0, 1.0 - 2.0 * input.fragPos.y/winDims.y, rayMin, 1.0);\n maxPosSC = maxPosSC * (1.0 / maxPosSC.w);\n\n var rayLengthSC: f32 = distance(minPosSC.xyz, maxPosSC.xyz);\n var rayStepSC: vec4<f32> = (maxPosSC - minPosSC)*(mapperUBO.SampleDistance/rayLengthSC);\n rayStepSC.w = 0.0;\n\n var computedColor: vec4<f32>;\n\n//VTK::Volume::Loop\n\n//VTK::RenderEncoder::Impl\n }\n\n return output;\n}\n";
|
|
12
|
+
var volFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Volume::TraverseDec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\nfn getTextureValue(vTex: texture_3d<f32>, tpos: vec4<f32>) -> f32\n{\n // todo multicomponent support\n return textureSampleLevel(vTex, clampSampler, tpos.xyz, 0.0).r;\n}\n\nfn getGradient(vTex: texture_3d<f32>, tpos: vec4<f32>, vNum: i32, scalar: f32) -> vec4<f32>\n{\n var result: vec4<f32>;\n\n var tstep: vec4<f32> = volumeSSBO.values[vNum].tstep;\n result.x = getTextureValue(vTex, tpos + vec4<f32>(tstep.x, 0.0, 0.0, 1.0)) - scalar;\n result.y = getTextureValue(vTex, tpos + vec4<f32>(0.0, tstep.y, 0.0, 1.0)) - scalar;\n result.z = getTextureValue(vTex, tpos + vec4<f32>(0.0, 0.0, tstep.z, 1.0)) - scalar;\n\n // divide by spacing\n result = result / volumeSSBO.values[vNum].spacing;\n\n var grad: f32 = length(result.xyz);\n\n // // rotate to View Coords, needed for lighting and shading\n // result.xyz =\n // result.x * vPlaneNormal0 +\n // result.y * vPlaneNormal2 +\n // result.z * vPlaneNormal4;\n\n if (grad > 0.0)\n {\n result = result * (1.0 / grad);\n }\n\n result.w = grad;\n\n return result;\n}\n\nfn processVolume(vTex: texture_3d<f32>, vNum: i32, cNum: i32, posSC: vec4<f32>, tfunRows: f32) -> vec4<f32>\n{\n var outColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n\n // convert to tcoords and reject if outside the volume\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*posSC;\n if (tpos.x < 0.0 || tpos.y < 0.0 || tpos.z < 0.0 ||\n tpos.x > 1.0 || tpos.y > 1.0 || tpos.z > 1.0) { return outColor; }\n\n var scalar: f32 = getTextureValue(vTex, tpos);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n\n var gofactor: f32 = 1.0;\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\n var normal: vec4<f32> = getGradient(vTex, tpos, vNum, scalar);\n gofactor = clamp(normal.a*componentSSBO.values[cNum].goScale + componentSSBO.values[cNum].goShift,\n componentSSBO.values[cNum].gomin, componentSSBO.values[cNum].gomax);\n }\n\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n\n outColor = vec4<f32>(color.rgb, gofactor * opacity);\n\n//VTK::Volume::Process\n\n return outColor;\n}\n\n// adjust the start and end point of a raycast such that it intersects the unit cube.\n// This function is used to take a raycast starting point and step vector\n// and numSteps and return the startijng and ending steps for intersecting the\n// unit cube. Recall for a 3D texture, the unit cube is the range of texture coordsinates\n// that have valid values. So this funtion can be used to take a ray in texture coordinates\n// and bound it to intersecting the texture.\n//\nfn adjustBounds(tpos: vec4<f32>, tstep: vec4<f32>, numSteps: f32) -> vec2<f32>\n{\n var result: vec2<f32> = vec2<f32>(0.0, numSteps);\n var tpos2: vec4<f32> = tpos + tstep*numSteps;\n\n // move tpos to the start of the volume\n var adjust: f32 =\n min(\n max(tpos.x/tstep.x, (tpos.x - 1.0)/tstep.x),\n min(\n max((tpos.y - 1.0)/tstep.y, tpos.y/tstep.y),\n max((tpos.z - 1.0)/tstep.z, tpos.z/tstep.z)));\n if (adjust < 0.0)\n {\n result.x = result.x - adjust;\n }\n\n // adjust length to the end\n adjust =\n max(\n min(tpos2.x/tstep.x, (tpos2.x - 1.0)/tstep.x),\n max(\n min((tpos2.y - 1.0)/tstep.y, tpos2.y/tstep.y),\n min((tpos2.z - 1.0)/tstep.z, tpos2.z/tstep.z)));\n if (adjust > 0.0)\n {\n result.y = result.y - adjust;\n }\n\n return result;\n}\n\nfn getSimpleColor(scalar: f32, vNum: i32, cNum: i32) -> vec4<f32>\n{\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var coord: vec2<f32> =\n vec2<f32>(scalar * componentSSBO.values[cNum].cScale + componentSSBO.values[cNum].cShift,\n (0.5 + 2.0 * f32(vNum)) / tfunRows);\n var color: vec4<f32> = textureSampleLevel(tfunTexture, clampSampler, coord, 0.0);\n coord.x = (scalar * componentSSBO.values[cNum].oScale + componentSSBO.values[cNum].oShift);\n var opacity: f32 = textureSampleLevel(ofunTexture, clampSampler, coord, 0.0).r;\n return vec4<f32>(color.rgb, opacity);\n}\n\nfn traverseMax(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var maxVal: f32 = -1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar > maxVal)\n {\n maxVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(maxVal, vNum, cNum);\n}\n\nfn traverseMin(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var minVal: f32 = 1.0e37;\n loop\n {\n var scalar: f32 = getTextureValue(vTex, tpos);\n if (scalar < minVal)\n {\n minVal = scalar;\n }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(minVal, vNum, cNum);\n}\n\nfn traverseAverage(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var avgVal: f32 = 0.0;\n var sampleCount: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n avgVal = avgVal + sample;\n sampleCount = sampleCount + 1.0;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n if (sampleCount <= 0.0)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(avgVal/sampleCount, vNum, cNum);\n}\n\nfn traverseAdditive(vTex: texture_3d<f32>, vNum: i32, cNum: i32, rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>)\n{\n // convert to tcoords and reject if outside the volume\n var numSteps: f32 = rayLengthSC/mapperUBO.SampleDistance;\n var tpos: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*minPosSC;\n var tpos2: vec4<f32> = volumeSSBO.values[vNum].SCTCMatrix*(minPosSC + rayStepSC);\n var tstep: vec4<f32> = tpos2 - tpos;\n\n var rayBounds: vec2<f32> = adjustBounds(tpos, tstep, numSteps);\n\n // did we hit anything\n if (rayBounds.x >= rayBounds.y)\n {\n traverseVals[vNum] = vec4<f32>(0.0,0.0,0.0,0.0);\n return;\n }\n\n let ipRange: vec4<f32> = volumeSSBO.values[vNum].ipScalarRange;\n tpos = tpos + tstep*rayBounds.x;\n var curDist: f32 = rayBounds.x;\n var sumVal: f32 = 0.0;\n loop\n {\n var sample: f32 = getTextureValue(vTex, tpos);\n // right now leave filtering off until WebGL changes get merged\n // if (ipRange.z == 0.0 || sample >= ipRange.x && sample <= ipRange.y)\n // {\n sumVal = sumVal + sample;\n // }\n\n // increment position\n curDist = curDist + 1.0;\n tpos = tpos + tstep;\n\n // check if we have reached a terminating condition\n if (curDist > rayBounds.y) { break; }\n }\n\n // process to get the color and opacity\n traverseVals[vNum] = getSimpleColor(sumVal, vNum, cNum);\n}\n\nfn composite(rayLengthSC: f32, minPosSC: vec4<f32>, rayStepSC: vec4<f32>) -> vec4<f32>\n{\n // initial ray position is at the beginning\n var rayPosSC: vec4<f32> = minPosSC;\n\n // how many rows (tfuns) do we have in our tfunTexture\n var tfunRows: f32 = f32(textureDimensions(tfunTexture).y);\n\n var curDist: f32 = 0.0;\n var computedColor: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n var sampleColor: vec4<f32>;\n//VTK::Volume::TraverseCalls\n\n loop\n {\n // for each volume, sample and accumulate color\n//VTK::Volume::CompositeCalls\n\n // increment position\n curDist = curDist + mapperUBO.SampleDistance;\n rayPosSC = rayPosSC + rayStepSC;\n\n // check if we have reached a terminating condition\n if (curDist > rayLengthSC) { break; }\n if (computedColor.a > 0.98) { break; }\n }\n return computedColor;\n}\n\n[[stage(fragment)]]\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var rayMax: f32 = textureSampleLevel(maxTexture, clampSampler, input.tcoordVS, 0.0).r;\n var rayMin: f32 = textureSampleLevel(minTexture, clampSampler, input.tcoordVS, 0.0).r;\n\n // discard empty rays\n if (rayMax <= rayMin) { discard; }\n else\n {\n // compute start and end ray positions in view coordinates\n var minPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0 * input.tcoordVS.x - 1.0, 1.0 - 2.0 * input.tcoordVS.y, rayMax, 1.0);\n minPosSC = minPosSC * (1.0 / minPosSC.w);\n var maxPosSC: vec4<f32> = rendererUBO.PCSCMatrix*vec4<f32>(2.0 * input.tcoordVS.x - 1.0, 1.0 - 2.0 * input.tcoordVS.y, rayMin, 1.0);\n maxPosSC = maxPosSC * (1.0 / maxPosSC.w);\n\n var rayLengthSC: f32 = distance(minPosSC.xyz, maxPosSC.xyz);\n var rayStepSC: vec4<f32> = (maxPosSC - minPosSC)*(mapperUBO.SampleDistance/rayLengthSC);\n rayStepSC.w = 0.0;\n\n var computedColor: vec4<f32>;\n\n//VTK::Volume::Loop\n\n//VTK::RenderEncoder::Impl\n }\n\n return output;\n}\n";
|
|
13
13
|
var tmpMat4 = new Float64Array(16);
|
|
14
14
|
var tmp2Mat4 = new Float64Array(16); // ----------------------------------------------------------------------------
|
|
15
15
|
// vtkWebGPUVolumePassFSQ methods
|