@kitware/vtk.js 22.2.3 → 22.4.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/Rendering/Core/ScalarBarActor.js +196 -143
- package/Rendering/OpenGL/Profiles/All.js +1 -0
- package/Rendering/OpenGL/Profiles/Geometry.js +1 -0
- package/Rendering/OpenGL/ScalarBarActor.js +61 -0
- package/Rendering/Profiles/All.js +2 -0
- package/Rendering/Profiles/Geometry.js +2 -0
- package/Rendering/WebGPU/BindGroup.js +5 -4
- package/Rendering/WebGPU/Buffer.js +6 -3
- package/Rendering/WebGPU/BufferManager.js +3 -1
- package/Rendering/WebGPU/Device.js +1 -1
- package/Rendering/WebGPU/Glyph3DMapper.js +3 -2
- package/Rendering/WebGPU/HardwareSelectionPass.js +13 -9
- package/Rendering/WebGPU/HardwareSelector.js +6 -2
- package/Rendering/WebGPU/ImageMapper.js +14 -12
- package/Rendering/WebGPU/MapperHelper.js +3 -2
- package/Rendering/WebGPU/OpaquePass.js +11 -7
- package/Rendering/WebGPU/OrderIndependentTranslucentPass.js +21 -13
- package/Rendering/WebGPU/Pipeline.js +6 -5
- package/Rendering/WebGPU/PolyDataMapper.js +7 -5
- package/Rendering/WebGPU/Profiles/All.js +1 -0
- package/Rendering/WebGPU/Profiles/Geometry.js +1 -0
- package/Rendering/WebGPU/RenderEncoder.js +17 -6
- package/Rendering/WebGPU/Renderer.js +9 -6
- package/Rendering/WebGPU/Sampler.js +4 -3
- package/Rendering/WebGPU/ScalarBarActor.js +61 -0
- package/Rendering/WebGPU/StorageBuffer.js +6 -5
- package/Rendering/WebGPU/Texture.js +13 -7
- package/Rendering/WebGPU/TextureView.js +10 -21
- package/Rendering/WebGPU/UniformBuffer.js +6 -5
- package/Rendering/WebGPU/VolumePass.js +50 -33
- package/Rendering/WebGPU/VolumePassFSQ.js +32 -16
- package/package.json +2 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { newInstance as newInstance$1 } from '../../macros.js';
|
|
2
|
+
import vtkScalarBarActor from '../Core/ScalarBarActor.js';
|
|
3
|
+
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
4
|
+
import { registerOverride } from './ViewNodeFactory.js';
|
|
5
|
+
|
|
6
|
+
// vtkWebGPUScalarBarActor methods
|
|
7
|
+
// ----------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
function vtkWebGPUScalarBarActor(publicAPI, model) {
|
|
10
|
+
model.classHierarchy.push('vtkWebGPUScalarBarActor'); // Builds myself.
|
|
11
|
+
|
|
12
|
+
publicAPI.buildPass = function (prepass) {
|
|
13
|
+
if (prepass) {
|
|
14
|
+
model.WebGPURenderer = publicAPI.getFirstAncestorOfType('vtkWebGPURenderer');
|
|
15
|
+
model.WebGPURenderWindow = model.WebGPURenderer.getParent();
|
|
16
|
+
|
|
17
|
+
if (!model.scalarBarActorHelper.getRenderable()) {
|
|
18
|
+
model.scalarBarActorHelper.setRenderable(model.renderable);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
publicAPI.prepareNodes();
|
|
22
|
+
publicAPI.addMissingNode(model.scalarBarActorHelper.getBarActor());
|
|
23
|
+
publicAPI.addMissingNode(model.scalarBarActorHelper.getTmActor());
|
|
24
|
+
publicAPI.removeUnusedNodes();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
publicAPI.opaquePass = function (prepass, renderPass) {
|
|
29
|
+
if (prepass) {
|
|
30
|
+
var camera = model.WebGPURenderer ? model.WebGPURenderer.getRenderable().getActiveCamera() : null;
|
|
31
|
+
var tsize = model.WebGPURenderer.getTiledSizeAndOrigin();
|
|
32
|
+
model.scalarBarActorHelper.updateAPISpecificData([tsize.usize, tsize.vsize], camera, model.WebGPURenderWindow.getRenderable());
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
} // ----------------------------------------------------------------------------
|
|
36
|
+
// Object factory
|
|
37
|
+
// ----------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
var DEFAULT_VALUES = {}; // ----------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
function extend(publicAPI, model) {
|
|
43
|
+
var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
44
|
+
Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance
|
|
45
|
+
|
|
46
|
+
vtkViewNode.extend(publicAPI, model, initialValues);
|
|
47
|
+
model.scalarBarActorHelper = vtkScalarBarActor.newScalarBarActorHelper(); // Object methods
|
|
48
|
+
|
|
49
|
+
vtkWebGPUScalarBarActor(publicAPI, model);
|
|
50
|
+
} // ----------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
var newInstance = newInstance$1(extend, 'vtkWebGPUScalarBarActor'); // ----------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
var index = {
|
|
55
|
+
newInstance: newInstance,
|
|
56
|
+
extend: extend
|
|
57
|
+
}; // Register ourself to WebGPU backend if imported
|
|
58
|
+
|
|
59
|
+
registerOverride('vtkScalarBarActor', newInstance);
|
|
60
|
+
|
|
61
|
+
export { index as default, extend, newInstance };
|
|
@@ -41,7 +41,8 @@ function vtkWebGPUStorageBuffer(publicAPI, model) {
|
|
|
41
41
|
var req = {
|
|
42
42
|
nativeArray: model.Float32Array,
|
|
43
43
|
time: 0,
|
|
44
|
-
usage: BufferUsage.Storage
|
|
44
|
+
usage: BufferUsage.Storage,
|
|
45
|
+
label: model.label
|
|
45
46
|
};
|
|
46
47
|
model._buffer = device.getBufferManager().getBuffer(req);
|
|
47
48
|
model.bindGroupTime.modified();
|
|
@@ -171,14 +172,14 @@ function vtkWebGPUStorageBuffer(publicAPI, model) {
|
|
|
171
172
|
};
|
|
172
173
|
|
|
173
174
|
publicAPI.getShaderCode = function (binding, group) {
|
|
174
|
-
var lines = ["struct ".concat(model.
|
|
175
|
+
var lines = ["struct ".concat(model.label, "StructEntry\n{")];
|
|
175
176
|
|
|
176
177
|
for (var i = 0; i < model.bufferEntries.length; i++) {
|
|
177
178
|
var entry = model.bufferEntries[i];
|
|
178
179
|
lines.push(" ".concat(entry.name, ": ").concat(entry.type, ";"));
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
lines.push("\n};\nstruct ".concat(model.
|
|
182
|
+
lines.push("\n};\nstruct ".concat(model.label, "Struct\n{\n values: array<").concat(model.label, "StructEntry>;\n};\n@binding(").concat(binding, ") @group(").concat(group, ") var<storage, read> ").concat(model.label, ": ").concat(model.label, "Struct;\n"));
|
|
182
183
|
return lines.join('\n');
|
|
183
184
|
};
|
|
184
185
|
|
|
@@ -209,7 +210,7 @@ var DEFAULT_VALUES = {
|
|
|
209
210
|
bufferEntries: null,
|
|
210
211
|
bufferEntryNames: null,
|
|
211
212
|
sizeInBytes: 0,
|
|
212
|
-
|
|
213
|
+
label: null,
|
|
213
214
|
numberOfInstances: 1
|
|
214
215
|
}; // ----------------------------------------------------------------------------
|
|
215
216
|
|
|
@@ -236,7 +237,7 @@ function extend(publicAPI, model) {
|
|
|
236
237
|
}
|
|
237
238
|
};
|
|
238
239
|
macro.get(publicAPI, model, ['bindGroupTime']);
|
|
239
|
-
macro.setGet(publicAPI, model, ['device', 'bindGroupLayoutEntry', '
|
|
240
|
+
macro.setGet(publicAPI, model, ['device', 'bindGroupLayoutEntry', 'label', 'numberOfInstances', 'sizeInBytes']); // Object methods
|
|
240
241
|
|
|
241
242
|
vtkWebGPUStorageBuffer(publicAPI, model);
|
|
242
243
|
} // ----------------------------------------------------------------------------
|
|
@@ -36,6 +36,7 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
36
36
|
format: model.format,
|
|
37
37
|
// 'rgba8unorm',
|
|
38
38
|
usage: model.usage,
|
|
39
|
+
label: model.label,
|
|
39
40
|
dimension: dimension
|
|
40
41
|
});
|
|
41
42
|
};
|
|
@@ -172,7 +173,8 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
172
173
|
model.handle = model.device.getHandle().createTexture({
|
|
173
174
|
size: [model.width, model.height, model.depth],
|
|
174
175
|
format: model.format,
|
|
175
|
-
usage: model.usage
|
|
176
|
+
usage: model.usage,
|
|
177
|
+
label: model.label
|
|
176
178
|
});
|
|
177
179
|
}
|
|
178
180
|
};
|
|
@@ -187,20 +189,23 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
187
189
|
model.handle = model.device.getHandle().createTexture({
|
|
188
190
|
size: [model.width, model.height, model.depth],
|
|
189
191
|
format: model.format,
|
|
190
|
-
usage: model.usage
|
|
192
|
+
usage: model.usage,
|
|
193
|
+
label: model.label
|
|
191
194
|
});
|
|
192
195
|
}
|
|
193
196
|
};
|
|
194
197
|
|
|
195
|
-
publicAPI.createView = function () {
|
|
196
|
-
var options = arguments.length >
|
|
198
|
+
publicAPI.createView = function (label) {
|
|
199
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
197
200
|
|
|
198
201
|
// if options is missing values try to add them in
|
|
199
202
|
if (!options.dimension) {
|
|
200
203
|
options.dimension = model.depth === 1 ? '2d' : '3d';
|
|
201
204
|
}
|
|
202
205
|
|
|
203
|
-
var view = vtkWebGPUTextureView.newInstance(
|
|
206
|
+
var view = vtkWebGPUTextureView.newInstance({
|
|
207
|
+
label: label
|
|
208
|
+
});
|
|
204
209
|
view.create(publicAPI, options);
|
|
205
210
|
return view;
|
|
206
211
|
};
|
|
@@ -213,7 +218,8 @@ var DEFAULT_VALUES = {
|
|
|
213
218
|
device: null,
|
|
214
219
|
handle: null,
|
|
215
220
|
buffer: null,
|
|
216
|
-
ready: false
|
|
221
|
+
ready: false,
|
|
222
|
+
label: null
|
|
217
223
|
}; // ----------------------------------------------------------------------------
|
|
218
224
|
|
|
219
225
|
function extend(publicAPI, model) {
|
|
@@ -222,7 +228,7 @@ function extend(publicAPI, model) {
|
|
|
222
228
|
|
|
223
229
|
macro.obj(publicAPI, model);
|
|
224
230
|
macro.get(publicAPI, model, ['handle', 'ready', 'width', 'height', 'depth', 'format', 'usage']);
|
|
225
|
-
macro.setGet(publicAPI, model, ['device']);
|
|
231
|
+
macro.setGet(publicAPI, model, ['device', 'label']);
|
|
226
232
|
vtkWebGPUTexture(publicAPI, model);
|
|
227
233
|
} // ----------------------------------------------------------------------------
|
|
228
234
|
|
|
@@ -15,6 +15,7 @@ function vtkWebGPUTextureView(publicAPI, model) {
|
|
|
15
15
|
model.texture = texture;
|
|
16
16
|
model.options = options;
|
|
17
17
|
model.options.dimension = model.options.dimension || '2d';
|
|
18
|
+
model.options.label = model.label;
|
|
18
19
|
model.textureHandle = texture.getHandle();
|
|
19
20
|
model.handle = model.textureHandle.createView(model.options);
|
|
20
21
|
model.bindGroupLayoutEntry.texture.viewDimension = model.options.dimension;
|
|
@@ -38,33 +39,21 @@ function vtkWebGPUTextureView(publicAPI, model) {
|
|
|
38
39
|
ttype = 'u32';
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
var result = "@binding(".concat(binding, ") @group(").concat(group, ") var ").concat(model.
|
|
42
|
+
var result = "@binding(".concat(binding, ") @group(").concat(group, ") var ").concat(model.label, ": texture_").concat(model.options.dimension, "<").concat(ttype, ">;");
|
|
42
43
|
|
|
43
44
|
if (model.bindGroupLayoutEntry.texture.sampleType === 'depth') {
|
|
44
|
-
result = "@binding(".concat(binding, ") @group(").concat(group, ") var ").concat(model.
|
|
45
|
+
result = "@binding(".concat(binding, ") @group(").concat(group, ") var ").concat(model.label, ": texture_depth_").concat(model.options.dimension, ";");
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
return result;
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
publicAPI.addSampler = function (device, options) {
|
|
51
|
-
var newSamp = vtkWebGPUSampler.newInstance(
|
|
52
|
+
var newSamp = vtkWebGPUSampler.newInstance({
|
|
53
|
+
label: "".concat(model.label, "Sampler")
|
|
54
|
+
});
|
|
52
55
|
newSamp.create(device, options);
|
|
53
56
|
publicAPI.setSampler(newSamp);
|
|
54
|
-
model.sampler.setName("".concat(model.name, "Sampler"));
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
publicAPI.setName = function (val) {
|
|
58
|
-
if (model.sampler) {
|
|
59
|
-
model.sampler.setName("".concat(val, "Sampler"));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (model.name === val) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
model.name = val;
|
|
67
|
-
publicAPI.modified();
|
|
68
57
|
};
|
|
69
58
|
|
|
70
59
|
publicAPI.getBindGroupTime = function () {
|
|
@@ -96,8 +85,8 @@ function vtkWebGPUTextureView(publicAPI, model) {
|
|
|
96
85
|
var DEFAULT_VALUES = {
|
|
97
86
|
texture: null,
|
|
98
87
|
handle: null,
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
sampler: null,
|
|
89
|
+
label: null
|
|
101
90
|
}; // ----------------------------------------------------------------------------
|
|
102
91
|
|
|
103
92
|
function extend(publicAPI, model) {
|
|
@@ -120,8 +109,8 @@ function extend(publicAPI, model) {
|
|
|
120
109
|
macro.obj(model.bindGroupTime, {
|
|
121
110
|
mtime: 0
|
|
122
111
|
});
|
|
123
|
-
macro.get(publicAPI, model, ['bindGroupTime', '
|
|
124
|
-
macro.setGet(publicAPI, model, ['bindGroupLayoutEntry', 'sampler']);
|
|
112
|
+
macro.get(publicAPI, model, ['bindGroupTime', 'texture']);
|
|
113
|
+
macro.setGet(publicAPI, model, ['bindGroupLayoutEntry', 'label', 'sampler']);
|
|
125
114
|
vtkWebGPUTextureView(publicAPI, model);
|
|
126
115
|
} // ----------------------------------------------------------------------------
|
|
127
116
|
|
|
@@ -207,7 +207,8 @@ function vtkWebGPUUniformBuffer(publicAPI, model) {
|
|
|
207
207
|
var req = {
|
|
208
208
|
nativeArray: model.Float32Array,
|
|
209
209
|
time: 0,
|
|
210
|
-
usage: BufferUsage.UniformArray
|
|
210
|
+
usage: BufferUsage.UniformArray,
|
|
211
|
+
label: model.label
|
|
211
212
|
};
|
|
212
213
|
model.UBO = device.getBufferManager().getBuffer(req);
|
|
213
214
|
model.bindGroupTime.modified();
|
|
@@ -301,14 +302,14 @@ function vtkWebGPUUniformBuffer(publicAPI, model) {
|
|
|
301
302
|
publicAPI.getShaderCode = function (binding, group) {
|
|
302
303
|
// sort the entries
|
|
303
304
|
publicAPI.sortBufferEntries();
|
|
304
|
-
var lines = ["struct ".concat(model.
|
|
305
|
+
var lines = ["struct ".concat(model.label, "Struct\n{")];
|
|
305
306
|
|
|
306
307
|
for (var i = 0; i < model.bufferEntries.length; i++) {
|
|
307
308
|
var entry = model.bufferEntries[i];
|
|
308
309
|
lines.push(" ".concat(entry.name, ": ").concat(entry.type, ";"));
|
|
309
310
|
}
|
|
310
311
|
|
|
311
|
-
lines.push("};\n@binding(".concat(binding, ") @group(").concat(group, ") var<uniform> ").concat(model.
|
|
312
|
+
lines.push("};\n@binding(".concat(binding, ") @group(").concat(group, ") var<uniform> ").concat(model.label, ": ").concat(model.label, "Struct;"));
|
|
312
313
|
return lines.join('\n');
|
|
313
314
|
};
|
|
314
315
|
} // ----------------------------------------------------------------------------
|
|
@@ -320,7 +321,7 @@ var DEFAULT_VALUES = {
|
|
|
320
321
|
bufferEntries: null,
|
|
321
322
|
bufferEntryNames: null,
|
|
322
323
|
sizeInBytes: 0,
|
|
323
|
-
|
|
324
|
+
label: null,
|
|
324
325
|
bindGroupLayoutEntry: null,
|
|
325
326
|
bindGroupEntry: null
|
|
326
327
|
}; // ----------------------------------------------------------------------------
|
|
@@ -350,7 +351,7 @@ function extend(publicAPI, model) {
|
|
|
350
351
|
model.sendDirty = true;
|
|
351
352
|
model.sortDirty = true;
|
|
352
353
|
macro.get(publicAPI, model, ['binding', 'bindGroupTime']);
|
|
353
|
-
macro.setGet(publicAPI, model, ['bindGroupLayoutEntry', 'device', '
|
|
354
|
+
macro.setGet(publicAPI, model, ['bindGroupLayoutEntry', 'device', 'label', 'sizeInBytes']); // Object methods
|
|
354
355
|
|
|
355
356
|
vtkWebGPUUniformBuffer(publicAPI, model);
|
|
356
357
|
} // ----------------------------------------------------------------------------
|
|
@@ -34,7 +34,7 @@ var BufferUsage = vtkWebGPUBufferManager.BufferUsage,
|
|
|
34
34
|
|
|
35
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]];
|
|
36
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(
|
|
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(volumePassColorTexture,\n volumePassColorTextureSampler, mapperUBO.tscale*input.tcoordVS);\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
|
|
38
38
|
/* eslint-disable no-undef */
|
|
39
39
|
|
|
40
40
|
/* eslint-disable no-bitwise */
|
|
@@ -76,9 +76,9 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
76
76
|
|
|
77
77
|
model._volumeCopyQuad.setFragmentShaderTemplate(volumeCopyFragTemplate);
|
|
78
78
|
|
|
79
|
-
model._copyUBO = vtkWebGPUUniformBuffer.newInstance(
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
model._copyUBO = vtkWebGPUUniformBuffer.newInstance({
|
|
80
|
+
label: 'mapperUBO'
|
|
81
|
+
});
|
|
82
82
|
|
|
83
83
|
model._copyUBO.addEntry('tscale', 'vec2<f32>');
|
|
84
84
|
|
|
@@ -206,14 +206,20 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
206
206
|
if (!model._animationRateSubscription) {
|
|
207
207
|
// when the animation frame rate changes recompute the scale factor
|
|
208
208
|
model._animationRateSubscription = rwi.onAnimationFrameRateUpdate(function () {
|
|
209
|
-
var
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
var firstMapper = model.volumes[0].getRenderable().getMapper();
|
|
210
|
+
|
|
211
|
+
if (firstMapper.getAutoAdjustSampleDistances()) {
|
|
212
|
+
var frate = rwi.getRecentAnimationFrameRate();
|
|
213
|
+
var targetScale = model._lastScale * rwi.getDesiredUpdateRate() / frate;
|
|
214
|
+
model._lastScale = targetScale; // clamp scale to some reasonable values.
|
|
215
|
+
// Below 1.5 we will just be using full resolution as that is close enough
|
|
216
|
+
// Above 400 seems like a lot so we limit to that 1/20th per axis
|
|
217
|
+
|
|
218
|
+
if (model._lastScale > 400) {
|
|
219
|
+
model._lastScale = 400;
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
model._lastScale = firstMapper.getImageSampleDistance() * firstMapper.getImageSampleDistance();
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
if (model._lastScale < 1.5) {
|
|
@@ -366,7 +372,9 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
366
372
|
|
|
367
373
|
publicAPI.createDepthRangeEncoder = function (viewNode) {
|
|
368
374
|
var device = viewNode.getDevice();
|
|
369
|
-
model._depthRangeEncoder = vtkWebGPURenderEncoder.newInstance(
|
|
375
|
+
model._depthRangeEncoder = vtkWebGPURenderEncoder.newInstance({
|
|
376
|
+
label: 'VolumePass DepthRange'
|
|
377
|
+
});
|
|
370
378
|
|
|
371
379
|
model._depthRangeEncoder.setPipelineHash('volr');
|
|
372
380
|
|
|
@@ -382,11 +390,13 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
382
390
|
model._depthRangeEncoder.setDescription({
|
|
383
391
|
colorAttachments: [{
|
|
384
392
|
view: null,
|
|
385
|
-
|
|
393
|
+
clearValue: [0.0, 0.0, 0.0, 0.0],
|
|
394
|
+
loadOp: 'clear',
|
|
386
395
|
storeOp: 'store'
|
|
387
396
|
}, {
|
|
388
397
|
view: null,
|
|
389
|
-
|
|
398
|
+
clearValue: [1.0, 1.0, 1.0, 1.0],
|
|
399
|
+
loadOp: 'clear',
|
|
390
400
|
storeOp: 'store'
|
|
391
401
|
}]
|
|
392
402
|
});
|
|
@@ -429,7 +439,9 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
429
439
|
}); // and the textures it needs
|
|
430
440
|
|
|
431
441
|
|
|
432
|
-
model._depthRangeTexture = vtkWebGPUTexture.newInstance(
|
|
442
|
+
model._depthRangeTexture = vtkWebGPUTexture.newInstance({
|
|
443
|
+
label: 'volumePassMaxDepth'
|
|
444
|
+
});
|
|
433
445
|
|
|
434
446
|
model._depthRangeTexture.create(device, {
|
|
435
447
|
width: viewNode.getCanvas().width,
|
|
@@ -438,13 +450,13 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
438
450
|
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
439
451
|
});
|
|
440
452
|
|
|
441
|
-
var maxView = model._depthRangeTexture.createView();
|
|
442
|
-
|
|
443
|
-
maxView.setName('maxTexture');
|
|
453
|
+
var maxView = model._depthRangeTexture.createView('maxTexture');
|
|
444
454
|
|
|
445
455
|
model._depthRangeEncoder.setColorTextureView(0, maxView);
|
|
446
456
|
|
|
447
|
-
model._depthRangeTexture2 = vtkWebGPUTexture.newInstance(
|
|
457
|
+
model._depthRangeTexture2 = vtkWebGPUTexture.newInstance({
|
|
458
|
+
label: 'volumePassDepthMin'
|
|
459
|
+
});
|
|
448
460
|
|
|
449
461
|
model._depthRangeTexture2.create(device, {
|
|
450
462
|
width: viewNode.getCanvas().width,
|
|
@@ -453,9 +465,7 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
453
465
|
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
|
|
454
466
|
});
|
|
455
467
|
|
|
456
|
-
var minView = model._depthRangeTexture2.createView();
|
|
457
|
-
|
|
458
|
-
minView.setName('minTexture');
|
|
468
|
+
var minView = model._depthRangeTexture2.createView('minTexture');
|
|
459
469
|
|
|
460
470
|
model._depthRangeEncoder.setColorTextureView(1, minView);
|
|
461
471
|
|
|
@@ -465,7 +475,9 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
465
475
|
};
|
|
466
476
|
|
|
467
477
|
publicAPI.createClearEncoder = function (viewNode) {
|
|
468
|
-
model._colorTexture = vtkWebGPUTexture.newInstance(
|
|
478
|
+
model._colorTexture = vtkWebGPUTexture.newInstance({
|
|
479
|
+
label: 'volumePassColor'
|
|
480
|
+
});
|
|
469
481
|
|
|
470
482
|
model._colorTexture.create(viewNode.getDevice(), {
|
|
471
483
|
width: viewNode.getCanvas().width,
|
|
@@ -478,23 +490,24 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
478
490
|
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC
|
|
479
491
|
});
|
|
480
492
|
|
|
481
|
-
model._colorTextureView = model._colorTexture.createView();
|
|
482
|
-
|
|
483
|
-
model._colorTextureView.setName('volumePassSmallColorTexture');
|
|
493
|
+
model._colorTextureView = model._colorTexture.createView('volumePassColorTexture');
|
|
484
494
|
|
|
485
495
|
model._colorTextureView.addSampler(viewNode.getDevice(), {
|
|
486
496
|
minFilter: 'linear',
|
|
487
497
|
magFilter: 'linear'
|
|
488
498
|
});
|
|
489
499
|
|
|
490
|
-
model._clearEncoder = vtkWebGPURenderEncoder.newInstance(
|
|
500
|
+
model._clearEncoder = vtkWebGPURenderEncoder.newInstance({
|
|
501
|
+
label: 'VolumePass Clear'
|
|
502
|
+
});
|
|
491
503
|
|
|
492
504
|
model._clearEncoder.setColorTextureView(0, model._colorTextureView);
|
|
493
505
|
|
|
494
506
|
model._clearEncoder.setDescription({
|
|
495
507
|
colorAttachments: [{
|
|
496
508
|
view: null,
|
|
497
|
-
|
|
509
|
+
clearValue: [0.0, 0.0, 0.0, 0.0],
|
|
510
|
+
loadOp: 'clear',
|
|
498
511
|
storeOp: 'store'
|
|
499
512
|
}]
|
|
500
513
|
});
|
|
@@ -524,12 +537,14 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
524
537
|
};
|
|
525
538
|
|
|
526
539
|
publicAPI.createCopyEncoder = function (viewNode) {
|
|
527
|
-
model._copyEncoder = vtkWebGPURenderEncoder.newInstance(
|
|
540
|
+
model._copyEncoder = vtkWebGPURenderEncoder.newInstance({
|
|
541
|
+
label: 'volumePassCopy'
|
|
542
|
+
});
|
|
528
543
|
|
|
529
544
|
model._copyEncoder.setDescription({
|
|
530
545
|
colorAttachments: [{
|
|
531
546
|
view: null,
|
|
532
|
-
|
|
547
|
+
loadOp: 'load',
|
|
533
548
|
storeOp: 'store'
|
|
534
549
|
}]
|
|
535
550
|
});
|
|
@@ -559,14 +574,16 @@ function vtkWebGPUVolumePass(publicAPI, model) {
|
|
|
559
574
|
};
|
|
560
575
|
|
|
561
576
|
publicAPI.createMergeEncoder = function (viewNode) {
|
|
562
|
-
model._mergeEncoder = vtkWebGPURenderEncoder.newInstance(
|
|
577
|
+
model._mergeEncoder = vtkWebGPURenderEncoder.newInstance({
|
|
578
|
+
label: 'volumePassMerge'
|
|
579
|
+
});
|
|
563
580
|
|
|
564
581
|
model._mergeEncoder.setColorTextureView(0, model._colorTextureView);
|
|
565
582
|
|
|
566
583
|
model._mergeEncoder.setDescription({
|
|
567
584
|
colorAttachments: [{
|
|
568
585
|
view: null,
|
|
569
|
-
|
|
586
|
+
loadOp: 'load',
|
|
570
587
|
storeOp: 'store'
|
|
571
588
|
}]
|
|
572
589
|
});
|
|
@@ -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 // 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";
|
|
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 result.w = 0.0;\n\n // divide by spacing as that is our delta\n result = result / volumeSSBO.values[vNum].spacing;\n // now we have a gradient in unit tcoords\n\n var grad: f32 = length(result.xyz);\n if (grad > 0.0)\n {\n // rotate to View Coords, needed for lighting and shading\n var nMat: mat4x4<f32> = rendererUBO.SCVCMatrix * volumeSSBO.values[vNum].planeNormals;\n result = nMat * result;\n result = result / length(result);\n }\n\n // store gradient magnitude in .w\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 var normal: vec4<f32> = vec4<f32>(0.0,0.0,0.0,0.0);\n if (componentSSBO.values[cNum].gomin < 1.0 || volumeSSBO.values[vNum].shade[0] > 0.0)\n {\n normal = getGradient(vTex, tpos, vNum, scalar);\n if (componentSSBO.values[cNum].gomin < 1.0)\n {\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\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 if (volumeSSBO.values[vNum].shade[0] > 0.0)\n {\n color = color*abs(normal.z);\n }\n\n outColor = vec4<f32>(color.rgb, gofactor * opacity);\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
|
|
@@ -189,8 +189,7 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
189
189
|
format: 'rgba8unorm'
|
|
190
190
|
};
|
|
191
191
|
var newTex = device.getTextureManager().getTexture(treq);
|
|
192
|
-
var tview = newTex.createView();
|
|
193
|
-
tview.setName('tfunTexture');
|
|
192
|
+
var tview = newTex.createView('tfunTexture');
|
|
194
193
|
model.textureViews[2] = tview;
|
|
195
194
|
}
|
|
196
195
|
{
|
|
@@ -204,9 +203,7 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
204
203
|
|
|
205
204
|
var _newTex = device.getTextureManager().getTexture(_treq);
|
|
206
205
|
|
|
207
|
-
var _tview = _newTex.createView();
|
|
208
|
-
|
|
209
|
-
_tview.setName('ofunTexture');
|
|
206
|
+
var _tview = _newTex.createView('ofunTexture');
|
|
210
207
|
|
|
211
208
|
model.textureViews[3] = _tview;
|
|
212
209
|
}
|
|
@@ -243,7 +240,9 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
243
240
|
//
|
|
244
241
|
|
|
245
242
|
var marray = new Float64Array(model.volumes.length * 16);
|
|
243
|
+
var vPlaneArray = new Float64Array(model.volumes.length * 16);
|
|
246
244
|
var tstepArray = new Float64Array(model.volumes.length * 4);
|
|
245
|
+
var shadeArray = new Float64Array(model.volumes.length * 4);
|
|
247
246
|
var spacingArray = new Float64Array(model.volumes.length * 4);
|
|
248
247
|
var ipScalarRangeArray = new Float64Array(model.volumes.length * 4);
|
|
249
248
|
|
|
@@ -280,10 +279,20 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
280
279
|
marray[vidx * 16 + j] = tmpMat4[j];
|
|
281
280
|
}
|
|
282
281
|
|
|
282
|
+
mat4.invert(tmpMat4, tmpMat4); // now it is Tcoord To SC
|
|
283
|
+
|
|
284
|
+
for (var _j = 0; _j < 4; _j++) {
|
|
285
|
+
vPlaneArray[vidx * 16 + _j * 4] = tmpMat4[_j * 4];
|
|
286
|
+
vPlaneArray[vidx * 16 + _j * 4 + 1] = tmpMat4[_j * 4 + 1];
|
|
287
|
+
vPlaneArray[vidx * 16 + _j * 4 + 2] = tmpMat4[_j * 4 + 2];
|
|
288
|
+
vPlaneArray[vidx * 16 + _j * 4 + 3] = 0.0;
|
|
289
|
+
}
|
|
290
|
+
|
|
283
291
|
tstepArray[vidx * 4] = 1.0 / dims[0];
|
|
284
292
|
tstepArray[vidx * 4 + 1] = 1.0 / dims[1];
|
|
285
293
|
tstepArray[vidx * 4 + 2] = 1.0 / dims[2];
|
|
286
294
|
tstepArray[vidx * 4 + 3] = 1.0;
|
|
295
|
+
shadeArray[vidx * 4] = actor.getProperty().getShade() ? 1.0 : 0.0;
|
|
287
296
|
|
|
288
297
|
var spacing = _image3.getSpacing();
|
|
289
298
|
|
|
@@ -302,10 +311,14 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
302
311
|
}
|
|
303
312
|
|
|
304
313
|
model.SSBO.addEntry('SCTCMatrix', 'mat4x4<f32>');
|
|
314
|
+
model.SSBO.addEntry('planeNormals', 'mat4x4<f32>');
|
|
315
|
+
model.SSBO.addEntry('shade', 'vec4<f32>');
|
|
305
316
|
model.SSBO.addEntry('tstep', 'vec4<f32>');
|
|
306
317
|
model.SSBO.addEntry('spacing', 'vec4<f32>');
|
|
307
318
|
model.SSBO.addEntry('ipScalarRange', 'vec4<f32>');
|
|
308
319
|
model.SSBO.setAllInstancesFromArray('SCTCMatrix', marray);
|
|
320
|
+
model.SSBO.setAllInstancesFromArray('planeNormals', vPlaneArray);
|
|
321
|
+
model.SSBO.setAllInstancesFromArray('shade', shadeArray);
|
|
309
322
|
model.SSBO.setAllInstancesFromArray('tstep', tstepArray);
|
|
310
323
|
model.SSBO.setAllInstancesFromArray('spacing', spacingArray);
|
|
311
324
|
model.SSBO.setAllInstancesFromArray('ipScalarRange', ipScalarRangeArray);
|
|
@@ -446,8 +459,7 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
446
459
|
var newTex = device.getTextureManager().getTexture(treq);
|
|
447
460
|
|
|
448
461
|
if (!model.textureViews[vidx + 4] || model.textureViews[vidx + 4].getTexture() !== newTex) {
|
|
449
|
-
var tview = newTex.createView();
|
|
450
|
-
tview.setName("volTexture".concat(vidx));
|
|
462
|
+
var tview = newTex.createView("volTexture".concat(vidx));
|
|
451
463
|
model.textureViews[vidx + 4] = tview;
|
|
452
464
|
}
|
|
453
465
|
} // clear any old leftovers
|
|
@@ -498,8 +510,9 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
498
510
|
publicAPI.updateBuffers(device);
|
|
499
511
|
|
|
500
512
|
if (!model.clampSampler) {
|
|
501
|
-
model.clampSampler = vtkWebGPUSampler.newInstance(
|
|
502
|
-
|
|
513
|
+
model.clampSampler = vtkWebGPUSampler.newInstance({
|
|
514
|
+
label: 'clampSampler'
|
|
515
|
+
});
|
|
503
516
|
model.clampSampler.create(device, {
|
|
504
517
|
minFilter: 'linear',
|
|
505
518
|
magFilter: 'linear'
|
|
@@ -534,13 +547,16 @@ function extend(publicAPI, model) {
|
|
|
534
547
|
|
|
535
548
|
vtkWebGPUFullScreenQuad.extend(publicAPI, model, initialValues);
|
|
536
549
|
model.fragmentShaderTemplate = volFragTemplate;
|
|
537
|
-
model.UBO = vtkWebGPUUniformBuffer.newInstance(
|
|
538
|
-
|
|
550
|
+
model.UBO = vtkWebGPUUniformBuffer.newInstance({
|
|
551
|
+
label: 'mapperUBO'
|
|
552
|
+
});
|
|
539
553
|
model.UBO.addEntry('SampleDistance', 'f32');
|
|
540
|
-
model.SSBO = vtkWebGPUStorageBuffer.newInstance(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
model.componentSSBO.
|
|
554
|
+
model.SSBO = vtkWebGPUStorageBuffer.newInstance({
|
|
555
|
+
label: 'volumeSSBO'
|
|
556
|
+
});
|
|
557
|
+
model.componentSSBO = vtkWebGPUStorageBuffer.newInstance({
|
|
558
|
+
label: 'componentSSBO'
|
|
559
|
+
});
|
|
544
560
|
model.lutBuildTime = {};
|
|
545
561
|
macro.obj(model.lutBuildTime, {
|
|
546
562
|
mtime: 0
|