@kitware/vtk.js 22.0.1 → 22.1.2
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.
- package/Common/Core/Math/index.js +1 -1
- package/Common/Core/Math.js +1 -1
- package/Common/DataModel/ITKHelper.js +1 -1
- package/Common/DataModel/ImageData.js +1 -1
- package/Common/DataModel/Line.js +1 -1
- package/Common/DataModel/Plane.js +1 -1
- package/Common/DataModel/Polygon.js +1 -1
- package/Common/DataModel/Triangle.js +1 -1
- package/Common/Transform/LandmarkTransform.js +1 -1
- package/Filters/General/ImageMarchingCubes.js +1 -1
- package/Filters/General/MoleculeToRepresentation.js +1 -1
- package/Filters/General/TubeFilter.js +1 -1
- package/Filters/General/WindowedSincPolyDataFilter.js +1 -1
- package/Filters/Sources/CircleSource.js +1 -1
- package/Filters/Sources/LineSource.js +1 -1
- package/Filters/Sources/PlaneSource.js +1 -1
- package/Filters/Sources/PointSource.js +1 -1
- package/Filters/Texture/TextureMapToPlane.js +1 -1
- package/Filters/Texture/TextureMapToSphere.js +1 -1
- package/Interaction/Manipulators/KeyboardCameraManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraAxisRotateManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraTrackballPanManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraTrackballRotateManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraUnicamManipulator.js +1 -1
- package/Interaction/Manipulators/MouseCameraUnicamRotateManipulator.js +1 -1
- package/Interaction/Style/InteractorStyleMPRSlice.js +1 -1
- package/Interaction/Widgets/DistanceRepresentation.js +1 -1
- package/Interaction/Widgets/ImageCroppingRegionsRepresentation.js +1 -1
- package/Interaction/Widgets/ImageCroppingRegionsWidget.js +1 -1
- package/Interaction/Widgets/LabelRepresentation.js +1 -1
- package/Interaction/Widgets/ResliceCursor/ResliceCursor.js +1 -1
- package/Interaction/Widgets/ResliceCursor/ResliceCursorLineRepresentation.js +1 -1
- package/Interaction/Widgets/ResliceCursor/ResliceCursorRepresentation.js +1 -1
- package/Interaction/Widgets/SphereHandleRepresentation.js +1 -1
- package/Proxy/Core/View2DProxy.js +1 -1
- package/Rendering/Core/AnnotatedCubeActor.d.ts +3 -2
- package/Rendering/Core/Camera.js +1 -1
- package/Rendering/Core/CellPicker.js +1 -1
- package/Rendering/Core/Light.js +1 -1
- package/Rendering/Core/Picker.js +1 -1
- package/Rendering/Core/RenderWindowInteractor.js +27 -5
- package/Rendering/OpenGL/PolyDataMapper.js +1 -1
- package/Rendering/WebGPU/BufferManager.js +1 -1
- package/Rendering/WebGPU/VolumePass.js +332 -73
- package/Rendering/WebGPU/VolumePassFSQ.js +1 -1
- package/Widgets/Core/StateBuilder/orientationMixin.js +1 -1
- package/Widgets/Manipulators/LineManipulator.js +1 -1
- package/Widgets/Manipulators/TrackballManipulator.js +1 -1
- package/Widgets/Representations/PolyLineRepresentation.js +1 -1
- package/Widgets/Representations/ResliceCursorContextRepresentation.js +1 -1
- package/Widgets/Representations/WidgetRepresentation.js +1 -1
- package/Widgets/Widgets3D/AngleWidget.js +1 -1
- package/Widgets/Widgets3D/DistanceWidget.js +1 -1
- package/Widgets/Widgets3D/LineWidget/behavior.js +1 -1
- package/Widgets/Widgets3D/LineWidget/helpers.js +1 -1
- package/Widgets/Widgets3D/LineWidget.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +1 -1
- package/Widgets/Widgets3D/ResliceCursorWidget.js +1 -1
- package/Widgets/Widgets3D/ShapeWidget/behavior.js +1 -1
- package/macros.js +14 -30
- package/package.json +1 -1
|
@@ -8,8 +8,9 @@ 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 vtkWebGPUFullScreenQuad from './FullScreenQuad.js';
|
|
11
12
|
import vtkWebGPUVolumePassFSQ from './VolumePassFSQ.js';
|
|
12
|
-
import {
|
|
13
|
+
import { b as distance2BetweenPoints } from '../../Common/Core/Math/index.js';
|
|
13
14
|
|
|
14
15
|
var Representation = vtkProperty.Representation;
|
|
15
16
|
var BufferUsage = vtkWebGPUBufferManager.BufferUsage,
|
|
@@ -32,6 +33,7 @@ var BufferUsage = vtkWebGPUBufferManager.BufferUsage,
|
|
|
32
33
|
|
|
33
34
|
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
35
|
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";
|
|
36
|
+
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, input.tcoordVS);\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
|
|
35
37
|
/* eslint-disable no-undef */
|
|
36
38
|
|
|
37
39
|
/* eslint-disable no-bitwise */
|
|
@@ -39,7 +41,41 @@ var DepthBoundsFS = "\n//VTK::Renderer::Dec\n\n//VTK::Select::Dec\n\n//VTK::Volu
|
|
|
39
41
|
|
|
40
42
|
function vtkWebGPUVolumePass(publicAPI, model) {
|
|
41
43
|
// Set our className
|
|
42
|
-
model.classHierarchy.push('vtkWebGPUVolumePass');
|
|
44
|
+
model.classHierarchy.push('vtkWebGPUVolumePass'); // create the required textures, encoders, FSQ etc
|
|
45
|
+
|
|
46
|
+
publicAPI.initialize = function (viewNode) {
|
|
47
|
+
if (!model._mergeEncoder) {
|
|
48
|
+
publicAPI.createMergeEncoder(viewNode);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!model._clearEncoder) {
|
|
52
|
+
publicAPI.createClearEncoder(viewNode);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!model._copyEncoder) {
|
|
56
|
+
publicAPI.createCopyEncoder(viewNode);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!model._depthRangeEncoder) {
|
|
60
|
+
publicAPI.createDepthRangeEncoder(viewNode);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!model.fullScreenQuad) {
|
|
64
|
+
model.fullScreenQuad = vtkWebGPUVolumePassFSQ.newInstance();
|
|
65
|
+
model.fullScreenQuad.setDevice(viewNode.getDevice());
|
|
66
|
+
model.fullScreenQuad.setTextureViews(_toConsumableArray(model._depthRangeEncoder.getColorTextureViews()));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!model._volumeCopyQuadQuad) {
|
|
70
|
+
model._volumeCopyQuad = vtkWebGPUFullScreenQuad.newInstance();
|
|
71
|
+
|
|
72
|
+
model._volumeCopyQuad.setPipelineHash('volpassfsq');
|
|
73
|
+
|
|
74
|
+
model._volumeCopyQuad.setDevice(viewNode.getDevice());
|
|
75
|
+
|
|
76
|
+
model._volumeCopyQuad.setFragmentShaderTemplate(volumeCopyFragTemplate);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
43
79
|
|
|
44
80
|
publicAPI.traverse = function (renNode, viewNode) {
|
|
45
81
|
if (model.deleted) {
|
|
@@ -47,29 +83,25 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
47
83
|
} // we just render our delegates in order
|
|
48
84
|
|
|
49
85
|
|
|
50
|
-
model.currentParent = viewNode; //
|
|
51
|
-
|
|
52
|
-
if (!model.finalEncoder) {
|
|
53
|
-
publicAPI.createFinalEncoder(viewNode);
|
|
54
|
-
} // first render the boxes to generate a min max depth
|
|
55
|
-
// map for all the volumes
|
|
86
|
+
model.currentParent = viewNode; // create stuff we need
|
|
56
87
|
|
|
88
|
+
publicAPI.initialize(viewNode); // determine if we are rendering a small size
|
|
57
89
|
|
|
58
|
-
publicAPI.
|
|
90
|
+
publicAPI.computeTiming(viewNode); // first render the boxes to generate a min max depth
|
|
91
|
+
// map for all the volumes
|
|
59
92
|
|
|
60
|
-
|
|
61
|
-
model.fullScreenQuad = vtkWebGPUVolumePassFSQ.newInstance();
|
|
62
|
-
model.fullScreenQuad.setDevice(viewNode.getDevice());
|
|
63
|
-
model.fullScreenQuad.setTextureViews(_toConsumableArray(model.depthRangeEncoder.getColorTextureViews()));
|
|
64
|
-
}
|
|
93
|
+
publicAPI.renderDepthBounds(renNode, viewNode); // always mark true
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
model._firstGroup = true;
|
|
96
|
+
var device = viewNode.getDevice(); // determine how many volumes we can render at a time. We subtract
|
|
97
|
+
// 4 because we use know we use textures for min, max, ofun and tfun
|
|
67
98
|
|
|
68
|
-
var maxVolumes = device.getHandle().limits.maxSampledTexturesPerShaderStage - 4;
|
|
69
|
-
|
|
99
|
+
var maxVolumes = device.getHandle().limits.maxSampledTexturesPerShaderStage - 4; // if we have to make multiple passes then break the volumes up into groups
|
|
100
|
+
// rendered from farthest to closest
|
|
70
101
|
|
|
71
102
|
if (model.volumes.length > maxVolumes) {
|
|
72
|
-
// sort from back to front based on volume centroid
|
|
103
|
+
var cameraPos = renNode.getRenderable().getActiveCamera().getPosition(); // sort from back to front based on volume centroid
|
|
104
|
+
|
|
73
105
|
var distances = [];
|
|
74
106
|
|
|
75
107
|
for (var v = 0; v < model.volumes.length; v++) {
|
|
@@ -94,26 +126,116 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
94
126
|
volumesToRender.push(model.volumes[volumeOrder[_v]]);
|
|
95
127
|
|
|
96
128
|
if (volumesToRender.length >= chunkSize) {
|
|
97
|
-
publicAPI.
|
|
129
|
+
publicAPI.rayCastPass(viewNode, renNode, volumesToRender);
|
|
98
130
|
volumesToRender = [];
|
|
99
131
|
chunkSize = maxVolumes;
|
|
132
|
+
model._firstGroup = false;
|
|
100
133
|
}
|
|
101
134
|
}
|
|
102
135
|
} else {
|
|
103
|
-
|
|
136
|
+
// if not rendering in chunks then just draw all of them at once
|
|
137
|
+
publicAPI.rayCastPass(viewNode, renNode, model.volumes);
|
|
138
|
+
} // copy back to the original color buffer
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if (model.small) {
|
|
142
|
+
model._volumeCopyQuad.setTextureViews([model._smallColorTextureView]);
|
|
143
|
+
} else {
|
|
144
|
+
model._volumeCopyQuad.setTextureViews([model._largeColorTextureView]);
|
|
145
|
+
} // final composite
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
model._copyEncoder.setColorTextureView(0, model.colorTextureView);
|
|
149
|
+
|
|
150
|
+
model._copyEncoder.attachTextureViews();
|
|
151
|
+
|
|
152
|
+
renNode.setRenderEncoder(model._copyEncoder);
|
|
153
|
+
|
|
154
|
+
model._copyEncoder.begin(viewNode.getCommandEncoder());
|
|
155
|
+
|
|
156
|
+
renNode.scissorAndViewport(model._copyEncoder);
|
|
157
|
+
|
|
158
|
+
model._volumeCopyQuad.setWebGPURenderer(renNode);
|
|
159
|
+
|
|
160
|
+
model._volumeCopyQuad.render(model._copyEncoder, viewNode.getDevice());
|
|
161
|
+
|
|
162
|
+
model._copyEncoder.end();
|
|
163
|
+
}; // unsubscribe from our listeners
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
publicAPI.delete = macro.chain(function () {
|
|
167
|
+
if (model._animationRateSubscription) {
|
|
168
|
+
model._animationRateSubscription.unsubscribe();
|
|
169
|
+
|
|
170
|
+
model._animationRateSubscription = null;
|
|
171
|
+
}
|
|
172
|
+
}, publicAPI.delete);
|
|
173
|
+
|
|
174
|
+
publicAPI.computeTiming = function (viewNode) {
|
|
175
|
+
model.small = false;
|
|
176
|
+
var rwi = viewNode.getRenderable().getInteractor();
|
|
177
|
+
|
|
178
|
+
if (rwi.isAnimating() && model._lastScale > 1.5) {
|
|
179
|
+
model.small = true;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (model.small) {
|
|
183
|
+
model._activeColorTextureView = model._smallColorTextureView;
|
|
184
|
+
} else {
|
|
185
|
+
model._largeColorTexture.resize(viewNode.getCanvas().width, viewNode.getCanvas().height);
|
|
186
|
+
|
|
187
|
+
model._activeColorTextureView = model._largeColorTextureView;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!model._animationRateSubscription) {
|
|
191
|
+
// when the animation frame rate changes recompute the scale factor
|
|
192
|
+
model._animationRateSubscription = rwi.onAnimationFrameRateUpdate(function () {
|
|
193
|
+
var frate = rwi.getRecentAnimationFrameRate();
|
|
194
|
+
var targetScale = model._lastScale * rwi.getDesiredUpdateRate() / frate; // Do we need a resize? The values below are to provide some stickyness
|
|
195
|
+
// so that we are not changing texture size every second. Instead the targhet scale
|
|
196
|
+
// has to be a certain amount larger or smaller than the current value to force a
|
|
197
|
+
// change in texture size
|
|
198
|
+
|
|
199
|
+
if (targetScale > 1.4 * model._lastScale || targetScale < 0.7 * model._lastScale) {
|
|
200
|
+
model._lastScale = targetScale; // clamp scale to some reasonable values.
|
|
201
|
+
// Below 1.5 we will just be using full resolution as that is close enough
|
|
202
|
+
// Above 400 seems like a lot so we limit to that 1/20th per axis
|
|
203
|
+
|
|
204
|
+
if (model._lastScale > 400) {
|
|
205
|
+
model._lastScale = 400;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (model._lastScale < 1.5) {
|
|
209
|
+
model._lastScale = 1.5;
|
|
210
|
+
} else {
|
|
211
|
+
var targetSmallWidth = 64 * Math.ceil(viewNode.getCanvas().width / (Math.sqrt(model._lastScale) * 64));
|
|
212
|
+
var targetSmallHeight = Math.ceil(targetSmallWidth * viewNode.getCanvas().height / viewNode.getCanvas().width);
|
|
213
|
+
|
|
214
|
+
model._smallColorTexture.resize(targetSmallWidth, targetSmallHeight);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
});
|
|
104
218
|
}
|
|
105
219
|
};
|
|
106
220
|
|
|
107
|
-
publicAPI.
|
|
108
|
-
model.
|
|
109
|
-
model.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
221
|
+
publicAPI.rayCastPass = function (viewNode, renNode, volumes) {
|
|
222
|
+
var encoder = model._firstGroup ? model._clearEncoder : model._mergeEncoder;
|
|
223
|
+
encoder.setColorTextureView(0, model._activeColorTextureView);
|
|
224
|
+
encoder.attachTextureViews();
|
|
225
|
+
renNode.setRenderEncoder(encoder);
|
|
226
|
+
encoder.begin(viewNode.getCommandEncoder());
|
|
227
|
+
|
|
228
|
+
var width = model._activeColorTextureView.getTexture().getWidth();
|
|
229
|
+
|
|
230
|
+
var height = model._activeColorTextureView.getTexture().getHeight();
|
|
231
|
+
|
|
232
|
+
encoder.getHandle().setViewport(0, 0, width, height, 0.0, 1.0); // set scissor
|
|
233
|
+
|
|
234
|
+
encoder.getHandle().setScissorRect(0, 0, width, height);
|
|
113
235
|
model.fullScreenQuad.setWebGPURenderer(renNode);
|
|
114
236
|
model.fullScreenQuad.setVolumes(volumes);
|
|
115
|
-
model.fullScreenQuad.render(
|
|
116
|
-
|
|
237
|
+
model.fullScreenQuad.render(encoder, viewNode.getDevice());
|
|
238
|
+
encoder.end();
|
|
117
239
|
};
|
|
118
240
|
|
|
119
241
|
publicAPI.renderDepthBounds = function (renNode, viewNode) {
|
|
@@ -211,55 +333,32 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
211
333
|
publicAPI.drawDepthRange = function (renNode, viewNode) {
|
|
212
334
|
var device = viewNode.getDevice(); // copy current depth buffer to
|
|
213
335
|
|
|
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);
|
|
336
|
+
model._depthRangeTexture.resizeToMatch(model.colorTextureView.getTexture());
|
|
236
337
|
|
|
237
|
-
|
|
338
|
+
model._depthRangeTexture2.resizeToMatch(model.colorTextureView.getTexture());
|
|
238
339
|
|
|
239
|
-
|
|
240
|
-
} else {
|
|
241
|
-
model.depthRangeTexture.resizeToMatch(model.colorTextureView.getTexture());
|
|
242
|
-
model.depthRangeTexture2.resizeToMatch(model.colorTextureView.getTexture());
|
|
243
|
-
}
|
|
340
|
+
model._depthRangeEncoder.attachTextureViews();
|
|
244
341
|
|
|
245
|
-
model.depthRangeEncoder.attachTextureViews();
|
|
246
342
|
publicAPI.setCurrentOperation('volumeDepthRangePass');
|
|
247
|
-
renNode.setRenderEncoder(model.
|
|
343
|
+
renNode.setRenderEncoder(model._depthRangeEncoder);
|
|
248
344
|
renNode.volumeDepthRangePass(true);
|
|
249
345
|
|
|
250
346
|
model._mapper.setWebGPURenderer(renNode);
|
|
251
347
|
|
|
252
|
-
model._mapper.build(model.
|
|
348
|
+
model._mapper.build(model._depthRangeEncoder, device);
|
|
253
349
|
|
|
254
350
|
model._mapper.registerToDraw();
|
|
255
351
|
|
|
256
352
|
renNode.volumeDepthRangePass(false);
|
|
257
353
|
};
|
|
258
354
|
|
|
259
|
-
publicAPI.createDepthRangeEncoder = function () {
|
|
260
|
-
|
|
261
|
-
model.
|
|
262
|
-
|
|
355
|
+
publicAPI.createDepthRangeEncoder = function (viewNode) {
|
|
356
|
+
var device = viewNode.getDevice();
|
|
357
|
+
model._depthRangeEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
358
|
+
|
|
359
|
+
model._depthRangeEncoder.setPipelineHash('volr');
|
|
360
|
+
|
|
361
|
+
model._depthRangeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
|
|
263
362
|
var fDesc = pipeline.getShaderDescription('fragment');
|
|
264
363
|
fDesc.addOutput('vec4<f32>', 'outColor1');
|
|
265
364
|
fDesc.addOutput('vec4<f32>', 'outColor2');
|
|
@@ -267,7 +366,8 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
267
366
|
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
367
|
fDesc.setCode(code);
|
|
269
368
|
});
|
|
270
|
-
|
|
369
|
+
|
|
370
|
+
model._depthRangeEncoder.setDescription({
|
|
271
371
|
colorAttachments: [{
|
|
272
372
|
view: null,
|
|
273
373
|
loadValue: [0.0, 0.0, 0.0, 0.0],
|
|
@@ -278,7 +378,8 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
278
378
|
storeOp: 'store'
|
|
279
379
|
}]
|
|
280
380
|
});
|
|
281
|
-
|
|
381
|
+
|
|
382
|
+
model._depthRangeEncoder.setPipelineSettings({
|
|
282
383
|
primitive: {
|
|
283
384
|
cullMode: 'none'
|
|
284
385
|
},
|
|
@@ -313,27 +414,184 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
313
414
|
}
|
|
314
415
|
}]
|
|
315
416
|
}
|
|
417
|
+
}); // and the textures it needs
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
model._depthRangeTexture = vtkWebGPUTexture.newInstance();
|
|
421
|
+
|
|
422
|
+
model._depthRangeTexture.create(device, {
|
|
423
|
+
width: viewNode.getCanvas().width,
|
|
424
|
+
height: viewNode.getCanvas().height,
|
|
425
|
+
format: 'r16float',
|
|
426
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
var maxView = model._depthRangeTexture.createView();
|
|
430
|
+
|
|
431
|
+
maxView.setName('maxTexture');
|
|
432
|
+
|
|
433
|
+
model._depthRangeEncoder.setColorTextureView(0, maxView);
|
|
434
|
+
|
|
435
|
+
model._depthRangeTexture2 = vtkWebGPUTexture.newInstance();
|
|
436
|
+
|
|
437
|
+
model._depthRangeTexture2.create(device, {
|
|
438
|
+
width: viewNode.getCanvas().width,
|
|
439
|
+
height: viewNode.getCanvas().height,
|
|
440
|
+
format: 'r16float',
|
|
441
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
var minView = model._depthRangeTexture2.createView();
|
|
445
|
+
|
|
446
|
+
minView.setName('minTexture');
|
|
447
|
+
|
|
448
|
+
model._depthRangeEncoder.setColorTextureView(1, minView);
|
|
449
|
+
|
|
450
|
+
model._mapper.setDevice(viewNode.getDevice());
|
|
451
|
+
|
|
452
|
+
model._mapper.setTextureViews([model.depthTextureView]);
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
publicAPI.createClearEncoder = function (viewNode) {
|
|
456
|
+
model._smallColorTexture = vtkWebGPUTexture.newInstance(); // multiple of 64 pixels to help with texture alignment/size issues
|
|
457
|
+
// as webgpu textures have to be multiples of 256 bytes wide
|
|
458
|
+
// for data transfers (just in case)
|
|
459
|
+
|
|
460
|
+
var targetSmallWidth = 64 * Math.ceil(0.7 * viewNode.getCanvas().width / 64);
|
|
461
|
+
|
|
462
|
+
model._smallColorTexture.create(viewNode.getDevice(), {
|
|
463
|
+
width: targetSmallWidth,
|
|
464
|
+
height: Math.ceil(targetSmallWidth * viewNode.getCanvas().height / viewNode.getCanvas().width),
|
|
465
|
+
format: 'bgra8unorm',
|
|
466
|
+
|
|
467
|
+
/* eslint-disable no-undef */
|
|
468
|
+
|
|
469
|
+
/* eslint-disable no-bitwise */
|
|
470
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
model._smallColorTextureView = model._smallColorTexture.createView();
|
|
474
|
+
|
|
475
|
+
model._smallColorTextureView.setName('volumePassSmallColorTexture');
|
|
476
|
+
|
|
477
|
+
model._smallColorTextureView.addSampler(viewNode.getDevice(), {
|
|
478
|
+
minFilter: 'linear',
|
|
479
|
+
magFilter: 'linear'
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
model._largeColorTexture = vtkWebGPUTexture.newInstance();
|
|
483
|
+
|
|
484
|
+
model._largeColorTexture.create(viewNode.getDevice(), {
|
|
485
|
+
width: viewNode.getCanvas().width,
|
|
486
|
+
height: viewNode.getCanvas().height,
|
|
487
|
+
format: 'bgra8unorm',
|
|
488
|
+
|
|
489
|
+
/* eslint-disable no-undef */
|
|
490
|
+
|
|
491
|
+
/* eslint-disable no-bitwise */
|
|
492
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
model._largeColorTextureView = model._largeColorTexture.createView();
|
|
496
|
+
|
|
497
|
+
model._largeColorTextureView.setName('volumePassSmallColorTexture');
|
|
498
|
+
|
|
499
|
+
model._largeColorTextureView.addSampler(viewNode.getDevice(), {
|
|
500
|
+
minFilter: 'linear',
|
|
501
|
+
magFilter: 'linear'
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
model._clearEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
505
|
+
|
|
506
|
+
model._clearEncoder.setDescription({
|
|
507
|
+
colorAttachments: [{
|
|
508
|
+
view: null,
|
|
509
|
+
loadValue: [0.0, 0.0, 0.0, 0.0],
|
|
510
|
+
storeOp: 'store'
|
|
511
|
+
}]
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
model._clearEncoder.setPipelineHash('volpf');
|
|
515
|
+
|
|
516
|
+
model._clearEncoder.setPipelineSettings({
|
|
517
|
+
primitive: {
|
|
518
|
+
cullMode: 'none'
|
|
519
|
+
},
|
|
520
|
+
fragment: {
|
|
521
|
+
targets: [{
|
|
522
|
+
format: 'bgra8unorm',
|
|
523
|
+
blend: {
|
|
524
|
+
color: {
|
|
525
|
+
srcFactor: 'src-alpha',
|
|
526
|
+
dstFactor: 'one-minus-src-alpha'
|
|
527
|
+
},
|
|
528
|
+
alpha: {
|
|
529
|
+
srcfactor: 'one',
|
|
530
|
+
dstFactor: 'one-minus-src-alpha'
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}]
|
|
534
|
+
}
|
|
316
535
|
});
|
|
317
536
|
};
|
|
318
537
|
|
|
319
|
-
publicAPI.
|
|
320
|
-
model.
|
|
321
|
-
|
|
538
|
+
publicAPI.createCopyEncoder = function (viewNode) {
|
|
539
|
+
model._copyEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
540
|
+
|
|
541
|
+
model._copyEncoder.setDescription({
|
|
322
542
|
colorAttachments: [{
|
|
323
543
|
view: null,
|
|
324
544
|
loadValue: 'load',
|
|
325
545
|
storeOp: 'store'
|
|
326
546
|
}]
|
|
327
547
|
});
|
|
328
|
-
|
|
548
|
+
|
|
549
|
+
model._copyEncoder.setPipelineHash('volcopypf');
|
|
550
|
+
|
|
551
|
+
model._copyEncoder.setPipelineSettings({
|
|
552
|
+
primitive: {
|
|
553
|
+
cullMode: 'none'
|
|
554
|
+
},
|
|
555
|
+
fragment: {
|
|
556
|
+
targets: [{
|
|
557
|
+
format: 'bgra8unorm',
|
|
558
|
+
blend: {
|
|
559
|
+
color: {
|
|
560
|
+
srcFactor: 'one',
|
|
561
|
+
dstFactor: 'one-minus-src-alpha'
|
|
562
|
+
},
|
|
563
|
+
alpha: {
|
|
564
|
+
srcfactor: 'one',
|
|
565
|
+
dstFactor: 'one-minus-src-alpha'
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}]
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
publicAPI.createMergeEncoder = function (viewNode) {
|
|
574
|
+
model._mergeEncoder = vtkWebGPURenderEncoder.newInstance();
|
|
575
|
+
|
|
576
|
+
model._mergeEncoder.setDescription({
|
|
577
|
+
colorAttachments: [{
|
|
578
|
+
view: null,
|
|
579
|
+
loadValue: 'load',
|
|
580
|
+
storeOp: 'store'
|
|
581
|
+
}]
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
model._mergeEncoder.setReplaceShaderCodeFunction(function (pipeline) {
|
|
329
585
|
var fDesc = pipeline.getShaderDescription('fragment');
|
|
330
586
|
fDesc.addOutput('vec4<f32>', 'outColor');
|
|
331
587
|
var code = fDesc.getCode();
|
|
332
|
-
code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb
|
|
588
|
+
code = vtkWebGPUShaderCache.substitute(code, '//VTK::RenderEncoder::Impl', ['output.outColor = vec4<f32>(computedColor.rgb, computedColor.a);']).result;
|
|
333
589
|
fDesc.setCode(code);
|
|
334
590
|
});
|
|
335
|
-
|
|
336
|
-
model.
|
|
591
|
+
|
|
592
|
+
model._mergeEncoder.setPipelineHash('volpf');
|
|
593
|
+
|
|
594
|
+
model._mergeEncoder.setPipelineSettings({
|
|
337
595
|
primitive: {
|
|
338
596
|
cullMode: 'none'
|
|
339
597
|
},
|
|
@@ -342,7 +600,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
342
600
|
format: 'bgra8unorm',
|
|
343
601
|
blend: {
|
|
344
602
|
color: {
|
|
345
|
-
srcFactor: '
|
|
603
|
+
srcFactor: 'src-alpha',
|
|
346
604
|
dstFactor: 'one-minus-src-alpha'
|
|
347
605
|
},
|
|
348
606
|
alpha: {
|
|
@@ -387,6 +645,7 @@ function extend(publicAPI, model) {
|
|
|
387
645
|
Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API
|
|
388
646
|
|
|
389
647
|
vtkRenderPass.extend(publicAPI, model, initialValues);
|
|
648
|
+
model._lastScale = 2.0;
|
|
390
649
|
model._mapper = vtkWebGPUMapperHelper.newInstance();
|
|
391
650
|
|
|
392
651
|
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
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import macro from '../../../macros.js';
|
|
2
|
-
import {
|
|
2
|
+
import { g as normalize, e as cross } from '../../../Common/Core/Math/index.js';
|
|
3
3
|
|
|
4
4
|
function eq(v1, v2) {
|
|
5
5
|
return v1.length === 3 && v2.length === 3 && v1[0] === v2[0] && v1[1] === v2[1] && v1[2] === v2[2];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import macro from '../../macros.js';
|
|
2
|
-
import {
|
|
2
|
+
import { c as subtract, e as cross, d as dot, m as multiplyScalar, f as add } from '../../Common/Core/Math/index.js';
|
|
3
3
|
|
|
4
4
|
function projectDisplayToLine(x, y, lineOrigin, lineDirection, renderer, glRenderWindow) {
|
|
5
5
|
var near = glRenderWindow.displayToWorld(x, y, 0, renderer);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mat4, vec3 } from 'gl-matrix';
|
|
2
2
|
import macro from '../../macros.js';
|
|
3
|
-
import {
|
|
3
|
+
import { e as cross, r as radiansFromDegrees } from '../../Common/Core/Math/index.js';
|
|
4
4
|
|
|
5
5
|
function trackballRotate(prevX, prevY, curX, curY, origin, direction, renderer, glRenderWindow) {
|
|
6
6
|
var dx = curX - prevX;
|
|
@@ -2,7 +2,7 @@ import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
|
2
2
|
import macro from '../../macros.js';
|
|
3
3
|
import vtkActor from '../../Rendering/Core/Actor.js';
|
|
4
4
|
import vtkMapper from '../../Rendering/Core/Mapper.js';
|
|
5
|
-
import { z as areEquals,
|
|
5
|
+
import { z as areEquals, b as distance2BetweenPoints } from '../../Common/Core/Math/index.js';
|
|
6
6
|
import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
|
|
7
7
|
import vtkPolyData from '../../Common/DataModel/PolyData.js';
|
|
8
8
|
import vtkTubeFilter from '../../Filters/General/TubeFilter.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
2
2
|
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
3
3
|
import macro from '../../macros.js';
|
|
4
|
-
import {
|
|
4
|
+
import { c as subtract, Q as multiplyAccumulate, g as normalize, e as cross } from '../../Common/Core/Math/index.js';
|
|
5
5
|
import vtkActor from '../../Rendering/Core/Actor.js';
|
|
6
6
|
import vtkCylinderSource from '../../Filters/Sources/CylinderSource.js';
|
|
7
7
|
import vtkMapper from '../../Rendering/Core/Mapper.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
2
2
|
import macro from '../../macros.js';
|
|
3
3
|
import vtkProp from '../../Rendering/Core/Prop.js';
|
|
4
|
-
import {
|
|
4
|
+
import { c as subtract, d as dot } from '../../Common/Core/Math/index.js';
|
|
5
5
|
import { Behavior } from './WidgetRepresentation/Constants.js';
|
|
6
6
|
import { RenderingTypes } from '../Core/WidgetManager/Constants.js';
|
|
7
7
|
import { CATEGORIES } from '../../Rendering/Core/Mapper/CoincidentTopologyHelper.js';
|
|
@@ -3,7 +3,7 @@ import vtkAbstractWidgetFactory from '../Core/AbstractWidgetFactory.js';
|
|
|
3
3
|
import vtkPlanePointManipulator from '../Manipulators/PlaneManipulator.js';
|
|
4
4
|
import vtkPolyLineRepresentation from '../Representations/PolyLineRepresentation.js';
|
|
5
5
|
import vtkSphereHandleRepresentation from '../Representations/SphereHandleRepresentation.js';
|
|
6
|
-
import {
|
|
6
|
+
import { c as subtract, R as angleBetweenVectors } from '../../Common/Core/Math/index.js';
|
|
7
7
|
import widgetBehavior from './AngleWidget/behavior.js';
|
|
8
8
|
import generateState from './AngleWidget/state.js';
|
|
9
9
|
import { ViewTypes } from '../Core/WidgetManager/Constants.js';
|