@vitessce/spatial-accelerated 3.8.0
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/LICENSE +21 -0
- package/dist/index.js +52925 -0
- package/dist-tsc/SpatialWrapper.d.ts +2 -0
- package/dist-tsc/SpatialWrapper.d.ts.map +1 -0
- package/dist-tsc/SpatialWrapper.js +45 -0
- package/dist-tsc/VolumeDataManager.d.ts +170 -0
- package/dist-tsc/VolumeDataManager.d.ts.map +1 -0
- package/dist-tsc/VolumeDataManager.js +1273 -0
- package/dist-tsc/VolumeDataManager.test.d.ts +2 -0
- package/dist-tsc/VolumeDataManager.test.d.ts.map +1 -0
- package/dist-tsc/VolumeDataManager.test.js +164 -0
- package/dist-tsc/VolumeRenderManager.d.ts +265 -0
- package/dist-tsc/VolumeRenderManager.d.ts.map +1 -0
- package/dist-tsc/VolumeRenderManager.js +396 -0
- package/dist-tsc/VolumeShader.d.ts +262 -0
- package/dist-tsc/VolumeShader.d.ts.map +1 -0
- package/dist-tsc/VolumeShader.js +73 -0
- package/dist-tsc/VolumeView.d.ts +2 -0
- package/dist-tsc/VolumeView.d.ts.map +1 -0
- package/dist-tsc/VolumeView.js +521 -0
- package/dist-tsc/index.d.ts +2 -0
- package/dist-tsc/index.d.ts.map +1 -0
- package/dist-tsc/index.js +1 -0
- package/dist-tsc/shaders/GaussianFragmentShader.d.ts +2 -0
- package/dist-tsc/shaders/GaussianFragmentShader.d.ts.map +1 -0
- package/dist-tsc/shaders/GaussianFragmentShader.js +242 -0
- package/dist-tsc/shaders/GaussianVertexShader.d.ts +2 -0
- package/dist-tsc/shaders/GaussianVertexShader.d.ts.map +1 -0
- package/dist-tsc/shaders/GaussianVertexShader.js +51 -0
- package/dist-tsc/shaders/VolumeFragmentShader.d.ts +2 -0
- package/dist-tsc/shaders/VolumeFragmentShader.d.ts.map +1 -0
- package/dist-tsc/shaders/VolumeFragmentShader.js +1817 -0
- package/dist-tsc/shaders/VolumeVertexShader.d.ts +2 -0
- package/dist-tsc/shaders/VolumeVertexShader.d.ts.map +1 -0
- package/dist-tsc/shaders/VolumeVertexShader.js +110 -0
- package/package.json +63 -0
- package/src/SpatialWrapper.js +60 -0
- package/src/VolumeDataManager.js +1515 -0
- package/src/VolumeDataManager.test.js +180 -0
- package/src/VolumeRenderManager.js +589 -0
- package/src/VolumeShader.js +74 -0
- package/src/VolumeView.js +651 -0
- package/src/index.js +1 -0
- package/src/shaders/GaussianFragmentShader.js +243 -0
- package/src/shaders/GaussianVertexShader.js +52 -0
- package/src/shaders/VolumeFragmentShader.js +1818 -0
- package/src/shaders/VolumeVertexShader.js +111 -0
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import {
|
|
3
|
+
Vector2,
|
|
4
|
+
UniformsUtils,
|
|
5
|
+
} from 'three';
|
|
6
|
+
import { CoordinationType } from '@vitessce/constants-internal';
|
|
7
|
+
import { log, atLeastLogLevel, LogLevel } from '@vitessce/globals';
|
|
8
|
+
import { VolumeShader } from './VolumeShader.js';
|
|
9
|
+
|
|
10
|
+
function logWithColor(message) {
|
|
11
|
+
if (atLeastLogLevel(LogLevel.DEBUG)) {
|
|
12
|
+
console.warn(
|
|
13
|
+
`%cRM: ${message}`,
|
|
14
|
+
'background: orange; color: white; padding: 2px; border-radius: 3px;',
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Normalize a value based on the volume's min/max range
|
|
21
|
+
* @param {number} value - Value to normalize
|
|
22
|
+
* @param {Array} minMax - [min, max] values of the volume
|
|
23
|
+
* @returns {number} Normalized value
|
|
24
|
+
*/
|
|
25
|
+
function normalizeValue(value, minMax) {
|
|
26
|
+
return value / minMax[1];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Manages the volume rendering state, shader setup, and uniform management
|
|
31
|
+
* Serves as a replacement for many functions from three-utils.js
|
|
32
|
+
*/
|
|
33
|
+
export class VolumeRenderManager {
|
|
34
|
+
constructor() {
|
|
35
|
+
logWithColor('Initializing VolumeRenderManager');
|
|
36
|
+
// Rendering settings
|
|
37
|
+
this.uniforms = null;
|
|
38
|
+
this.shader = null;
|
|
39
|
+
this.meshScale = [1, 1, 1];
|
|
40
|
+
this.geometrySize = [1, 1, 1];
|
|
41
|
+
this.boxSize = [1, 1, 1];
|
|
42
|
+
this.zarrInit = false;
|
|
43
|
+
|
|
44
|
+
// Channel and texture state
|
|
45
|
+
this.channelsVisible = [];
|
|
46
|
+
this.channelTargetC = [];
|
|
47
|
+
this.zarrStoreNumResolutions = null;
|
|
48
|
+
this.channelMaxResolutionIndex = [];
|
|
49
|
+
this.colors = [];
|
|
50
|
+
this.contrastLimits = [];
|
|
51
|
+
this.layerTransparency = 1.0;
|
|
52
|
+
this.xSlice = new Vector2(-1, 100000);
|
|
53
|
+
this.ySlice = new Vector2(-1, 100000);
|
|
54
|
+
this.zSlice = new Vector2(-1, 100000);
|
|
55
|
+
this.originalScale = [1, 1, 1];
|
|
56
|
+
this.physicalDimensions = [1, 1, 1]; // can be used later, currently our props scale by pixel
|
|
57
|
+
this.maxResolution = [1, 1, 1];
|
|
58
|
+
|
|
59
|
+
this.maxRange = 255;
|
|
60
|
+
this.maxRangeSet = false;
|
|
61
|
+
|
|
62
|
+
// Additional state
|
|
63
|
+
// this.mrt = null; // Single MRT reference
|
|
64
|
+
|
|
65
|
+
// Initialize shader
|
|
66
|
+
this.initializeShader();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Initialize shader and uniform objects
|
|
71
|
+
*/
|
|
72
|
+
initializeShader() {
|
|
73
|
+
logWithColor('Initializing shader');
|
|
74
|
+
this.shader = VolumeShader;
|
|
75
|
+
this.uniforms = UniformsUtils.clone(this.shader.uniforms);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Extract rendering settings from coordination props
|
|
80
|
+
* @param {Object} props - Component props
|
|
81
|
+
* @returns {Object} Extracted rendering settings
|
|
82
|
+
*/
|
|
83
|
+
extractRenderingSettingsFromProps(props) {
|
|
84
|
+
logWithColor('Extracting rendering settings from props');
|
|
85
|
+
// log.debug('props', props);
|
|
86
|
+
// log.debug('props.imageChannelCoordination', props.imageChannelCoordination[0]['A']);
|
|
87
|
+
const {
|
|
88
|
+
images = {},
|
|
89
|
+
imageLayerScopes = [],
|
|
90
|
+
imageLayerCoordination = [{}],
|
|
91
|
+
imageChannelScopesByLayer = {},
|
|
92
|
+
imageChannelCoordination = [{}],
|
|
93
|
+
spatialRenderingMode,
|
|
94
|
+
} = props;
|
|
95
|
+
|
|
96
|
+
const layerScope = imageLayerScopes[0];
|
|
97
|
+
if (!layerScope) {
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const channelScopes = imageChannelScopesByLayer[layerScope];
|
|
104
|
+
const layerCoordination = imageLayerCoordination[0][layerScope];
|
|
105
|
+
const channelCoordination = imageChannelCoordination[0][layerScope];
|
|
106
|
+
|
|
107
|
+
const data = images[layerScope]?.image?.instance?.getData();
|
|
108
|
+
if (!data) {
|
|
109
|
+
return {
|
|
110
|
+
valid: false,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if (!channelCoordination[channelScopes?.[0]][CoordinationType.SPATIAL_CHANNEL_WINDOW]) {
|
|
116
|
+
// We don't want to initialize with a null spatialChannelWindow,
|
|
117
|
+
// since down below we use the default value of [0, 255], but we may have a Uint16 image.
|
|
118
|
+
// This will cause the sliders to not affect the contrast limits until below 255,
|
|
119
|
+
// but their range will be much larger (e.g., 0-65535).
|
|
120
|
+
|
|
121
|
+
// TODO(mark): the defaults should be set based on the image dtype.
|
|
122
|
+
// TODO(mark): wait for all channel IQR ranges to be computed before initializing,
|
|
123
|
+
// as we do not want this.maxRange to be incorrect.
|
|
124
|
+
return {
|
|
125
|
+
valid: false,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const imageWrapperInstance = images[layerScope].image.instance;
|
|
130
|
+
const is3dMode = spatialRenderingMode === '3D';
|
|
131
|
+
const isRgb = layerCoordination[CoordinationType.PHOTOMETRIC_INTERPRETATION] === 'RGB';
|
|
132
|
+
const visible = layerCoordination[CoordinationType.SPATIAL_LAYER_VISIBLE];
|
|
133
|
+
const layerTransparency = layerCoordination[CoordinationType.SPATIAL_LAYER_OPACITY];
|
|
134
|
+
|
|
135
|
+
// Extract colors
|
|
136
|
+
const colors = isRgb ? ([
|
|
137
|
+
[255, 0, 0],
|
|
138
|
+
[0, 255, 0],
|
|
139
|
+
[0, 0, 255],
|
|
140
|
+
]) : channelScopes.map(cScope => (
|
|
141
|
+
channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_COLOR]
|
|
142
|
+
));
|
|
143
|
+
|
|
144
|
+
// Extract contrast limits
|
|
145
|
+
const contrastLimits = isRgb ? ([
|
|
146
|
+
[0, 255],
|
|
147
|
+
[0, 255],
|
|
148
|
+
[0, 255],
|
|
149
|
+
]) : channelScopes.map(cScope => (
|
|
150
|
+
channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_WINDOW]
|
|
151
|
+
|| ([0, 255])
|
|
152
|
+
));
|
|
153
|
+
|
|
154
|
+
if (!this.maxRangeSet) {
|
|
155
|
+
this.maxRange = Math.max(...contrastLimits.map(limit => limit[1]));
|
|
156
|
+
this.maxRangeSet = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Extract channel visibility
|
|
160
|
+
const channelsVisible = isRgb ? ([
|
|
161
|
+
visible && true,
|
|
162
|
+
visible && true,
|
|
163
|
+
visible && true,
|
|
164
|
+
]) : channelScopes.map(cScope => (
|
|
165
|
+
visible && channelCoordination[cScope][CoordinationType.SPATIAL_CHANNEL_VISIBLE]
|
|
166
|
+
));
|
|
167
|
+
|
|
168
|
+
// Extract channel target indices
|
|
169
|
+
const channelTargetC = isRgb ? ([
|
|
170
|
+
visible && true,
|
|
171
|
+
visible && true,
|
|
172
|
+
visible && true,
|
|
173
|
+
]) : channelScopes.map(cScope => (
|
|
174
|
+
visible && imageWrapperInstance.getChannelIndex(
|
|
175
|
+
channelCoordination[cScope][CoordinationType.SPATIAL_TARGET_C],
|
|
176
|
+
)
|
|
177
|
+
));
|
|
178
|
+
|
|
179
|
+
// Get max resolution index
|
|
180
|
+
const channelMaxResolutionIndex = isRgb ? ([
|
|
181
|
+
visible && null,
|
|
182
|
+
visible && null,
|
|
183
|
+
visible && null,
|
|
184
|
+
]) : channelScopes.map(cScope => (
|
|
185
|
+
channelCoordination[cScope][CoordinationType.SPATIAL_MAX_RESOLUTION]
|
|
186
|
+
));
|
|
187
|
+
|
|
188
|
+
// Get slice planes
|
|
189
|
+
let xSlice = layerCoordination[CoordinationType.SPATIAL_SLICE_X];
|
|
190
|
+
let ySlice = layerCoordination[CoordinationType.SPATIAL_SLICE_Y];
|
|
191
|
+
let zSlice = layerCoordination[CoordinationType.SPATIAL_SLICE_Z];
|
|
192
|
+
|
|
193
|
+
xSlice = xSlice !== null ? xSlice : new Vector2(-1, 100000);
|
|
194
|
+
ySlice = ySlice !== null ? ySlice : new Vector2(-1, 100000);
|
|
195
|
+
zSlice = zSlice !== null ? zSlice : new Vector2(-1, 100000);
|
|
196
|
+
|
|
197
|
+
const allChannels = images[layerScope].image.loaders[0].channels;
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
valid: true,
|
|
201
|
+
channelsVisible,
|
|
202
|
+
allChannels,
|
|
203
|
+
channelTargetC,
|
|
204
|
+
channelMaxResolutionIndex,
|
|
205
|
+
data,
|
|
206
|
+
colors,
|
|
207
|
+
contrastLimits,
|
|
208
|
+
is3dMode,
|
|
209
|
+
layerTransparency,
|
|
210
|
+
xSlice,
|
|
211
|
+
ySlice,
|
|
212
|
+
zSlice,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update the render settings from the given props
|
|
218
|
+
* @param {Object} props - Component props
|
|
219
|
+
* @returns {boolean} True if settings were successfully updated
|
|
220
|
+
*/
|
|
221
|
+
updateFromProps(props) {
|
|
222
|
+
logWithColor('Updating from props');
|
|
223
|
+
const settings = this.extractRenderingSettingsFromProps(props);
|
|
224
|
+
if (!settings.valid) {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Store the extracted settings
|
|
229
|
+
this.channelsVisible = settings.channelsVisible;
|
|
230
|
+
this.channelTargetC = settings.channelTargetC;
|
|
231
|
+
this.channelMaxResolutionIndex = settings.channelMaxResolutionIndex;
|
|
232
|
+
this.colors = settings.colors;
|
|
233
|
+
this.contrastLimits = settings.contrastLimits;
|
|
234
|
+
this.renderingMode = settings.renderingMode;
|
|
235
|
+
this.layerTransparency = settings.layerTransparency;
|
|
236
|
+
this.xSlice = settings.xSlice;
|
|
237
|
+
this.ySlice = settings.ySlice;
|
|
238
|
+
this.zSlice = settings.zSlice;
|
|
239
|
+
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Update the rendering based on the current volume data from the data manager
|
|
245
|
+
* @param {object} volumeDataManagerProps - Params derived from the volume data manager
|
|
246
|
+
* @returns {Object|null} Updated rendering settings or null if rendering is not possible
|
|
247
|
+
*/
|
|
248
|
+
updateRendering({
|
|
249
|
+
zarrStoreShapes,
|
|
250
|
+
originalScaleXYZ,
|
|
251
|
+
physicalDimensionsXYZ,
|
|
252
|
+
maxResolutionXYZ,
|
|
253
|
+
boxDimensionsXYZ,
|
|
254
|
+
normalizedScaleXYZ,
|
|
255
|
+
bcTHREE,
|
|
256
|
+
ptTHREE,
|
|
257
|
+
}) {
|
|
258
|
+
logWithColor('Updating rendering');
|
|
259
|
+
|
|
260
|
+
// Check if we have at least one visible channel
|
|
261
|
+
const visibleChannelIndex = this.channelTargetC.findIndex(
|
|
262
|
+
(channel, idx) => this.channelsVisible[idx],
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// if (visibleChannelIndex === -1) {
|
|
266
|
+
// return null;
|
|
267
|
+
// }
|
|
268
|
+
|
|
269
|
+
// Instead of getting dimensions from a volume, get from zarrStore
|
|
270
|
+
if (!Array.isArray(zarrStoreShapes) || zarrStoreShapes.length === 0) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Get dimensions from zarrStore
|
|
275
|
+
const shape = zarrStoreShapes[0];
|
|
276
|
+
// Use the highest resolution shape (shape at index 0)
|
|
277
|
+
// Shape format is typically [t, c, z, y, x]
|
|
278
|
+
const dimensions = {
|
|
279
|
+
xLength: shape[4] || 1,
|
|
280
|
+
yLength: shape[3] || 1,
|
|
281
|
+
zLength: shape[2] || 1,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Collect settings for active channels
|
|
285
|
+
const texturesList = [];
|
|
286
|
+
const colorsSave = [];
|
|
287
|
+
const contrastLimitsList = [];
|
|
288
|
+
|
|
289
|
+
// ', this);
|
|
290
|
+
|
|
291
|
+
// log.debug('this.channelsVisible', this.channelsVisible);
|
|
292
|
+
// log.debug('this.channelTargetC', this.channelTargetC);
|
|
293
|
+
// log.debug('this.colors', this.colors);
|
|
294
|
+
// log.debug('this.contrastLimits', this.contrastLimits);
|
|
295
|
+
|
|
296
|
+
this.channelTargetC.forEach((channel, id) => {
|
|
297
|
+
// log.debug('channel', channel);
|
|
298
|
+
// log.debug('id', id);
|
|
299
|
+
// log.debug('this.channelsVisible', this.channelsVisible);
|
|
300
|
+
if (this.channelsVisible[id] || true) { // TODO: remove true
|
|
301
|
+
// Since we don't have volume-based minMax, use fixed values
|
|
302
|
+
// or get them from your brick cache metadata if available
|
|
303
|
+
// big TODO
|
|
304
|
+
const max = this.maxRange ? this.maxRange : 255;
|
|
305
|
+
const minMax = [0, max]; // Default values
|
|
306
|
+
|
|
307
|
+
// log.debug('this.colors[id]', this.colors[id]);
|
|
308
|
+
// log.debug('this.channelsVisible[id]', this.channelsVisible[id]);
|
|
309
|
+
|
|
310
|
+
colorsSave.push([
|
|
311
|
+
this.colors[id][0] / 255,
|
|
312
|
+
this.colors[id][1] / 255,
|
|
313
|
+
this.colors[id][2] / 255,
|
|
314
|
+
this.channelsVisible[id] ? 1.0 : 0.0,
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
log.debug('colorsSave', colorsSave);
|
|
318
|
+
|
|
319
|
+
if (this.contrastLimits[id][0] === 0 && this.contrastLimits[id][1] === 255) {
|
|
320
|
+
contrastLimitsList.push([
|
|
321
|
+
normalizeValue(minMax[0], minMax),
|
|
322
|
+
normalizeValue(minMax[1], minMax),
|
|
323
|
+
]);
|
|
324
|
+
} else {
|
|
325
|
+
contrastLimitsList.push([
|
|
326
|
+
normalizeValue(this.contrastLimits[id][0], minMax),
|
|
327
|
+
normalizeValue(this.contrastLimits[id][1], minMax),
|
|
328
|
+
]);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (!this.zarrInit) {
|
|
334
|
+
// Initialize from zarrStore data
|
|
335
|
+
this.originalScale = originalScaleXYZ;
|
|
336
|
+
this.physicalDimensions = physicalDimensionsXYZ;
|
|
337
|
+
this.maxResolution = maxResolutionXYZ;
|
|
338
|
+
const scaledResolution = boxDimensionsXYZ;
|
|
339
|
+
this.normalizedScale = normalizedScaleXYZ;
|
|
340
|
+
|
|
341
|
+
this.meshScale = [
|
|
342
|
+
this.originalScale[0] / this.originalScale[0],
|
|
343
|
+
this.originalScale[1] / this.originalScale[0],
|
|
344
|
+
this.originalScale[2] / this.originalScale[0],
|
|
345
|
+
];
|
|
346
|
+
this.geometrySize = scaledResolution;
|
|
347
|
+
this.boxSize = scaledResolution;
|
|
348
|
+
|
|
349
|
+
log.debug('this.boxSize', this.boxSize);
|
|
350
|
+
log.debug('this.geometrySize', this.geometrySize);
|
|
351
|
+
log.debug('this.meshScale', this.meshScale);
|
|
352
|
+
log.debug('this.originalScale', this.originalScale);
|
|
353
|
+
log.debug('this.physicalDimensions', this.physicalDimensions);
|
|
354
|
+
log.debug('this.maxResolution', this.maxResolution);
|
|
355
|
+
log.debug('scaledResolution', scaledResolution);
|
|
356
|
+
|
|
357
|
+
this.zarrInit = true;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Update shader uniforms
|
|
361
|
+
this.updateUniforms(
|
|
362
|
+
texturesList,
|
|
363
|
+
dimensions, // Pass dimensions object instead of volume
|
|
364
|
+
this.renderingMode,
|
|
365
|
+
contrastLimitsList,
|
|
366
|
+
colorsSave,
|
|
367
|
+
this.layerTransparency,
|
|
368
|
+
this.xSlice, this.ySlice, this.zSlice,
|
|
369
|
+
bcTHREE,
|
|
370
|
+
ptTHREE,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
uniforms: this.uniforms,
|
|
375
|
+
shader: this.shader,
|
|
376
|
+
meshScale: this.meshScale,
|
|
377
|
+
geometrySize: this.geometrySize,
|
|
378
|
+
boxSize: this.boxSize,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Update shader uniforms with current rendering values
|
|
384
|
+
* @param {Array} textures - List of 3D textures
|
|
385
|
+
* @param {Object} dimensions - Dimensions object
|
|
386
|
+
* @param {number} renderstyle - Rendering mode value
|
|
387
|
+
* @param {Array} contrastLimits - List of contrast limits for each channel
|
|
388
|
+
* @param {Array} colors - List of colors for each channel
|
|
389
|
+
* @param {number} layerTransparency - Overall transparency value
|
|
390
|
+
* @param {Vector2} xSlice - X clipping plane
|
|
391
|
+
* @param {Vector2} ySlice - Y clipping plane
|
|
392
|
+
* @param {Vector2} zSlice - Z clipping plane
|
|
393
|
+
*/
|
|
394
|
+
updateUniforms(
|
|
395
|
+
textures, dimensions, renderstyle, contrastLimits, colors, layerTransparency,
|
|
396
|
+
xSlice, ySlice, zSlice,
|
|
397
|
+
brickCacheTexture, pageTableTexture,
|
|
398
|
+
) {
|
|
399
|
+
logWithColor('Updating uniforms');
|
|
400
|
+
// Set base uniforms
|
|
401
|
+
this.uniforms.boxSize.value.set(this.boxSize[0], this.boxSize[1], this.boxSize[2]);
|
|
402
|
+
|
|
403
|
+
// Set texture uniforms
|
|
404
|
+
this.uniforms.brickCacheTex.value = brickCacheTexture;
|
|
405
|
+
this.uniforms.pageTableTex.value = pageTableTexture;
|
|
406
|
+
|
|
407
|
+
// Set general rendering parameters
|
|
408
|
+
this.uniforms.near.value = 0.1;
|
|
409
|
+
this.uniforms.far.value = 3000; // TODO: check this
|
|
410
|
+
this.uniforms.opacity.value = layerTransparency;
|
|
411
|
+
this.uniforms.volumeCount.value = textures.length;
|
|
412
|
+
|
|
413
|
+
// Set size and scale parameters using dimensions object
|
|
414
|
+
this.uniforms.u_size.value.set(dimensions.xLength, dimensions.yLength, dimensions.zLength);
|
|
415
|
+
this.uniforms.u_window_size.value.set(0, 0);
|
|
416
|
+
this.uniforms.u_vol_scale.value.set(
|
|
417
|
+
1.0 / dimensions.xLength,
|
|
418
|
+
1.0 / dimensions.yLength,
|
|
419
|
+
1.0 / dimensions.zLength * 2.0,
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
// Set contrast limits (up to 6 channels)
|
|
423
|
+
this.uniforms.clim0.value.set(
|
|
424
|
+
contrastLimits.length > 0 ? contrastLimits[0][0] : null,
|
|
425
|
+
contrastLimits.length > 0 ? contrastLimits[0][1] : null,
|
|
426
|
+
);
|
|
427
|
+
this.uniforms.clim1.value.set(
|
|
428
|
+
contrastLimits.length > 1 ? contrastLimits[1][0] : null,
|
|
429
|
+
contrastLimits.length > 1 ? contrastLimits[1][1] : null,
|
|
430
|
+
);
|
|
431
|
+
this.uniforms.clim2.value.set(
|
|
432
|
+
contrastLimits.length > 2 ? contrastLimits[2][0] : null,
|
|
433
|
+
contrastLimits.length > 2 ? contrastLimits[2][1] : null,
|
|
434
|
+
);
|
|
435
|
+
this.uniforms.clim3.value.set(
|
|
436
|
+
contrastLimits.length > 3 ? contrastLimits[3][0] : null,
|
|
437
|
+
contrastLimits.length > 3 ? contrastLimits[3][1] : null,
|
|
438
|
+
);
|
|
439
|
+
this.uniforms.clim4.value.set(
|
|
440
|
+
contrastLimits.length > 4 ? contrastLimits[4][0] : null,
|
|
441
|
+
contrastLimits.length > 4 ? contrastLimits[4][1] : null,
|
|
442
|
+
);
|
|
443
|
+
this.uniforms.clim5.value.set(
|
|
444
|
+
contrastLimits.length > 5 ? contrastLimits[5][0] : null,
|
|
445
|
+
contrastLimits.length > 5 ? contrastLimits[5][1] : null,
|
|
446
|
+
);
|
|
447
|
+
this.uniforms.clim6.value.set(
|
|
448
|
+
contrastLimits.length > 6 ? contrastLimits[6][0] : null,
|
|
449
|
+
contrastLimits.length > 6 ? contrastLimits[6][1] : null,
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
// Set clipping planes
|
|
453
|
+
this.uniforms.xClip.value.set(
|
|
454
|
+
xSlice[0] * (1.0 / this.maxResolution[0]) * this.boxSize[0],
|
|
455
|
+
xSlice[1] * (1.0 / this.maxResolution[0]) * this.boxSize[0],
|
|
456
|
+
);
|
|
457
|
+
this.uniforms.yClip.value.set(
|
|
458
|
+
ySlice[0] * (1.0 / this.maxResolution[1]) * this.boxSize[1],
|
|
459
|
+
ySlice[1] * (1.0 / this.maxResolution[1]) * this.boxSize[1],
|
|
460
|
+
);
|
|
461
|
+
this.uniforms.zClip.value.set(
|
|
462
|
+
zSlice[0] * (1.0 / this.maxResolution[2]) * this.boxSize[2],
|
|
463
|
+
zSlice[1] * (1.0 / this.maxResolution[2]) * this.boxSize[2],
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
// Set colors (up to 7 channels)
|
|
467
|
+
this.uniforms.color0.value.set(
|
|
468
|
+
colors.length > 0 ? colors[0][0] : null,
|
|
469
|
+
colors.length > 0 ? colors[0][1] : null,
|
|
470
|
+
colors.length > 0 ? colors[0][2] : null,
|
|
471
|
+
colors.length > 0 ? colors[0][3] : null,
|
|
472
|
+
);
|
|
473
|
+
this.uniforms.color1.value.set(
|
|
474
|
+
colors.length > 1 ? colors[1][0] : null,
|
|
475
|
+
colors.length > 1 ? colors[1][1] : null,
|
|
476
|
+
colors.length > 1 ? colors[1][2] : null,
|
|
477
|
+
colors.length > 1 ? colors[1][3] : null,
|
|
478
|
+
);
|
|
479
|
+
this.uniforms.color2.value.set(
|
|
480
|
+
colors.length > 2 ? colors[2][0] : null,
|
|
481
|
+
colors.length > 2 ? colors[2][1] : null,
|
|
482
|
+
colors.length > 2 ? colors[2][2] : null,
|
|
483
|
+
colors.length > 2 ? colors[2][3] : null,
|
|
484
|
+
);
|
|
485
|
+
this.uniforms.color3.value.set(
|
|
486
|
+
colors.length > 3 ? colors[3][0] : null,
|
|
487
|
+
colors.length > 3 ? colors[3][1] : null,
|
|
488
|
+
colors.length > 3 ? colors[3][2] : null,
|
|
489
|
+
colors.length > 3 ? colors[3][3] : null,
|
|
490
|
+
);
|
|
491
|
+
this.uniforms.color4.value.set(
|
|
492
|
+
colors.length > 4 ? colors[4][0] : null,
|
|
493
|
+
colors.length > 4 ? colors[4][1] : null,
|
|
494
|
+
colors.length > 4 ? colors[4][2] : null,
|
|
495
|
+
colors.length > 4 ? colors[4][3] : null,
|
|
496
|
+
);
|
|
497
|
+
this.uniforms.color5.value.set(
|
|
498
|
+
colors.length > 5 ? colors[5][0] : null,
|
|
499
|
+
colors.length > 5 ? colors[5][1] : null,
|
|
500
|
+
colors.length > 5 ? colors[5][2] : null,
|
|
501
|
+
colors.length > 5 ? colors[5][3] : null,
|
|
502
|
+
);
|
|
503
|
+
this.uniforms.color6.value.set(
|
|
504
|
+
colors.length > 6 ? colors[6][0] : null,
|
|
505
|
+
colors.length > 6 ? colors[6][1] : null,
|
|
506
|
+
colors.length > 6 ? colors[6][2] : null,
|
|
507
|
+
colors.length > 6 ? colors[6][3] : null,
|
|
508
|
+
);
|
|
509
|
+
for (let i = 0; i < 7; i++) {
|
|
510
|
+
if (typeof this.channelMaxResolutionIndex[i] === 'number') {
|
|
511
|
+
this.uniforms[`res${i}`].value.set(
|
|
512
|
+
Math.max(1, this.channelMaxResolutionIndex[i]), this.zarrStoreNumResolutions - 1,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Sets the processing render target
|
|
520
|
+
* @param {WebGLMultipleRenderTargets} mrt - Multiple render targets object with 3 attachments
|
|
521
|
+
*/
|
|
522
|
+
/*
|
|
523
|
+
setProcessingTargets(mrt) {
|
|
524
|
+
logWithColor('setting processing targets');
|
|
525
|
+
this.mrt = mrt;
|
|
526
|
+
}
|
|
527
|
+
*/
|
|
528
|
+
|
|
529
|
+
setChannelMapping(channelMapping) {
|
|
530
|
+
logWithColor('setting channel mapping');
|
|
531
|
+
log.debug('channelMapping', channelMapping);
|
|
532
|
+
this.uniforms.channelMapping.value = channelMapping;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
// Only called on initialization of the
|
|
537
|
+
// VolumeDataManager and VolumeRenderManager for a particular image.
|
|
538
|
+
setZarrUniforms(
|
|
539
|
+
zarrStore, PT,
|
|
540
|
+
) {
|
|
541
|
+
logWithColor('setting zarr uniforms');
|
|
542
|
+
log.debug('zarrStore', zarrStore);
|
|
543
|
+
log.debug('PT', PT);
|
|
544
|
+
for (let i = 0; i <= 9; i++) {
|
|
545
|
+
if (PT.anchors && PT.anchors[i]) {
|
|
546
|
+
this.uniforms[`anchor${i}`].value.set(
|
|
547
|
+
PT.anchors[i][0] || 0,
|
|
548
|
+
PT.anchors[i][1] || 0,
|
|
549
|
+
PT.anchors[i][2] || 0,
|
|
550
|
+
);
|
|
551
|
+
} else {
|
|
552
|
+
// Set default values if anchor doesn't exist
|
|
553
|
+
log.debug('anchor', i, 'does not exist');
|
|
554
|
+
this.uniforms[`anchor${i}`].value.set(0, 0, 0);
|
|
555
|
+
}
|
|
556
|
+
if (zarrStore.scales && zarrStore.scales[i]) {
|
|
557
|
+
this.uniforms[`scale${i}`].value.set(
|
|
558
|
+
zarrStore.scales[i][0] || 1,
|
|
559
|
+
zarrStore.scales[i][1] || 1,
|
|
560
|
+
zarrStore.scales[i][2] || 1,
|
|
561
|
+
);
|
|
562
|
+
} else {
|
|
563
|
+
log.debug('scale', i, 'does not exist');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
log.debug('zarrStore.brickLayout', zarrStore.brickLayout);
|
|
567
|
+
this.zarrStoreNumResolutions = zarrStore.brickLayout.length;
|
|
568
|
+
for (let i = 0; i < 7; i++) {
|
|
569
|
+
this.uniforms[`res${i}`].value.set(
|
|
570
|
+
// TODO(mark) make this dependent on the per-channel maxResolution slider value.
|
|
571
|
+
1, zarrStore.brickLayout.length - 1,
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
this.uniforms.resGlobal.value.set(
|
|
575
|
+
1, zarrStore.brickLayout.length - 1,
|
|
576
|
+
);
|
|
577
|
+
this.uniforms.voxelExtents.value.set(
|
|
578
|
+
zarrStore.shapes[0][4],
|
|
579
|
+
zarrStore.shapes[0][3],
|
|
580
|
+
zarrStore.shapes[0][2],
|
|
581
|
+
);
|
|
582
|
+
this.uniforms.maxChannels.value = Math.min(zarrStore.channelCount, 7);
|
|
583
|
+
log.debug('this.channelsVisible', this.channelsVisible);
|
|
584
|
+
log.debug('zarrStore.shapes[0]', zarrStore.shapes[0]);
|
|
585
|
+
log.debug('PT', PT);
|
|
586
|
+
log.debug('uniforms', this.uniforms);
|
|
587
|
+
// this.uniforms.physicalScale.value.set(physicalScale);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Vector2, Vector3, Vector4 } from 'three';
|
|
2
|
+
import { volumeVertexShader } from './shaders/VolumeVertexShader.js';
|
|
3
|
+
import { volumeFragmentShader } from './shaders/VolumeFragmentShader.js';
|
|
4
|
+
|
|
5
|
+
export const VolumeShader = {
|
|
6
|
+
uniforms: {
|
|
7
|
+
u_size: { value: new Vector3(1, 1, 1) },
|
|
8
|
+
clim0: { value: new Vector2(0.2, 0.8) },
|
|
9
|
+
clim1: { value: new Vector2(0.2, 0.8) },
|
|
10
|
+
clim2: { value: new Vector2(0.2, 0.8) },
|
|
11
|
+
clim3: { value: new Vector2(0.2, 0.8) },
|
|
12
|
+
clim4: { value: new Vector2(0.2, 0.8) },
|
|
13
|
+
clim5: { value: new Vector2(0.2, 0.8) },
|
|
14
|
+
clim6: { value: new Vector2(0.2, 0.8) },
|
|
15
|
+
clim7: { value: new Vector2(0.2, 0.8) },
|
|
16
|
+
xClip: { value: new Vector2(0, 1000000.0) },
|
|
17
|
+
yClip: { value: new Vector2(0, 1000000.0) },
|
|
18
|
+
zClip: { value: new Vector2(0, 1000000.0) },
|
|
19
|
+
u_window_size: { value: new Vector2(1, 1) },
|
|
20
|
+
u_vol_scale: { value: new Vector3(1, 1, 1) },
|
|
21
|
+
u_renderstyle: { value: 2 },
|
|
22
|
+
brickCacheTex: { type: 'sampler3D', value: null },
|
|
23
|
+
pageTableTex: { type: 'usampler3D', value: null },
|
|
24
|
+
color0: { value: new Vector4(0, 0, 0) },
|
|
25
|
+
color1: { value: new Vector4(0, 0, 0) },
|
|
26
|
+
color2: { value: new Vector4(0, 0, 0) },
|
|
27
|
+
color3: { value: new Vector4(0, 0, 0) },
|
|
28
|
+
color4: { value: new Vector4(0, 0, 0) },
|
|
29
|
+
color5: { value: new Vector4(0, 0, 0) },
|
|
30
|
+
color6: { value: new Vector4(0, 0, 0) },
|
|
31
|
+
channelMapping: {
|
|
32
|
+
value: [-1, -1, -1, -1, -1, -1, -1],
|
|
33
|
+
},
|
|
34
|
+
resGlobal: { value: new Vector2(0, 9) },
|
|
35
|
+
res0: { value: new Vector2(0, 9) },
|
|
36
|
+
res1: { value: new Vector2(0, 9) },
|
|
37
|
+
res2: { value: new Vector2(0, 9) },
|
|
38
|
+
res3: { value: new Vector2(0, 9) },
|
|
39
|
+
res4: { value: new Vector2(0, 9) },
|
|
40
|
+
res5: { value: new Vector2(0, 9) },
|
|
41
|
+
res6: { value: new Vector2(0, 9) },
|
|
42
|
+
maxChannels: { value: 0 },
|
|
43
|
+
lodFactor: { value: 1.0 },
|
|
44
|
+
near: { value: 0.1 },
|
|
45
|
+
far: { value: 10000 },
|
|
46
|
+
opacity: { value: 1 },
|
|
47
|
+
volumeCount: { value: 0 },
|
|
48
|
+
boxSize: { value: new Vector3(1, 1, 1) },
|
|
49
|
+
renderRes: { value: 1000 },
|
|
50
|
+
voxelExtents: { value: new Vector3(1, 1, 1) },
|
|
51
|
+
anchor0: { value: new Vector3(0, 0, 0) },
|
|
52
|
+
anchor1: { value: new Vector3(0, 0, 0) },
|
|
53
|
+
anchor2: { value: new Vector3(0, 0, 0) },
|
|
54
|
+
anchor3: { value: new Vector3(0, 0, 0) },
|
|
55
|
+
anchor4: { value: new Vector3(0, 0, 0) },
|
|
56
|
+
anchor5: { value: new Vector3(0, 0, 0) },
|
|
57
|
+
anchor6: { value: new Vector3(0, 0, 0) },
|
|
58
|
+
anchor7: { value: new Vector3(0, 0, 0) },
|
|
59
|
+
anchor8: { value: new Vector3(0, 0, 0) },
|
|
60
|
+
anchor9: { value: new Vector3(0, 0, 0) },
|
|
61
|
+
scale0: { value: new Vector3(1, 1, 1) },
|
|
62
|
+
scale1: { value: new Vector3(2, 2, 2) },
|
|
63
|
+
scale2: { value: new Vector3(4, 4, 4) },
|
|
64
|
+
scale3: { value: new Vector3(8, 8, 8) },
|
|
65
|
+
scale4: { value: new Vector3(16, 16, 16) },
|
|
66
|
+
scale5: { value: new Vector3(32, 32, 32) },
|
|
67
|
+
scale6: { value: new Vector3(64, 64, 64) },
|
|
68
|
+
scale7: { value: new Vector3(128, 128, 128) },
|
|
69
|
+
scale8: { value: new Vector3(256, 256, 256) },
|
|
70
|
+
scale9: { value: new Vector3(512, 512, 512) },
|
|
71
|
+
},
|
|
72
|
+
vertexShader: volumeVertexShader,
|
|
73
|
+
fragmentShader: volumeFragmentShader,
|
|
74
|
+
};
|