@kitware/vtk.js 25.7.2 → 25.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.
@@ -25,9 +25,9 @@ export interface vtkCellArray extends vtkDataArray {
25
25
 
26
26
  /**
27
27
  * Set the data of this array.
28
- * @param {TypedArray} typedArray The Array value.
28
+ * @param {Number[]|TypedArray} typedArray The Array value.
29
29
  */
30
- setData(typedArray: TypedArray): void;
30
+ setData(typedArray: number[]|TypedArray): void;
31
31
 
32
32
  /**
33
33
  * Returns the point indices at the given location as a subarray.
@@ -1,5 +1,5 @@
1
1
  import { vtkObject, vtkRange } from './../../interfaces';
2
- import { float, int, Nullable, TypedArray } from './../../types';
2
+ import { float, int, Nullable, Range, TypedArray } from './../../types';
3
3
 
4
4
 
5
5
  /**
@@ -30,7 +30,7 @@ export interface IDataArrayInitialValues {
30
30
  empty?: boolean;
31
31
  name?: string;
32
32
  numberOfComponents?: number;
33
- rangeTuple?: [number, number];
33
+ rangeTuple?: Range;
34
34
  size?: number;
35
35
  values?: Array<number>|TypedArray;
36
36
  }
@@ -60,21 +60,21 @@ export interface vtkDataArray extends vtkObject {
60
60
  /**
61
61
  *
62
62
  */
63
- getData(): TypedArray;
63
+ getData(): number[]|TypedArray;
64
64
 
65
65
  /**
66
66
  * Get the range of the given component.
67
67
  *
68
68
  * @param {Number} componentIndex (default: -1)
69
69
  */
70
- getRange(componentIndex?: number): [number, number];
70
+ getRange(componentIndex?: number): Range;
71
71
 
72
72
  /**
73
73
  *
74
74
  * @param {vtkRange} rangeValue
75
75
  * @param {Number} componentIndex
76
76
  */
77
- setRange(rangeValue: vtkRange, componentIndex: number): [number, number];
77
+ setRange(rangeValue: vtkRange, componentIndex: number): Range;
78
78
 
79
79
  /**
80
80
  * Set the given tuple at the given index.
@@ -93,17 +93,20 @@ export interface vtkDataArray extends vtkObject {
93
93
  /**
94
94
  * Get the tuple at the given index.
95
95
  *
96
- * The recommended use is the following:
96
+ * For performance reasons, it is advised to pass a 'tupleToFill':
97
97
  * `const x = [];`
98
- * `dataArray.getTuple(idx, x);`
98
+ * `for (int i = 0; i < N; ++i) {
99
+ * ` dataArray.getTuple(idx, x);`
100
+ * ` ...`
99
101
  * instead of:
100
- * `const x = dataArray.getTuple(idx);`
101
- *
102
+ * `for (int i = 0; i < N; ++i) {
103
+ * ` const x = dataArray.getTuple(idx);`
104
+ * `...`
102
105
  * @param {Number} idx
103
- * @param {Array<Number>} [tupleToFill] (default [])
104
- * @returns {Array<Number>}
106
+ * @param {Number[]|TypedArray} [tupleToFill] (default [])
107
+ * @returns {Number[]|TypedArray}
105
108
  */
106
- getTuple(idx: number, tupleToFill?: Array<number>): Array<number>;
109
+ getTuple(idx: number, tupleToFill?: number[]|TypedArray): number[]|TypedArray;
107
110
 
108
111
  /**
109
112
  * Get the tuples between fromId (inclusive) and toId (exclusive).
@@ -223,10 +226,10 @@ export interface vtkDataArray extends vtkObject {
223
226
  * If this dataArray's numberOfComponents doesn't divide the given array's
224
227
  * length, this dataArray's numberOfComponents is set to 1.
225
228
  *
226
- * @param {TypedArray} typedArray
227
- * @param {Number} [numberOfComponents]
229
+ * @param {Number[]|TypedArray} typedArray The Array value.
230
+ * @param {Number} [numberOfComponents]
228
231
  */
229
- setData(typedArray: TypedArray, numberOfComponents?: number): void;
232
+ setData(typedArray: number[]|TypedArray, numberOfComponents?: number): void;
230
233
 
231
234
  /**
232
235
  * Get the state of this array.
@@ -7,8 +7,7 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
7
7
 
8
8
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
9
9
  var vtkErrorMacro = vtkErrorMacro$1;
10
- var DefaultDataType = Constants.DefaultDataType;
11
- var TUPLE_HOLDER = []; // ----------------------------------------------------------------------------
10
+ var DefaultDataType = Constants.DefaultDataType; // ----------------------------------------------------------------------------
12
11
  // Global methods
13
12
  // ----------------------------------------------------------------------------
14
13
  // Original source from https://www.npmjs.com/package/compute-range
@@ -132,9 +131,11 @@ function getDataType(typedArray) {
132
131
  function getMaxNorm(normArray) {
133
132
  var numComps = normArray.getNumberOfComponents();
134
133
  var maxNorm = 0.0;
134
+ var tuple = new Array(numComps);
135
135
 
136
136
  for (var i = 0; i < normArray.getNumberOfTuples(); ++i) {
137
- var norm$1 = norm(normArray.getTuple(i), numComps);
137
+ normArray.getTuple(i, tuple);
138
+ var norm$1 = norm(tuple, numComps);
138
139
 
139
140
  if (norm$1 > maxNorm) {
140
141
  maxNorm = norm$1;
@@ -335,13 +336,8 @@ function vtkDataArray(publicAPI, model) {
335
336
  };
336
337
 
337
338
  publicAPI.getTuple = function (idx) {
338
- var tupleToFill = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TUPLE_HOLDER;
339
+ var tupleToFill = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
339
340
  var numberOfComponents = model.numberOfComponents || 1;
340
-
341
- if (tupleToFill.length !== numberOfComponents) {
342
- tupleToFill.length = numberOfComponents;
343
- }
344
-
345
341
  var offset = idx * numberOfComponents; // Check most common component sizes first
346
342
  // to avoid doing a for loop if possible
347
343
 
@@ -363,7 +359,7 @@ function vtkDataArray(publicAPI, model) {
363
359
  break;
364
360
 
365
361
  default:
366
- for (var i = 0; i < numberOfComponents; i++) {
362
+ for (var i = numberOfComponents - 1; i >= 0; --i) {
367
363
  tupleToFill[i] = model.values[offset + i];
368
364
  }
369
365
 
@@ -482,12 +478,10 @@ function vtkDataArray(publicAPI, model) {
482
478
  vtkErrorMacro('numberOfComponents must match');
483
479
  }
484
480
 
485
- var tuple1 = [];
486
- var tuple2 = [];
481
+ var tuple1 = source1.getTuple(source1Idx);
482
+ var tuple2 = source2.getTuple(source2Idx);
487
483
  var out = [];
488
- out.length = numberOfComponents;
489
- source1.getTuple(source1Idx, tuple1);
490
- source2.getTuple(source2Idx, tuple2); // Check most common component sizes first
484
+ out.length = numberOfComponents; // Check most common component sizes first
491
485
  // to avoid doing a for loop if possible
492
486
 
493
487
  switch (numberOfComponents) {
@@ -78,11 +78,11 @@ declare interface Transform {
78
78
  * iterations (sets of 3) to loop through. Assumes the `typedArray` is an
79
79
  * array of multiples of 3, unless specifically handling with offset and
80
80
  * iterations. Returns the instance for chaining.
81
- * @param {TypedArray} typedArray
81
+ * @param {Number[]|TypedArray} typedArray The Array value.
82
82
  * @param {Number} [offset]
83
83
  * @param {Number} [nbIterations]
84
84
  */
85
- apply(typedArray: TypedArray, offset?: number, nbIterations?: number): Transform
85
+ apply(typedArray: number[]|TypedArray, offset?: number, nbIterations?: number): Transform
86
86
 
87
87
  /**
88
88
  * Returns the internal `mat4` matrix.
@@ -1,5 +1,5 @@
1
1
  import vtkDataArray from './DataArray';
2
- import { Bounds } from './../../types';
2
+ import { Bounds, TypedArray } from './../../types';
3
3
 
4
4
  /**
5
5
  *
@@ -26,10 +26,10 @@ export interface vtkPoints extends vtkDataArray {
26
26
  /**
27
27
  * Get the coordinate of a point.
28
28
  * @param {Number} idx The index of point.
29
- * @param {Number[]} [tupleToFill]
30
- * @default []
29
+ * @param {Number[]|TypedArray} [tupleToFill] (default [])
30
+ * @returns {Number[]|TypedArray}
31
31
  */
32
- getPoint(idx: number, tupleToFill?: number[]): number[];
32
+ getPoint(idx: number, tupleToFill?: number[]|TypedArray): number[]|TypedArray;
33
33
 
34
34
  /**
35
35
  * Get the number of points for this object can hold.
@@ -1,7 +1,6 @@
1
1
  import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
2
2
  import macro from '../../macros.js';
3
3
 
4
- var TUPLE_HOLDER = []; // ----------------------------------------------------------------------------
5
4
  // vtkStringArray methods
6
5
  // ----------------------------------------------------------------------------
7
6
 
@@ -34,7 +33,7 @@ function vtkStringArray(publicAPI, model) {
34
33
  };
35
34
 
36
35
  publicAPI.getTuple = function (idx) {
37
- var tupleToFill = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TUPLE_HOLDER;
36
+ var tupleToFill = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
38
37
  var numberOfComponents = model.numberOfComponents || 1;
39
38
 
40
39
  if (tupleToFill.length) {
@@ -1,7 +1,6 @@
1
1
  import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
2
2
  import macro from '../../macros.js';
3
3
 
4
- var TUPLE_HOLDER = []; // ----------------------------------------------------------------------------
5
4
  // vtkVariantArray methods
6
5
  // ----------------------------------------------------------------------------
7
6
 
@@ -34,7 +33,7 @@ function vtkVariantArray(publicAPI, model) {
34
33
  };
35
34
 
36
35
  publicAPI.getTuple = function (idx) {
37
- var tupleToFill = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TUPLE_HOLDER;
36
+ var tupleToFill = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
38
37
  var numberOfComponents = model.numberOfComponents || 1;
39
38
 
40
39
  if (tupleToFill.length) {
@@ -56,6 +56,24 @@ function _addPoint(bounds) {
56
56
  return bounds;
57
57
  }
58
58
 
59
+ function _addPoints(bounds, points) {
60
+ if (points.length === 0) {
61
+ return bounds;
62
+ }
63
+
64
+ if (Array.isArray(points[0])) {
65
+ for (var i = 0; i < points.length; ++i) {
66
+ _addPoint(bounds, points[i]);
67
+ }
68
+ } else {
69
+ for (var _i = 0; _i < points.length; _i += 3) {
70
+ _addPoint(bounds, points.slice(_i, _i + 3));
71
+ }
72
+ }
73
+
74
+ return bounds;
75
+ }
76
+
59
77
  function _addBounds(bounds, xMin, xMax, yMin, yMax, zMin, zMax) {
60
78
  var _bounds2 = _slicedToArray(bounds, 6),
61
79
  _xMin = _bounds2[0],
@@ -337,18 +355,18 @@ function _intersectBox(bounds, origin, dir, coord, tolerance) {
337
355
  } // Calculate parametric distance to plane
338
356
 
339
357
 
340
- for (var _i = 0; _i < 3; _i++) {
341
- if (quadrant[_i] !== MIDDLE && dir[_i] !== 0.0) {
342
- maxT[_i] = (candidatePlane[_i] - origin[_i]) / dir[_i];
358
+ for (var _i2 = 0; _i2 < 3; _i2++) {
359
+ if (quadrant[_i2] !== MIDDLE && dir[_i2] !== 0.0) {
360
+ maxT[_i2] = (candidatePlane[_i2] - origin[_i2]) / dir[_i2];
343
361
  } else {
344
- maxT[_i] = -1.0;
362
+ maxT[_i2] = -1.0;
345
363
  }
346
364
  } // Find the largest parametric value of intersection
347
365
 
348
366
 
349
- for (var _i2 = 0; _i2 < 3; _i2++) {
350
- if (maxT[whichPlane] < maxT[_i2]) {
351
- whichPlane = _i2;
367
+ for (var _i3 = 0; _i3 < 3; _i3++) {
368
+ if (maxT[whichPlane] < maxT[_i3]) {
369
+ whichPlane = _i3;
352
370
  }
353
371
  } // Check for valie intersection along line
354
372
 
@@ -359,15 +377,15 @@ function _intersectBox(bounds, origin, dir, coord, tolerance) {
359
377
 
360
378
  tolerance[0] = maxT[whichPlane]; // Intersection point along line is okay. Check bbox.
361
379
 
362
- for (var _i3 = 0; _i3 < 3; _i3++) {
363
- if (whichPlane !== _i3) {
364
- coord[_i3] = origin[_i3] + maxT[whichPlane] * dir[_i3];
380
+ for (var _i4 = 0; _i4 < 3; _i4++) {
381
+ if (whichPlane !== _i4) {
382
+ coord[_i4] = origin[_i4] + maxT[whichPlane] * dir[_i4];
365
383
 
366
- if (coord[_i3] < bounds[2 * _i3] || coord[_i3] > bounds[2 * _i3 + 1]) {
384
+ if (coord[_i4] < bounds[2 * _i4] || coord[_i4] > bounds[2 * _i4 + 1]) {
367
385
  return 0;
368
386
  }
369
387
  } else {
370
- coord[_i3] = candidatePlane[_i3];
388
+ coord[_i4] = candidatePlane[_i4];
371
389
  }
372
390
  }
373
391
 
@@ -623,6 +641,11 @@ var BoundingBox = /*#__PURE__*/function () {
623
641
 
624
642
  return _addPoint(this.bounds, xyz);
625
643
  }
644
+ }, {
645
+ key: "addPoints",
646
+ value: function addPoints(points) {
647
+ return _addPoints(this.bounds, points);
648
+ }
626
649
  }, {
627
650
  key: "addBounds",
628
651
  value: function addBounds(xMin, xMax, yMin, yMax, zMin, zMax) {
@@ -772,6 +795,7 @@ var STATIC = {
772
795
  setBounds: _setBounds,
773
796
  reset: _reset,
774
797
  addPoint: _addPoint,
798
+ addPoints: _addPoints,
775
799
  addBounds: _addBounds,
776
800
  setMinPoint: _setMinPoint,
777
801
  setMaxPoint: _setMaxPoint,
@@ -805,4 +829,4 @@ var vtkBoundingBox = _objectSpread({
805
829
  newInstance: newInstance
806
830
  }, STATIC);
807
831
 
808
- export { STATIC, _addBounds as addBounds, _addPoint as addPoint, _computeCornerPoints as computeCornerPoints, _computeLocalBounds as computeLocalBounds, _computeScale as computeScale3, contains, _containsPoint as containsPoint, _cutWithPlane as cutWithPlane, vtkBoundingBox as default, _equals as equals, _getCenter as getCenter, _getCorners as getCorners, _getDiagonalLength as getDiagonalLength, _getLength as getLength, _getLengths as getLengths, _getMaxLength as getMaxLength, _getMaxPoint as getMaxPoint, _getMinPoint as getMinPoint, _getXRange as getXRange, _getYRange as getYRange, _getZRange as getZRange, _inflate as inflate, _intersect as intersect, _intersectBox as intersectBox, _intersectPlane as intersectPlane, _intersects as intersects, _isValid as isValid, _reset as reset, _scale as scale, scaleAboutCenter, _setBounds as setBounds, _setMaxPoint as setMaxPoint, _setMinPoint as setMinPoint };
832
+ export { STATIC, _addBounds as addBounds, _addPoint as addPoint, _addPoints as addPoints, _computeCornerPoints as computeCornerPoints, _computeLocalBounds as computeLocalBounds, _computeScale as computeScale3, contains, _containsPoint as containsPoint, _cutWithPlane as cutWithPlane, vtkBoundingBox as default, _equals as equals, _getCenter as getCenter, _getCorners as getCorners, _getDiagonalLength as getDiagonalLength, _getLength as getLength, _getLengths as getLengths, _getMaxLength as getMaxLength, _getMaxPoint as getMaxPoint, _getMinPoint as getMinPoint, _getXRange as getXRange, _getYRange as getYRange, _getZRange as getZRange, _inflate as inflate, _intersect as intersect, _intersectBox as intersectBox, _intersectPlane as intersectPlane, _intersects as intersects, _isValid as isValid, _reset as reset, _scale as scale, scaleAboutCenter, _setBounds as setBounds, _setMaxPoint as setMaxPoint, _setMinPoint as setMinPoint };
@@ -1,11 +1,12 @@
1
1
  import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
2
+ import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
2
3
  import macro from '../../macros.js';
3
4
  import { c as computeBoundsFromPoints, b as roundVector, e as clampVector } from '../Core/Math/index.js';
4
5
  import vtkBoundingBox from './BoundingBox.js';
5
6
  import vtkDataSet from './DataSet.js';
6
7
  import vtkStructuredData from './StructuredData.js';
7
8
  import { StructuredType } from './StructuredData/Constants.js';
8
- import { mat3, vec3, mat4 } from 'gl-matrix';
9
+ import { mat3, mat4, vec3 } from 'gl-matrix';
9
10
 
10
11
  var vtkErrorMacro = macro.vtkErrorMacro; // ----------------------------------------------------------------------------
11
12
  // vtkImageData methods
@@ -31,15 +32,8 @@ function vtkImageData(publicAPI, model) {
31
32
  return false;
32
33
  }
33
34
 
34
- var changeDetected = false;
35
- model.extent.forEach(function (item, index) {
36
- if (item !== extentArray[index]) {
37
- if (changeDetected) {
38
- return;
39
- }
40
-
41
- changeDetected = true;
42
- }
35
+ var changeDetected = model.extent.some(function (item, index) {
36
+ return item !== extentArray[index];
43
37
  });
44
38
 
45
39
  if (changeDetected) {
@@ -196,49 +190,22 @@ function vtkImageData(publicAPI, model) {
196
190
 
197
191
  publicAPI.extentToBounds = function (ex) {
198
192
  // prettier-ignore
199
- var corners = [ex[0], ex[2], ex[4], ex[1], ex[2], ex[4], ex[0], ex[3], ex[4], ex[1], ex[3], ex[4], ex[0], ex[2], ex[5], ex[1], ex[2], ex[5], ex[0], ex[3], ex[5], ex[1], ex[3], ex[5]];
200
- var idx = new Float64Array([corners[0], corners[1], corners[2]]);
201
- var vout = new Float64Array(3);
202
- publicAPI.indexToWorld(idx, vout);
203
- var bounds = [vout[0], vout[0], vout[1], vout[1], vout[2], vout[2]];
204
-
205
- for (var i = 3; i < 24; i += 3) {
206
- vec3.set(idx, corners[i], corners[i + 1], corners[i + 2]);
207
- publicAPI.indexToWorld(idx, vout);
208
-
209
- if (vout[0] < bounds[0]) {
210
- bounds[0] = vout[0];
211
- }
193
+ var corners = [[ex[0], ex[2], ex[4]], [ex[1], ex[2], ex[4]], [ex[0], ex[3], ex[4]], [ex[1], ex[3], ex[4]], [ex[0], ex[2], ex[5]], [ex[1], ex[2], ex[5]], [ex[0], ex[3], ex[5]], [ex[1], ex[3], ex[5]]];
212
194
 
213
- if (vout[1] < bounds[2]) {
214
- bounds[2] = vout[1];
215
- }
195
+ var bounds = _toConsumableArray(vtkBoundingBox.INIT_BOUNDS);
216
196
 
217
- if (vout[2] < bounds[4]) {
218
- bounds[4] = vout[2];
219
- }
197
+ var vout = [];
220
198
 
221
- if (vout[0] > bounds[1]) {
222
- bounds[1] = vout[0];
223
- }
224
-
225
- if (vout[1] > bounds[3]) {
226
- bounds[3] = vout[1];
227
- }
228
-
229
- if (vout[2] > bounds[5]) {
230
- bounds[5] = vout[2];
231
- }
199
+ for (var i = 0; i < 8; ++i) {
200
+ publicAPI.indexToWorld(corners[i], vout);
201
+ vtkBoundingBox.addPoint.apply(vtkBoundingBox, [bounds].concat(vout));
232
202
  }
233
203
 
234
204
  return bounds;
235
205
  };
236
206
 
237
207
  publicAPI.getSpatialExtent = function () {
238
- var boundingBox = vtkBoundingBox.newInstance();
239
- boundingBox.setBounds(model.extent);
240
- boundingBox.inflate(0.5);
241
- return boundingBox.getBounds();
208
+ return vtkBoundingBox.inflate(_toConsumableArray(model.extent), 0.5);
242
209
  }; // Internal, shouldn't need to call this manually.
243
210
 
244
211
 
@@ -351,14 +318,7 @@ function vtkImageData(publicAPI, model) {
351
318
  publicAPI.computeTransforms();
352
319
 
353
320
  publicAPI.getCenter = function () {
354
- var bounds = publicAPI.getBounds();
355
- var center = [];
356
-
357
- for (var i = 0; i < 3; i++) {
358
- center[i] = (bounds[2 * i + 1] + bounds[2 * i]) / 2;
359
- }
360
-
361
- return center;
321
+ return vtkBoundingBox.getCenter(publicAPI.getBounds());
362
322
  };
363
323
 
364
324
  publicAPI.computeHistogram = function (worldBounds) {
@@ -91,10 +91,8 @@ function vtkClipClosedSurface(publicAPI, model) {
91
91
  } // Get the edge and interpolate the new point
92
92
 
93
93
 
94
- var p0 = [];
95
- var p1 = [];
96
- points.getPoint(i0, p0);
97
- points.getPoint(i1, p1);
94
+ var p0 = points.getPoint(i0);
95
+ var p1 = points.getPoint(i1);
98
96
  var f = v0 / (v0 - v1);
99
97
  var s = 1.0 - f;
100
98
  var t = 1.0 - s;
@@ -114,9 +114,10 @@ function vtkImageStreamline(publicAPI, model) {
114
114
  velAtArg[0] = 0.0;
115
115
  velAtArg[1] = 0.0;
116
116
  velAtArg[2] = 0.0;
117
+ var vel = new Array(3);
117
118
 
118
119
  for (var i = 0; i < 8; i++) {
119
- var vel = velArray.getTuple(voxelIndices[i]);
120
+ velArray.getTuple(voxelIndices[i], vel);
120
121
 
121
122
  for (var j = 0; j < 3; j++) {
122
123
  velAtArg[j] += weights[i] * vel[j];
@@ -212,9 +213,11 @@ function vtkImageStreamline(publicAPI, model) {
212
213
  var offset = 0;
213
214
  var datas = [];
214
215
  var vectors = input.getPointData().getVectors();
216
+ var point = [];
215
217
 
216
218
  for (var i = 0; i < nSeeds; i++) {
217
- var retVal = publicAPI.streamIntegrate(vectors, input, seedPts.getTuple(i), offset);
219
+ seedPts.getTuple(i, point);
220
+ var retVal = publicAPI.streamIntegrate(vectors, input, point, offset);
218
221
  offset += retVal[0].length / 3;
219
222
  datas.push(retVal);
220
223
  }
@@ -89,10 +89,11 @@ function vtkPaintFilter(publicAPI, model) {
89
89
 
90
90
  if (model.voxelFunc) {
91
91
  var bgScalars = model.backgroundImage.getPointData().getScalars();
92
+ var voxel = [];
92
93
 
93
94
  for (var _i = 0; _i < maskLabelMap.length; _i++) {
94
95
  if (maskLabelMap[_i]) {
95
- var voxel = bgScalars.getTuple(_i); // might not fill up snapshot
96
+ bgScalars.getTuple(_i, voxel); // might not fill up snapshot
96
97
 
97
98
  if (model.voxelFunc(voxel, _i, label)) {
98
99
  snapshot[diffIdx++] = [_i, data[_i]];
@@ -236,6 +236,7 @@ function vtkTubeFilter(publicAPI, model) {
236
236
  var normal = [0.0, 0.0, 0.0];
237
237
  var sFactor = 1.0;
238
238
  var ptId = offset;
239
+ var vector = [];
239
240
 
240
241
  for (var j = 0; j < npts; ++j) {
241
242
  // First point
@@ -307,7 +308,7 @@ function vtkTubeFilter(publicAPI, model) {
307
308
  if (inScalars && model.varyRadius === VaryRadius.VARY_RADIUS_BY_SCALAR) {
308
309
  sFactor = 1.0 + (model.radiusFactor - 1.0) * (inScalars.getComponent(pts[j], 0) - range[0]) / (range[1] - range[0]);
309
310
  } else if (inVectors && model.varyRadius === VaryRadius.VARY_RADIUS_BY_VECTOR) {
310
- sFactor = Math.sqrt(maxSpeed / norm(inVectors.getTuple(pts[j])));
311
+ sFactor = Math.sqrt(maxSpeed / norm(inVectors.getTuple(pts[j], vector)));
311
312
 
312
313
  if (sFactor > model.radiusFactor) {
313
314
  sFactor = model.radiusFactor;
@@ -667,16 +668,10 @@ function vtkTubeFilter(publicAPI, model) {
667
668
  var numStrips = 0;
668
669
  var inLinesData = inLines.getData();
669
670
  var npts = inLinesData[0];
670
- var sidesShareVerticesMultiplier = model.sidesShareVertices ? 1 : 2;
671
671
 
672
672
  for (var i = 0; i < inLinesData.length; i += npts + 1) {
673
- numNewPts += sidesShareVerticesMultiplier * npts * model.numberOfSides;
674
-
675
- if (model.capping) {
676
- numNewPts += 2 * model.numberOfSides;
677
- }
678
-
679
673
  npts = inLinesData[i];
674
+ numNewPts = computeOffset(numNewPts, npts);
680
675
  numStrips += (2 * npts + 1) * Math.ceil(model.numberOfSides / model.onRatio);
681
676
 
682
677
  if (model.capping) {
@@ -1,4 +1,3 @@
1
- import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
2
1
  import macro from '../../macros.js';
3
2
  import vtkBoundingBox from '../../Common/DataModel/BoundingBox.js';
4
3
  import vtkDataArray from '../../Common/Core/DataArray.js';
@@ -177,13 +176,13 @@ function vtkWindowedSincPolyDataFilter(publicAPI, model) {
177
176
  if (model.featureEdgeSmoothing) {
178
177
  // TODO: support polygons
179
178
  // vtkPolygon::ComputeNormal(inPts,npts,pts,normal);
180
- vtkTriangle.computeNormal(_toConsumableArray(inPts.getPoint(_pts2[0])), _toConsumableArray(inPts.getPoint(_pts2[1])), _toConsumableArray(inPts.getPoint(_pts2[2])), normal);
179
+ vtkTriangle.computeNormal(inPts.getPoint(_pts2[0]), inPts.getPoint(_pts2[1]), inPts.getPoint(_pts2[2]), normal);
181
180
 
182
181
  var _mesh$getCellPoints = mesh.getCellPoints(nei),
183
182
  cellPointIds = _mesh$getCellPoints.cellPointIds; // vtkPolygon::ComputeNormal(inPts,numNeiPts,neiPts,neiNormal);
184
183
 
185
184
 
186
- vtkTriangle.computeNormal(_toConsumableArray(inPts.getPoint(cellPointIds[0])), _toConsumableArray(inPts.getPoint(cellPointIds[1])), _toConsumableArray(inPts.getPoint(cellPointIds[2])), neiNormal);
185
+ vtkTriangle.computeNormal(inPts.getPoint(cellPointIds[0]), inPts.getPoint(cellPointIds[1]), inPts.getPoint(cellPointIds[2]), neiNormal);
187
186
 
188
187
  if (vtkMath.dot(normal, neiNormal) <= cosFeatureAngle) {
189
188
  edge = VertexType.VTK_FEATURE_EDGE_VERTEX;
@@ -230,12 +229,11 @@ function vtkWindowedSincPolyDataFilter(publicAPI, model) {
230
229
  verts[_i4].type = VertexType.VTK_FIXED_VERTEX;
231
230
  } // check angle between edges
232
231
  else {
233
- var _x = [0, 0, 0];
234
- inPts.getPoint(verts[_i4].edges[0], _x);
235
- var _x2 = [0, 0, 0];
236
- inPts.getPoint(_i4, _x2);
237
- var x3 = [0, 0, 0];
238
- inPts.getPoint(verts[_i4].edges[1], x3);
232
+ var _x = inPts.getPoint(verts[_i4].edges[0]);
233
+
234
+ var _x2 = inPts.getPoint(_i4);
235
+
236
+ var x3 = inPts.getPoint(verts[_i4].edges[1]);
239
237
  var l1 = [0, 0, 0];
240
238
  var l2 = [0, 0, 0];
241
239
 
@@ -12,6 +12,7 @@ import vtkOBBTree from './General/OBBTree.js';
12
12
  import vtkOutlineFilter from './General/OutlineFilter.js';
13
13
  import vtkPaintFilter from './General/PaintFilter.js';
14
14
  import vtkScalarToRGBA from './General/ScalarToRGBA.js';
15
+ import vtkTriangleFilter from './General/TriangleFilter.js';
15
16
  import vtkTubeFilter from './General/TubeFilter.js';
16
17
  import vtkWarpScalar from './General/WarpScalar.js';
17
18
  import vtkWindowedSincPolyDataFilter from './General/WindowedSincPolyDataFilter.js';
@@ -31,6 +32,7 @@ var General = {
31
32
  vtkOutlineFilter: vtkOutlineFilter,
32
33
  vtkPaintFilter: vtkPaintFilter,
33
34
  vtkScalarToRGBA: vtkScalarToRGBA,
35
+ vtkTriangleFilter: vtkTriangleFilter,
34
36
  vtkTubeFilter: vtkTubeFilter,
35
37
  vtkWarpScalar: vtkWarpScalar,
36
38
  vtkWindowedSincPolyDataFilter: vtkWindowedSincPolyDataFilter
@@ -4,8 +4,9 @@ export declare enum BlendMode {
4
4
  MINIMUM_INTENSITY_BLEND = 2,
5
5
  AVERAGE_INTENSITY_BLEND = 3,
6
6
  ADDITIVE_INTENSITY_BLEND = 4,
7
+ RADON_TRANSFORM_BLEND = 5,
7
8
  }
8
-
9
+
9
10
  export declare enum FilterMode {
10
11
  OFF = 0,
11
12
  NORMALIZED = 1,
@@ -3,7 +3,8 @@ var BlendMode = {
3
3
  MAXIMUM_INTENSITY_BLEND: 1,
4
4
  MINIMUM_INTENSITY_BLEND: 2,
5
5
  AVERAGE_INTENSITY_BLEND: 3,
6
- ADDITIVE_INTENSITY_BLEND: 4
6
+ ADDITIVE_INTENSITY_BLEND: 4,
7
+ RADON_TRANSFORM_BLEND: 5
7
8
  };
8
9
  var FilterMode = {
9
10
  OFF: 0,
@@ -1,3 +1,4 @@
1
+ import vtkPiecewiseFunction from './../../Common/DataModel/PiecewiseFunction';
1
2
  import { Bounds, Range } from './../../types';
2
3
  import vtkAbstractMapper, { IAbstractMapperInitialValues } from './AbstractMapper';
3
4
  import { BlendMode, FilterMode } from './VolumeMapper/Constants';
@@ -151,6 +152,11 @@ export interface vtkVolumeMapper extends vtkAbstractMapper {
151
152
  */
152
153
  setBlendModeToAverageIntensity(): void;
153
154
 
155
+ /**
156
+ * Set blend mode to RADON_TRANSFORM_BLEND
157
+ */
158
+ setBlendModeToRadonTransform(): void;
159
+
154
160
  /**
155
161
  * Get the distance between samples used for rendering
156
162
  * @param sampleDistance
@@ -233,6 +239,35 @@ export interface vtkVolumeMapper extends vtkAbstractMapper {
233
239
  update(): void;
234
240
  }
235
241
 
242
+ /**
243
+ * Create an absorption transfer function to set to the mapper when blend mode is RADON.
244
+ * The transfer function is a linear ramp between the lowest material with absorption and
245
+ * the material with maximum absorption. Voxel values lower than the lowest material with
246
+ * absorption will have no absorption associated. Voxel values higher than the maximum
247
+ * absorbent material will have the same absorption than the max absorbent material.
248
+ * The associated color transfer function is typically black to white between 0 and 1.
249
+ * An alternative is to create your own transfer function with HU/absorption pairs. e.g.
250
+ * const ofun = vtkPiecewiseFunction.newInstance();
251
+ * ofun.addPointLong(-1000,0, 1, 1); // air, "1, 1)" to flatten the function
252
+ * ofun.addPoint(-10, 0.01); // fat
253
+ * ofun.addPoint(-10, 0.015); // water
254
+ * ofun.addPointLong(1000, 0.03, 1, 1); // bone
255
+ * ofun.addPoint(3000, 1); // silver
256
+ * @static
257
+ * @param {number} firstAbsorbentMaterialHounsfieldValue: Define minimum voxel value (in hounsfield unit) with non zero absorption (e.g. water (0), fat(-10)...).
258
+ * Any voxel value lower than this parameter will have no absorption (absorption === 0)
259
+ * @param {number} firstAbsorbentMaterialAbsorption: Absorption attributed to voxels with firstAbsorbentMaterialHounsfieldValue (e.g. 0 or 0.01)
260
+ * @param {number} maxAbsorbentMaterialHounsfieldValue: Define maximum voxel value (in hounsfield unit) with increasing absorption (e.g. bone (1000))
261
+ * @param {number} maxAbsorbentMaterialAbsorption: Absorption attributed to voxels >= maxAbsorbentMaterialHounsfieldValue (e.g. 0.03)
262
+ * @param {vtkPiecewiseFunction} outputTransferFunction: To provide optionally to avoid instantiating a new transfer function each time.
263
+ * @return {vtkPiecewiseFunction} the created absorption transfer function to set on VolumeMapper scalarOpacity.
264
+ */
265
+ export function createRadonTransferFunction(
266
+ firstAbsorbentMaterialHounsfieldValue: number,
267
+ firstAbsorbentMaterialAbsorption: number,
268
+ maxAbsorbentMaterialHounsfieldValue: number,
269
+ maxAbsorbentMaterialAbsorption: number,
270
+ outputTransferFunction?: vtkPiecewiseFunction): vtkPiecewiseFunction;
236
271
 
237
272
  /**
238
273
  * Method use to decorate a given object (publicAPI+model) with vtkVolumeMapper characteristics.
@@ -3,12 +3,37 @@ import macro from '../../macros.js';
3
3
  import { O as createUninitializedBounds, D as clampValue, K as floor } from '../../Common/Core/Math/index.js';
4
4
  import Constants from './VolumeMapper/Constants.js';
5
5
  import vtkAbstractMapper from './AbstractMapper.js';
6
+ import vtkPiecewiseFunction from '../../Common/DataModel/PiecewiseFunction.js';
6
7
 
7
8
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
8
9
 
9
10
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
10
11
  var BlendMode = Constants.BlendMode,
11
- FilterMode = Constants.FilterMode; // ----------------------------------------------------------------------------
12
+ FilterMode = Constants.FilterMode;
13
+
14
+ function createRadonTransferFunction(firstAbsorbentMaterialHounsfieldValue, firstAbsorbentMaterialAbsorption, maxAbsorbentMaterialHounsfieldValue, maxAbsorbentMaterialAbsorption, outputTransferFunction) {
15
+ var ofun = null;
16
+
17
+ if (outputTransferFunction) {
18
+ ofun = outputTransferFunction;
19
+ ofun.removeAllPoints();
20
+ } else {
21
+ ofun = vtkPiecewiseFunction.newInstance();
22
+ }
23
+
24
+ ofun.addPointLong(-1024, 0, 1, 1); // air (i.e. material with no absorption)
25
+
26
+ ofun.addPoint(firstAbsorbentMaterialHounsfieldValue, firstAbsorbentMaterialAbsorption);
27
+ ofun.addPoint(maxAbsorbentMaterialHounsfieldValue, maxAbsorbentMaterialAbsorption);
28
+ return ofun;
29
+ } // ----------------------------------------------------------------------------
30
+ // Static API
31
+ // ----------------------------------------------------------------------------
32
+
33
+
34
+ var STATIC = {
35
+ createRadonTransferFunction: createRadonTransferFunction
36
+ }; // ----------------------------------------------------------------------------
12
37
  // vtkVolumeMapper methods
13
38
  // ----------------------------------------------------------------------------
14
39
 
@@ -58,6 +83,10 @@ function vtkVolumeMapper(publicAPI, model) {
58
83
  publicAPI.setBlendMode(BlendMode.ADDITIVE_INTENSITY_BLEND);
59
84
  };
60
85
 
86
+ publicAPI.setBlendModeToRadonTransform = function () {
87
+ publicAPI.setBlendMode(BlendMode.RADON_TRANSFORM_BLEND);
88
+ };
89
+
61
90
  publicAPI.getBlendModeAsString = function () {
62
91
  return macro.enumToString(BlendMode, model.blendMode);
63
92
  };
@@ -149,9 +178,9 @@ function extend(publicAPI, model) {
149
178
 
150
179
  var newInstance = macro.newInstance(extend, 'vtkVolumeMapper'); // ----------------------------------------------------------------------------
151
180
 
152
- var vtkVolumeMapper$1 = {
181
+ var vtkVolumeMapper$1 = _objectSpread({
153
182
  newInstance: newInstance,
154
183
  extend: extend
155
- };
184
+ }, STATIC);
156
185
 
157
- export { vtkVolumeMapper$1 as default, extend, newInstance };
186
+ export { STATIC, vtkVolumeMapper$1 as default, extend, newInstance };
@@ -1,5 +1,6 @@
1
1
  import { vtkObject } from './../../interfaces';
2
2
  import vtkCanvasView from './CanvasView';
3
+ import vtkImageStream from './../../IO/Core/ImageStream';
3
4
  import vtkViewStream from './../../IO/Core/ImageStream/ViewStream';
4
5
 
5
6
  interface IRemoteViewInitialValues {
@@ -188,13 +189,18 @@ export function newInstance(
188
189
  initialValues?: IRemoteViewInitialValues
189
190
  ): vtkRemoteView;
190
191
 
191
- export function connectImageStream(session: any): any;
192
+ export function connectImageStream(session: any): void;
193
+
194
+ export function disconnectImageStream(): void;
195
+
192
196
  /**
193
197
  * vtkRemoteView provides a way to create a remote view.
194
198
  */
195
199
  export declare const vtkRemoteView: {
196
200
  newInstance: typeof newInstance;
197
201
  extend: typeof extend;
202
+ SHARED_IMAGE_STREAM: vtkImageStream;
198
203
  connectImageStream: typeof connectImageStream;
204
+ disconnectImageStream: typeof disconnectImageStream;
199
205
  };
200
206
  export default vtkRemoteView;
@@ -209,9 +209,11 @@ function vtkOpenGLPolyDataMapper2D(publicAPI, model) {
209
209
  var p = vtkPoints.newInstance();
210
210
  var numPts = points.getNumberOfPoints();
211
211
  p.setNumberOfPoints(numPts);
212
+ var point = [];
212
213
 
213
214
  for (var i = 0; i < numPts; ++i) {
214
- transformCoordinate.setValue(points.getPoint(i));
215
+ points.getPoint(i, point);
216
+ transformCoordinate.setValue(point);
215
217
  var v = transformCoordinate.getComputedDoubleViewportValue(ren);
216
218
  p.setPoint(i, v[0], v[1], 0.0);
217
219
  }
@@ -41,7 +41,7 @@ function createContextProxyHandler() {
41
41
 
42
42
  return {
43
43
  get: function get(gl, prop, receiver) {
44
- var value = Reflect.get(gl, prop, receiver);
44
+ var value = Reflect.get(gl, prop, gl);
45
45
 
46
46
  if (value instanceof Function) {
47
47
  // prevents Illegal Invocation errors
@@ -1,3 +1,3 @@
1
- var vtkVolumeFS = "//VTK::System::Dec\n\n/*=========================================================================\n\n Program: Visualization Toolkit\n Module: vtkVolumeFS.glsl\n\n Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen\n All rights reserved.\n See Copyright.txt or http://www.kitware.com/Copyright.htm for details.\n\n This software is distributed WITHOUT ANY WARRANTY; without even\n the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n PURPOSE. See the above copyright notice for more information.\n\n=========================================================================*/\n// Template for the volume mappers fragment shader\n\n// the output of this shader\n//VTK::Output::Dec\n\nvarying vec3 vertexVCVSOutput;\n\n// first declare the settings from the mapper\n// that impact the code paths in here\n\n// always set vtkNumComponents 1,2,3,4\n//VTK::NumComponents\n\n// possibly define vtkTrilinearOn\n//VTK::TrilinearOn\n\n// possibly define vtkIndependentComponents\n//VTK::IndependentComponentsOn\n\n// possibly define any \"proportional\" components\n//VTK::vtkProportionalComponents\n\n// Define the blend mode to use\n#define vtkBlendMode //VTK::BlendMode\n\n// Possibly define vtkImageLabelOutlineOn\n//VTK::ImageLabelOutlineOn\n\n#ifdef vtkImageLabelOutlineOn\nuniform int outlineThickness;\nuniform float vpWidth;\nuniform float vpHeight;\nuniform float vpOffsetX;\nuniform float vpOffsetY;\nuniform mat4 PCWCMatrix;\nuniform mat4 vWCtoIDX;\n#endif\n\n// define vtkLightComplexity\n//VTK::LightComplexity\n#if vtkLightComplexity > 0\nuniform float vSpecularPower;\nuniform float vAmbient;\nuniform float vDiffuse;\nuniform float vSpecular;\n//VTK::Light::Dec\n#endif\n\n//VTK::VolumeShadowOn\n//VTK::SurfaceShadowOn\n//VTK::localAmbientOcclusionOn\n//VTK::LAO::Dec\n//VTK::VolumeShadow::Dec\n\n// define vtkComputeNormalFromOpacity\n//VTK::vtkComputeNormalFromOpacity\n\n// possibly define vtkGradientOpacityOn\n//VTK::GradientOpacityOn\n#ifdef vtkGradientOpacityOn\nuniform float goscale0;\nuniform float goshift0;\nuniform float gomin0;\nuniform float gomax0;\n#if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\nuniform float goscale1;\nuniform float goshift1;\nuniform float gomin1;\nuniform float gomax1;\n#if vtkNumComponents >= 3\nuniform float goscale2;\nuniform float goshift2;\nuniform float gomin2;\nuniform float gomax2;\n#endif\n#if vtkNumComponents >= 4\nuniform float goscale3;\nuniform float goshift3;\nuniform float gomin3;\nuniform float gomax3;\n#endif\n#endif\n#endif\n\n// if you want to see the raw tiled\n// data in webgl1 uncomment the following line\n// #define debugtile\n\n// camera values\nuniform float camThick;\nuniform float camNear;\nuniform float camFar;\nuniform int cameraParallel;\n\n// values describing the volume geometry\nuniform vec3 vOriginVC;\nuniform vec3 vSpacing;\nuniform ivec3 volumeDimensions; // 3d texture dimensions\nuniform vec3 vPlaneNormal0;\nuniform float vPlaneDistance0;\nuniform vec3 vPlaneNormal1;\nuniform float vPlaneDistance1;\nuniform vec3 vPlaneNormal2;\nuniform float vPlaneDistance2;\nuniform vec3 vPlaneNormal3;\nuniform float vPlaneDistance3;\nuniform vec3 vPlaneNormal4;\nuniform float vPlaneDistance4;\nuniform vec3 vPlaneNormal5;\nuniform float vPlaneDistance5;\n\n//VTK::ClipPlane::Dec\n\n// opacity and color textures\nuniform sampler2D otexture;\nuniform float oshift0;\nuniform float oscale0;\nuniform sampler2D ctexture;\nuniform float cshift0;\nuniform float cscale0;\n\n// jitter texture\nuniform sampler2D jtexture;\n\n// some 3D texture values\nuniform float sampleDistance;\nuniform vec3 vVCToIJK;\n\n// the heights defined below are the locations\n// for the up to four components of the tfuns\n// the tfuns have a height of 2XnumComps pixels so the\n// values are computed to hit the middle of the two rows\n// for that component\n#ifdef vtkIndependentComponentsOn\n#if vtkNumComponents == 2\nuniform float mix0;\nuniform float mix1;\n#define height0 0.25\n#define height1 0.75\n#endif\n#if vtkNumComponents == 3\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\n#define height0 0.17\n#define height1 0.5\n#define height2 0.83\n#endif\n#if vtkNumComponents == 4\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\nuniform float mix3;\n#define height0 0.125\n#define height1 0.375\n#define height2 0.625\n#define height3 0.875\n#endif\n#endif\n\n#if vtkNumComponents >= 2\nuniform float oshift1;\nuniform float oscale1;\nuniform float cshift1;\nuniform float cscale1;\n#endif\n#if vtkNumComponents >= 3\nuniform float oshift2;\nuniform float oscale2;\nuniform float cshift2;\nuniform float cscale2;\n#endif\n#if vtkNumComponents >= 4\nuniform float oshift3;\nuniform float oscale3;\nuniform float cshift3;\nuniform float cscale3;\n#endif\n\nuniform vec4 ipScalarRangeMin;\nuniform vec4 ipScalarRangeMax;\n\n// declaration for intermixed geometry\n//VTK::ZBuffer::Dec\n\n//=======================================================================\n// global and custom variables (a temporary section before photorealistics rendering module is complete)\nvec3 rayDirVC;\nfloat sampleDistanceISVS;\nfloat sampleDistanceIS;\n\n#define SQRT3 1.7321\n#define INV4PI 0.0796\n#define EPSILON 0.001\n#define PI 3.1415\n#define PI2 9.8696\n\n//=======================================================================\n// Webgl2 specific version of functions\n#if __VERSION__ == 300\n\nuniform highp sampler3D texture1;\n\nvec4 getTextureValue(vec3 pos)\n{\n vec4 tmp = texture(texture1, pos);\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.a = tmp.g;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n return tmp;\n}\n\n//=======================================================================\n// WebGL1 specific version of functions\n#else\n\nuniform sampler2D texture1;\n\nuniform float texWidth;\nuniform float texHeight;\nuniform int xreps;\nuniform int xstride;\nuniform int ystride;\n\n// if computing trilinear values from multiple z slices\n#ifdef vtkTrilinearOn\nvec4 getTextureValue(vec3 ijk)\n{\n float zoff = 1.0/float(volumeDimensions.z);\n vec4 val1 = getOneTextureValue(ijk);\n vec4 val2 = getOneTextureValue(vec3(ijk.xy, ijk.z + zoff));\n\n float indexZ = float(volumeDimensions)*ijk.z;\n float zmix = indexZ - floor(indexZ);\n\n return mix(val1, val2, zmix);\n}\n\nvec4 getOneTextureValue(vec3 ijk)\n#else // nearest or fast linear\nvec4 getTextureValue(vec3 ijk)\n#endif\n{\n vec3 tdims = vec3(volumeDimensions);\n\n#ifdef debugtile\n vec2 tpos = vec2(ijk.x, ijk.y);\n vec4 tmp = texture2D(texture1, tpos);\n tmp.a = 1.0;\n\n#else\n int z = int(ijk.z * tdims.z);\n int yz = z / xreps;\n int xz = z - yz*xreps;\n\n int tileWidth = volumeDimensions.x/xstride;\n int tileHeight = volumeDimensions.y/ystride;\n\n xz *= tileWidth;\n yz *= tileHeight;\n\n float ni = float(xz) + (ijk.x*float(tileWidth));\n float nj = float(yz) + (ijk.y*float(tileHeight));\n\n vec2 tpos = vec2(ni/texWidth, nj/texHeight);\n\n vec4 tmp = texture2D(texture1, tpos);\n\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.g = tmp.a;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n#endif\n\n return tmp;\n}\n\n// End of Webgl1 specific code\n//=======================================================================\n#endif\n\n//=======================================================================\n// transformation between VC and IS space\n\n// convert vector position from idx to vc\n#if vtkLightComplexity > 0\nvec3 IStoVC(vec3 posIS){\n vec3 posVC = posIS / vVCToIJK;\n return posVC.x * vPlaneNormal0 + \n posVC.y * vPlaneNormal2 + \n posVC.z * vPlaneNormal4 + \n vOriginVC;\n}\n\n// convert vector position from vc to idx\nvec3 VCtoIS(vec3 posVC){\n posVC = posVC - vOriginVC;\n posVC = vec3(\n dot(posVC, vPlaneNormal0),\n dot(posVC, vPlaneNormal2),\n dot(posVC, vPlaneNormal4)); \n return posVC * vVCToIJK;\n}\n#endif\n\n//Rotate vector to view coordinate\n#if (vtkLightComplexity > 0) || (defined vtkGradientOpacityOn)\nvoid rotateToViewCoord(inout vec3 dirIS){\n dirIS.xyz =\n dirIS.x * vPlaneNormal0 +\n dirIS.y * vPlaneNormal2 +\n dirIS.z * vPlaneNormal4;\n}\n\n//Rotate vector to idx coordinate\nvec3 rotateToIDX(vec3 dirVC){\n vec3 dirIS;\n dirIS.xyz = vec3(\n dot(dirVC, vPlaneNormal0),\n dot(dirVC, vPlaneNormal2),\n dot(dirVC, vPlaneNormal4)); \n return dirIS;\n}\n#endif\n\n//=======================================================================\n// Given a normal compute the gradient opacity factors\nfloat computeGradientOpacityFactor(\n float normalMag, float goscale, float goshift, float gomin, float gomax)\n{\n#if defined(vtkGradientOpacityOn)\n return clamp(normalMag * goscale + goshift, gomin, gomax);\n#else\n return 1.0;\n#endif\n}\n\n//=======================================================================\n// compute the normal and gradient magnitude for a position, uses forward difference\n#if (vtkLightComplexity > 0) || (defined vtkGradientOpacityOn)\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n vec4 computeNormalForDensity(vec3 pos, float scalar, vec3 tstep, out mat3 scalarInterp, out vec3 secondaryGradientMag)\n {\n vec4 result;\n scalarInterp[0][0] = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a;\n scalarInterp[0][1] = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a;\n scalarInterp[0][2] = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a;\n // look up scalar values for computing secondary gradient\n scalarInterp[1][0] = getTextureValue(pos + vec3(2.0*tstep.x, 0.0, 0.0)).a;\n scalarInterp[1][1] = getTextureValue(pos + vec3(0.0, 2.0*tstep.y, 0.0)).a;\n scalarInterp[1][2] = getTextureValue(pos + vec3(0.0, 0.0, 2.0*tstep.z)).a;\n scalarInterp[2][0] = getTextureValue(pos + vec3(tstep.x, tstep.y, 0.0)).a;\n scalarInterp[2][1] = getTextureValue(pos + vec3(tstep.x, 0.0, tstep.z)).a;\n scalarInterp[2][2] = getTextureValue(pos + vec3(0.0, tstep.y, tstep.z)).a;\n result.x = scalarInterp[0][0] - scalar;\n result.y = scalarInterp[0][1] - scalar;\n result.z = scalarInterp[0][2] - scalar;\n // divide by spacing\n result.xyz /= vSpacing;\n result.w = length(result.xyz);\n rotateToViewCoord(result.xyz);\n secondaryGradientMag.x = length(vec3(scalarInterp[1][0] - scalarInterp[0][0],\n scalarInterp[2][0] - scalarInterp[0][0],\n scalarInterp[2][1] - scalarInterp[0][0]) / vSpacing);\n secondaryGradientMag.y = length(vec3(scalarInterp[2][0] - scalarInterp[0][1],\n scalarInterp[1][1] - scalarInterp[0][1],\n scalarInterp[2][2] - scalarInterp[0][1]) / vSpacing);\n secondaryGradientMag.z = length(vec3(scalarInterp[2][1] - scalarInterp[0][2],\n scalarInterp[2][2] - scalarInterp[0][2],\n scalarInterp[1][2] - scalarInterp[0][2]) / vSpacing);\n if (length(result.xyz) > 0.0) {\n return vec4(normalize(result.xyz),result.w);\n } else {\n return vec4(0.0);\n }\n }\n\n vec4 computeDensityNormal(float scalar, float gradientMag, mat3 scalarInterp, vec3 secondaryGradientMag)\n {\n vec4 opacityG;\n vec3 opacityInterp = vec3(0.0);\n float opacity = texture2D(otexture, vec2(scalar * oscale0 + oshift0, 0.5)).r;\n if (gradientMag >= 0.0){\n opacity *= computeGradientOpacityFactor(gradientMag, goscale0, goshift0, gomin0, gomax0);\n }\n opacityInterp.x = texture2D(otexture, vec2(scalarInterp[0][0] * oscale0 + oshift0, 0.5)).r; \n if (secondaryGradientMag.x >= 0.0){\n opacityInterp.x *= computeGradientOpacityFactor(secondaryGradientMag.x, goscale0, goshift0, gomin0, gomax0);\n }\n \n opacityInterp.y = texture2D(otexture, vec2(scalarInterp[0][1] * oscale0 + oshift0, 0.5)).r;\n if (secondaryGradientMag.y >= 0.0){\n opacityInterp.y *= computeGradientOpacityFactor(secondaryGradientMag.y, goscale0, goshift0, gomin0, gomax0);\n }\n\n opacityInterp.z = texture2D(otexture, vec2(scalarInterp[0][2] * oscale0 + oshift0, 0.5)).r;\n if (secondaryGradientMag.z >= 0.0){\n opacityInterp.z *= computeGradientOpacityFactor(secondaryGradientMag.z, goscale0, goshift0, gomin0, gomax0);\n }\n\n opacityG.xyz = opacityInterp - vec3(opacity,opacity,opacity);\n // divide by spacing\n opacityG.xyz /= vSpacing;\n opacityG.w = length(opacityG.xyz);\n rotateToViewCoord(opacityG.xyz);\n if (length(opacityG.xyz) > 0.0) { \n return vec4(normalize(opacityG.xyz),opacityG.w);\n } else {\n return vec4(0.0);\n }\n } \n\n #else\n //if gradient opacity not on but using density gradient\n vec4 computeDensityNormal(float scalar, vec3 scalarInterp) \n { \n vec4 opacityG; \n float opacity = texture2D(otexture, vec2(scalar * oscale0 + oshift0, 0.5)).r; \n opacityG.x = texture2D(otexture, vec2(scalarInterp.x * oscale0 + oshift0, 0.5)).r - opacity; \n opacityG.y = texture2D(otexture, vec2(scalarInterp.y * oscale0 + oshift0, 0.5)).r - opacity; \n opacityG.z = texture2D(otexture, vec2(scalarInterp.z * oscale0 + oshift0, 0.5)).r - opacity; \n // divide by spacing \n opacityG.xyz /= vSpacing; \n opacityG.w = length(opacityG.xyz); \n // rotate to View Coords \n rotateToViewCoord(opacityG.xyz);\n if (length(opacityG.xyz) > 0.0) { \n return vec4(normalize(opacityG.xyz),opacityG.w); \n } else { \n return vec4(0.0); \n } \n } \n vec4 computeNormalForDensity(vec3 pos, float scalar, vec3 tstep, out vec3 scalarInterp) \n { \n vec4 result; \n scalarInterp.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a; \n scalarInterp.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a; \n scalarInterp.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a; \n result.x = scalarInterp.x - scalar; \n result.y = scalarInterp.y - scalar; \n result.z = scalarInterp.z - scalar; \n // divide by spacing\n result.xyz /= vSpacing;\n result.w = length(result.xyz); \n // rotate to View Coords \n rotateToViewCoord(result.xyz); \n if (length(result.xyz) > 0.0) { \n return vec4(normalize(result.xyz),result.w); \n } else { \n return vec4(0.0); \n } \n } \n #endif\n #endif\n // compute scalar density\n vec4 computeNormal(vec3 pos, float scalar, vec3 tstep) \n { \n vec4 result; \n result.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a - scalar; \n result.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a - scalar; \n result.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a - scalar; \n // divide by spacing \n result.xyz /= vSpacing; \n result.w = length(result.xyz);\n if (result.w > 0.0){\n // rotate to View Coords \n rotateToViewCoord(result.xyz);\n return vec4(normalize(result.xyz),result.w); \n } else {\n return vec4(0.0);\n }\n } \n#endif\n\n#ifdef vtkImageLabelOutlineOn\nvec3 fragCoordToIndexSpace(vec4 fragCoord) {\n vec4 pcPos = vec4(\n (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,\n (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,\n (fragCoord.z - 0.5) * 2.0,\n 1.0);\n\n vec4 worldCoord = PCWCMatrix * pcPos;\n vec4 vertex = (worldCoord/worldCoord.w);\n\n vec3 index = (vWCtoIDX * vertex).xyz;\n\n // half voxel fix for labelmapOutline \n return (index + vec3(0.5)) / vec3(volumeDimensions);\n}\n#endif\n\n//=======================================================================\n// compute the normals and gradient magnitudes for a position\n// for independent components\nmat4 computeMat4Normal(vec3 pos, vec4 tValue, vec3 tstep)\n{\n mat4 result;\n vec4 distX = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)) - tValue;\n vec4 distY = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)) - tValue;\n vec4 distZ = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)) - tValue;\n\n // divide by spacing\n distX /= vSpacing.x;\n distY /= vSpacing.y;\n distZ /= vSpacing.z;\n\n mat3 rot;\n rot[0] = vPlaneNormal0;\n rot[1] = vPlaneNormal2;\n rot[2] = vPlaneNormal4;\n\n#if !defined(vtkComponent0Proportional)\n result[0].xyz = vec3(distX.r, distY.r, distZ.r);\n result[0].a = length(result[0].xyz);\n result[0].xyz *= rot;\n if (result[0].w > 0.0)\n {\n result[0].xyz /= result[0].w;\n }\n#endif\n\n// optionally compute the 2nd component\n#if vtkNumComponents >= 2 && !defined(vtkComponent1Proportional)\n result[1].xyz = vec3(distX.g, distY.g, distZ.g);\n result[1].a = length(result[1].xyz);\n result[1].xyz *= rot;\n if (result[1].w > 0.0)\n {\n result[1].xyz /= result[1].w;\n }\n#endif\n\n// optionally compute the 3rd component\n#if vtkNumComponents >= 3 && !defined(vtkComponent2Proportional)\n result[2].xyz = vec3(distX.b, distY.b, distZ.b);\n result[2].a = length(result[2].xyz);\n result[2].xyz *= rot;\n if (result[2].w > 0.0)\n {\n result[2].xyz /= result[2].w;\n }\n#endif\n\n// optionally compute the 4th component\n#if vtkNumComponents >= 4 && !defined(vtkComponent3Proportional)\n result[3].xyz = vec3(distX.a, distY.a, distZ.a);\n result[3].a = length(result[3].xyz);\n result[3].xyz *= rot;\n if (result[3].w > 0.0)\n {\n result[3].xyz /= result[3].w;\n }\n#endif\n\n return result;\n}\n\n//=======================================================================\n// global shadow - secondary ray\n#if defined(VolumeShadowOn) || defined(localAmbientOcclusionOn)\nfloat random()\n{ \n float rand = fract(sin(dot(gl_FragCoord.xy,vec2(12.9898,78.233)))*43758.5453123);\n float jitter=texture2D(jtexture,gl_FragCoord.xy/32.).r;\n uint pcg_state = floatBitsToUint(jitter);\n uint state = pcg_state;\n pcg_state = pcg_state * uint(747796405) + uint(2891336453);\n uint word = ((state >> ((state >> uint(28)) + uint(4))) ^ state) * uint(277803737);\n return (float((((word >> uint(22)) ^ word) >> 1 ))/float(2147483647) + rand)/2.0;\n}\n#endif\n\n#ifdef VolumeShadowOn\n// henyey greenstein phase function\nfloat phase_function(float cos_angle)\n{\n // divide by 2.0 instead of 4pi to increase intensity\n return ((1.0-anisotropy2)/pow(1.0+anisotropy2-2.0*anisotropy*cos_angle, 1.5))/2.0;\n}\n\n// Computes the intersection between a ray and a box\nstruct Hit\n{\n float tmin;\n float tmax;\n};\n\nstruct Ray\n{\n vec3 origin;\n vec3 dir;\n vec3 invDir;\n};\n\nbool BBoxIntersect(vec3 boundMin, vec3 boundMax, const Ray r, out Hit hit)\n{\n vec3 tbot = r.invDir * (boundMin - r.origin);\n vec3 ttop = r.invDir * (boundMax - r.origin);\n vec3 tmin = min(ttop, tbot);\n vec3 tmax = max(ttop, tbot);\n vec2 t = max(tmin.xx, tmin.yz);\n float t0 = max(t.x, t.y);\n t = min(tmax.xx, tmax.yz);\n float t1 = min(t.x, t.y);\n hit.tmin = t0;\n hit.tmax = t1;\n return t1 > max(t0,0.0);\n}\n\n// As BBoxIntersect requires the inverse of the ray coords,\n// this function is used to avoid numerical issues\nvoid safe_0_vector(inout Ray ray)\n{\n if(abs(ray.dir.x) < EPSILON) ray.dir.x = sign(ray.dir.x) * EPSILON;\n if(abs(ray.dir.y) < EPSILON) ray.dir.y = sign(ray.dir.y) * EPSILON;\n if(abs(ray.dir.z) < EPSILON) ray.dir.z = sign(ray.dir.z) * EPSILON;\n}\n\nfloat volume_shadow(vec3 posIS, vec3 lightDirNormIS)\n{\n float shadow = 1.0;\n float opacity = 0.0;\n\n // modify sample distance with a random number between 0.8 and 1.0\n float sampleDistanceISVS_jitter = sampleDistanceISVS * mix(0.8, 1.0, random());\n float opacityPrev = texture2D(otexture, vec2(getTextureValue(posIS).r * oscale0 + oshift0, 0.5)).r;\n \n // in case the first sample near surface has a very tiled light ray, we need to offset start position \n posIS += sampleDistanceISVS_jitter * lightDirNormIS; \n\n // compute the start and end points for the ray\n Ray ray;\n Hit hit; \n ray.origin = posIS;\n ray.dir = lightDirNormIS;\n safe_0_vector(ray);\n ray.invDir = 1.0/ray.dir;\n \n if(!BBoxIntersect(vec3(0.0),vec3(1.0), ray, hit))\n {\n return 1.0;\n }\n vec4 scalar = vec4(0.0);\n float maxdist = hit.tmax;\n if(maxdist < EPSILON) {\n return 1.0;\n }\n\n // interpolate shadow ray length between: 1 unit of sample distance in IS to SQRT3, based on globalIlluminationReach\n float maxgi = mix(sampleDistanceISVS_jitter,SQRT3,giReach);\n maxdist = min(maxdist,maxgi);\n\n // support gradient opacity\n #ifdef vtkGradientOpacityOn\n vec4 normal;\n #endif\n\n vec3 current_step = sampleDistanceISVS_jitter * lightDirNormIS;\n float maxSteps = ceil(maxdist/sampleDistanceISVS_jitter);\n float opacityDelta = 0.0;\n\n for (float i = 0.0; i < maxSteps; i++)\n {\n scalar = getTextureValue(posIS);\n opacity = texture2D(otexture, vec2(scalar.r * oscale0 + oshift0, 0.5)).r;\n #ifdef vtkGradientOpacityOn \n normal = computeNormal(posIS, scalar.a, vec3(1.0/vec3(volumeDimensions))); \n opacity *= computeGradientOpacityFactor(normal.w, goscale0, goshift0, gomin0, gomax0);\n #endif \n shadow *= 1.0 - opacity;\n\n // optimization: early termination\n if (shadow < EPSILON){\n return 0.0;\n }\n\n // optimization: increase/decrease sample distance based on changed in opacity value\n opacityDelta = opacityPrev - opacity;\n opacityPrev = opacity;\n if (opacityDelta > 0.0){\n current_step *= 0.9;\n } else if (opacityDelta < 0.0){\n current_step *= 1.1;\n }\n posIS += current_step;\n }\n\n return shadow; \n}\n\nvec3 applyShadowRay(vec3 tColor, vec3 posIS, vec3 viewDirectionVC)\n{\n vec3 vertLight = vec3(0.0);\n vec3 secondary_contrib = vec3(0.0);\n // here we assume only positional light, no effect of cones\n for (int i = 0; i < lightNum; i++)\n {\n #if(vtkLightComplexity==3)\n if (lightPositional[i] == 1){\n vertLight = lightPositionVC[i] - IStoVC(posIS);\n }else{\n vertLight = - lightDirectionVC[i];\n }\n #else\n vertLight = - lightDirectionVC[i];\n #endif\n // here we assume achromatic light, only intensity\n float dDotL = dot(viewDirectionVC, normalize(vertLight));\n // isotropic scatter returns 0.5 instead of 1/4pi to increase intensity\n float phase_attenuation = 0.5;\n if (abs(anisotropy) > EPSILON){\n phase_attenuation = phase_function(dDotL);\n }\n float vol_shadow = volume_shadow(posIS, normalize(rotateToIDX(vertLight)));\n secondary_contrib += tColor * vDiffuse * lightColor[i] * vol_shadow * phase_attenuation; \n secondary_contrib += tColor * vAmbient;\n } \n return secondary_contrib;\n}\n#endif\n\n//=======================================================================\n// local ambient occlusion\n#ifdef localAmbientOcclusionOn\nvec3 sample_direction_uniform(int i)\n{\n float rand = random() * 0.5;\n float theta = PI2 * (kernelSample[i][0] + rand);\n float phi = acos(2.0 * (kernelSample[i][1] + rand) -1.0) / 2.5;\n return normalize(vec3(cos(theta)*sin(phi), sin(theta)*sin(phi), cos(phi)));\n}\n\n// return a matrix that transform startDir into z axis; startDir should be normalized\nmat3 zBaseRotationalMatrix(vec3 startDir){\n vec3 axis = cross(startDir, vec3(0.0,0.0,1.0));\n float cosA = startDir.z;\n float k = 1.0 / (1.0 + cosA);\n mat3 matrix = mat3((axis.x * axis.x * k) + cosA, (axis.y * axis.x * k) - axis.z, (axis.z * axis.x * k) + axis.y,\n (axis.x * axis.y * k) + axis.z, (axis.y * axis.y * k) + cosA, (axis.z * axis.y * k) - axis.x,\n (axis.x * axis.z * k) - axis.y, (axis.y * axis.z * k) + axis.x, (axis.z * axis.z * k) + cosA);\n return matrix;\n}\n\nfloat computeLAO(vec3 posIS, float opacity, vec3 lightDir, vec4 normal){\n // apply LAO only at selected locations, otherwise return full brightness\n if (normal.w > 0.0 && opacity > 0.05){\n float total_transmittance = 0.0;\n mat3 inverseRotateBasis = inverse(zBaseRotationalMatrix(normalize(-normal.xyz)));\n vec3 currPos, randomDirStep;\n float weight, transmittance, opacity;\n for (int i = 0; i < kernelSize; i++)\n {\n randomDirStep = inverseRotateBasis * sample_direction_uniform(i) * sampleDistanceIS;\n weight = 1.0 - dot(normalize(lightDir), normalize(randomDirStep));\n currPos = posIS;\n transmittance = 1.0;\n for (int j = 0; j < kernelRadius ; j++){\n currPos += randomDirStep;\n // check if it's at clipping plane, if so return full brightness\n if (all(greaterThan(currPos, vec3(EPSILON))) && all(lessThan(currPos,vec3(1.0-EPSILON)))){\n opacity = texture2D(otexture, vec2(getTextureValue(currPos).r * oscale0 + oshift0, 0.5)).r;\n #ifdef vtkGradientOpacityOn\n opacity *= computeGradientOpacityFactor(normal.w, goscale0, goshift0, gomin0, gomax0);\n #endif\n transmittance *= 1.0 - opacity;\n }\n else{\n break;\n }\n }\n total_transmittance += transmittance / float(kernelRadius) * weight;\n\n // early termination if fully translucent\n if (total_transmittance > 1.0 - EPSILON){\n return 1.0;\n }\n }\n // average transmittance and reduce variance\n return clamp(total_transmittance / float(kernelSize), 0.3, 1.0); \n } else {\n return 1.0;\n }\n}\n#endif\n\n//=======================================================================\n// surface light contribution\n#if vtkLightComplexity > 0\n void applyLighting(inout vec3 tColor, vec4 normal)\n {\n vec3 diffuse = vec3(0.0, 0.0, 0.0);\n vec3 specular = vec3(0.0, 0.0, 0.0);\n float df, sf = 0.0;\n for (int i = 0; i < lightNum; i++){\n df = abs(dot(normal.rgb, -lightDirectionVC[i]));\n diffuse += df * lightColor[i];\n sf = pow( abs(dot(lightHalfAngleVC[i],normal.rgb)), vSpecularPower);\n specular += sf * lightColor[i];\n }\n tColor.rgb = tColor.rgb*(diffuse*vDiffuse + vAmbient) + specular*vSpecular;\n }\n #ifdef SurfaceShadowOn\n #if vtkLightComplexity < 3\n vec3 applyLightingDirectional(vec3 posIS, vec4 tColor, vec4 normal)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n #ifdef localAmbientOcclusionOn\n vec3 ambient = vec3(0.0);\n #endif \n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float ndotL,vdotR;\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * lightColor[i];\n }\n }\n #ifdef localAmbientOcclusionOn\n ambient += computeLAO(posIS, tColor.a, vertLightDirection, normal);\n #endif\n } \n #ifdef localAmbientOcclusionOn\n return tColor.rgb * (diffuse * vDiffuse + vAmbient * ambient) + specular*vSpecular;\n #else \n return tColor.rgb * (diffuse * vDiffuse + vAmbient) + specular*vSpecular;\n #endif \n }\n #else\n vec3 applyLightingPositional(vec3 posIS, vec4 tColor, vec4 normal, vec3 posVC)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n #ifdef localAmbientOcclusionOn\n vec3 ambient = vec3(0.0);\n #endif \n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float distance,attenuation,ndotL,vdotR;\n vec3 lightDir;\n if (lightPositional[i] == 1){\n lightDir = lightDirectionVC[i];\n vertLightDirection = posVC - lightPositionVC[i]; \n distance = length(vertLightDirection);\n vertLightDirection = normalize(vertLightDirection);\n attenuation = 1.0 / (lightAttenuation[i].x\n + lightAttenuation[i].y * distance\n + lightAttenuation[i].z * distance * distance);\n // per OpenGL standard cone angle is 90 or less for a spot light\n if (lightConeAngle[i] <= 90.0){\n float coneDot = dot(vertLightDirection, lightDir);\n if (coneDot >= cos(radians(lightConeAngle[i]))){ // if inside cone\n attenuation = attenuation * pow(coneDot, lightExponent[i]);\n }\n else {\n attenuation = 0.0;\n }\n }\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * attenuation * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * attenuation * lightColor[i];\n }\n }\n #ifdef localAmbientOcclusionOn\n ambient += computeLAO(posIS, tColor.a, vertLightDirection, normal);\n #endif \n } else {\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * lightColor[i];\n }\n }\n #ifdef localAmbientOcclusionOn\n ambient += computeLAO(posIS, tColor.a, vertLightDirection, normal);\n #endif \n }\n }\n #ifdef localAmbientOcclusionOn\n return tColor.rgb * (diffuse * vDiffuse + vAmbient * ambient) + specular*vSpecular;\n #else \n return tColor.rgb * (diffuse * vDiffuse + vAmbient) + specular*vSpecular;\n #endif\n }\n #endif \n #endif\n#endif\n\n//=======================================================================\n// Given a texture value compute the color and opacity\n//\nvec4 getColorForValue(vec4 tValue, vec3 posIS, vec3 tstep)\n{\n#ifdef vtkImageLabelOutlineOn\n vec3 centerPosIS = fragCoordToIndexSpace(gl_FragCoord); // pos in texture space\n vec4 centerValue = getTextureValue(centerPosIS);\n bool pixelOnBorder = false;\n vec4 tColor = texture2D(ctexture, vec2(centerValue.r * cscale0 + cshift0, 0.5));\n\n // Get alpha of segment from opacity function.\n tColor.a = texture2D(otexture, vec2(centerValue.r * oscale0 + oshift0, 0.5)).r;\n\n // Only perform outline check on fragments rendering voxels that aren't invisible.\n // Saves a bunch of needless checks on the background.\n // TODO define epsilon when building shader?\n if (float(tColor.a) > 0.01) {\n for (int i = -outlineThickness; i <= outlineThickness; i++) {\n for (int j = -outlineThickness; j <= outlineThickness; j++) {\n if (i == 0 || j == 0) {\n continue;\n }\n\n vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(i),\n gl_FragCoord.y + float(j),\n gl_FragCoord.z, gl_FragCoord.w);\n\n vec3 neighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);\n vec4 value = getTextureValue(neighborPosIS);\n\n // If any of my neighbours are not the same value as I\n // am, this means I am on the border of the segment.\n // We can break the loops\n if (any(notEqual(value, centerValue))) {\n pixelOnBorder = true;\n break;\n }\n }\n\n if (pixelOnBorder == true) {\n break;\n }\n }\n\n // If I am on the border, I am displayed at full opacity\n if (pixelOnBorder == true) {\n tColor.a = 1.0;\n }\n }\n\n#else\n // compute the normal and gradient magnitude if needed\n // We compute it as a vec4 if possible otherwise a mat4\n //\n vec4 goFactor = vec4(1.0,1.0,1.0,1.0);\n\n // compute the normal vectors as needed\n #if (vtkLightComplexity > 0) || defined(vtkGradientOpacityOn)\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);\n #if !defined(vtkComponent0Proportional)\n vec4 normal0 = normalMat[0];\n #endif\n #if !defined(vtkComponent1Proportional)\n vec4 normal1 = normalMat[1];\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n vec4 normal2 = normalMat[2];\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n vec4 normal3 = normalMat[3];\n #endif\n #endif\n #endif\n #else\n vec4 normalLight;\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n mat3 scalarInterp; \n vec3 secondaryGradientMag; \n vec4 normal0 = computeNormalForDensity(posIS, tValue.a, tstep, scalarInterp, secondaryGradientMag); \n normalLight = computeDensityNormal(tValue.a, normal0.w, scalarInterp,secondaryGradientMag); \n if (length(normalLight) == 0.0){ \n normalLight = normal0; \n } \n #else\n vec3 scalarInterp; \n vec4 normal0 = computeNormalForDensity(posIS, tValue.a, tstep, scalarInterp); \n if (length(normal0)>0.0){ \n normalLight = computeDensityNormal(tValue.a,scalarInterp); \n if (length(normalLight)==0.0){ \n normalLight = normal0; \n } \n } \n #endif\n #else \n vec4 normal0 = computeNormal(posIS, tValue.a, tstep); \n normalLight = normal0; \n #endif\n #endif\n #endif\n\n // compute gradient opacity factors as needed\n #if defined(vtkGradientOpacityOn)\n #if !defined(vtkComponent0Proportional)\n goFactor.x =\n computeGradientOpacityFactor(normal0.a, goscale0, goshift0, gomin0, gomax0);\n #endif\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n #if !defined(vtkComponent1Proportional)\n goFactor.y =\n computeGradientOpacityFactor(normal1.a, goscale1, goshift1, gomin1, gomax1);\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n goFactor.z =\n computeGradientOpacityFactor(normal2.a, goscale2, goshift2, gomin2, gomax2);\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n goFactor.w =\n computeGradientOpacityFactor(normal3.a, goscale3, goshift3, gomin3, gomax3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n // single component is always independent\n #if vtkNumComponents == 1\n vec4 tColor = texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, 0.5));\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n if (tColor.a < EPSILON){\n return vec4(0.0);\n } \n #endif\n\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n vec4 tColor = mix0*texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, height0));\n #if !defined(vtkComponent0Proportional)\n tColor.a = goFactor.x*mix0*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n tColor *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix0));\n #endif\n\n vec3 tColor1 = mix1*texture2D(ctexture, vec2(tValue.g * cscale1 + cshift1, height1)).rgb;\n #if !defined(vtkComponent1Proportional)\n tColor.a += goFactor.y*mix1*texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n tColor1 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix1));\n #endif\n\n #if vtkNumComponents >= 3\n vec3 tColor2 = mix2*texture2D(ctexture, vec2(tValue.b * cscale2 + cshift2, height2)).rgb;\n #if !defined(vtkComponent2Proportional)\n tColor.a += goFactor.z*mix2*texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n tColor2 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix2));\n #endif\n\n #if vtkNumComponents >= 4\n vec3 tColor3 = mix3*texture2D(ctexture, vec2(tValue.a * cscale3 + cshift3, height3)).rgb;\n #if !defined(vtkComponent3Proportional)\n tColor.a += goFactor.w*mix3*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n tColor3 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix3));\n #endif\n #endif\n #endif\n #else // then not independent\n\n #if vtkNumComponents == 2\n float lum = tValue.r * cscale0 + cshift0;\n float alpha = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale1 + oshift1, 0.5)).r;\n vec4 tColor = vec4(lum, lum, lum, alpha);\n #endif\n #if vtkNumComponents == 3\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale0 + oshift0, 0.5)).r;\n #endif\n #if vtkNumComponents == 4\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, 0.5)).r;\n #endif\n #endif // dependent\n\n // apply lighting if requested as appropriate\n #if vtkLightComplexity > 0\n #if !defined(vtkComponent0Proportional) \n #if vtkNumComponents == 1\n \n #ifdef SurfaceShadowOn\n #if vtkLightComplexity < 3\n vec3 tColorS = applyLightingDirectional(posIS, tColor, normalLight);\n #else\n vec3 tColorS = applyLightingPositional(posIS, tColor, normalLight, IStoVC(posIS));\n #endif\n #endif\n\n #ifdef VolumeShadowOn\n vec3 tColorVS = applyShadowRay(tColor.rgb, posIS, rayDirVC);\n #ifdef SurfaceShadowOn\n float vol_coef = volumetricScatteringBlending * (1.0 - tColor.a / 2.0) * (1.0 - atan(normalLight.w) * INV4PI);\n tColor.rgb = (1.0-vol_coef) * tColorS + vol_coef * tColorVS;\n #else\n tColor.rgb = tColorVS;\n #endif\n #else\n tColor.rgb = tColorS;\n #endif\n \n #else\n applyLighting(tColor.rgb, normal0);\n #endif\n #endif\n\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n #if !defined(vtkComponent1Proportional)\n applyLighting(tColor1, normal1);\n #endif\n #if vtkNumComponents >= 3\n #if !defined(vtkComponent2Proportional)\n applyLighting(tColor2, normal2);\n #endif\n #if vtkNumComponents >= 4\n #if !defined(vtkComponent3Proportional)\n applyLighting(tColor3, normal3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n// perform final independent blend as needed\n#if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n tColor.rgb += tColor1;\n#if vtkNumComponents >= 3\n tColor.rgb += tColor2;\n#if vtkNumComponents >= 4\n tColor.rgb += tColor3;\n#endif\n#endif\n#endif\n\n#endif\n\n\n\n\n\n\n\nreturn tColor;\n}\n\nbool valueWithinScalarRange(vec4 val, vec4 min, vec4 max) {\n bool withinRange = false;\n #if vtkNumComponents == 1\n if (val.r >= min.r && val.r <= max.r) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents == 2\n if (val.r >= min.r && val.r <= max.r &&\n val.g >= min.g && val.g <= max.g) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 3\n if (all(greaterThanEqual(val, ipScalarRangeMin)) &&\n all(lessThanEqual(val, ipScalarRangeMax))) {\n withinRange = true;\n }\n #endif\n return withinRange;\n}\n\n//=======================================================================\n// Apply the specified blend mode operation along the ray's path.\n//\nvoid applyBlend(vec3 posIS, vec3 endIS, vec3 tdims)\n{\n vec3 tstep = 1.0/tdims;\n\n // start slightly inside and apply some jitter\n vec3 delta = endIS - posIS;\n vec3 stepIS = normalize(delta)*sampleDistanceIS;\n float raySteps = length(delta)/sampleDistanceIS;\n\n // avoid 0.0 jitter\n float jitter = 0.01 + 0.99*texture2D(jtexture, gl_FragCoord.xy/32.0).r;\n float stepsTraveled = jitter;\n\n // local vars for the loop\n vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n vec4 tValue;\n vec4 tColor;\n\n // if we have less than one step then pick the middle point\n // as our value\n // if (raySteps <= 1.0)\n // {\n // posIS = (posIS + endIS)*0.5;\n // }\n\n // Perform initial step at the volume boundary\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n #if vtkBlendMode == 0 // COMPOSITE_BLEND\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n // handle very thin volumes\n if (raySteps <= 1.0)\n {\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps);\n gl_FragData[0] = tColor;\n return;\n }\n\n tColor.a = 1.0 - pow(1.0 - tColor.a, jitter);\n color = vec4(tColor.rgb*tColor.a, tColor.a);\n posIS += (jitter*stepIS);\n\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n float mix = (1.0 - color.a);\n\n // this line should not be needed but nvidia seems to not handle\n // the break correctly on windows/chrome 58 angle\n //mix = mix * sign(max(raySteps - stepsTraveled - 1.0, 0.0));\n\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n stepsTraveled++;\n posIS += stepIS;\n if (color.a > 0.99) { color.a = 1.0; break; }\n }\n\n if (color.a < 0.99 && (raySteps - stepsTraveled) > 0.0)\n {\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps - stepsTraveled);\n\n float mix = (1.0 - color.a);\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n }\n\n gl_FragData[0] = vec4(color.rgb/color.a, color.a);\n #endif\n #if vtkBlendMode == 1 || vtkBlendMode == 2\n // MAXIMUM_INTENSITY_BLEND || MINIMUM_INTENSITY_BLEND\n // Find maximum/minimum intensity along the ray.\n\n // Define the operation we will use (min or max)\n #if vtkBlendMode == 1\n #define OP max\n #else\n #define OP min\n #endif\n\n // If the clipping range is shorter than the sample distance\n // we can skip the sampling loop along the ray.\n if (raySteps <= 1.0)\n {\n gl_FragData[0] = getColorForValue(tValue, posIS, tstep);\n return;\n }\n\n vec4 value = tValue;\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // Update the maximum value if necessary\n value = OP(tValue, value);\n\n // Otherwise, continue along the ray\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n tValue = getTextureValue(posIS);\n value = OP(tValue, value);\n\n // Now map through opacity and color\n gl_FragData[0] = getColorForValue(value, posIS, tstep);\n #endif\n #if vtkBlendMode == 3 || vtkBlendMode == 4 //AVERAGE_INTENSITY_BLEND || ADDITIVE_BLEND\n vec4 sum = vec4(0.);\n\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n }\n\n if (raySteps <= 1.0) {\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n return;\n }\n\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the AverageIPScalarRange to disregard scalar values, not in the range of interest, from the average computation.\n // Notes:\n // - We are comparing all values in the texture to see if any of them\n // are outside of the scalar range. In the future we might want to allow\n // scalar ranges for each component.\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n // Sum the values across each step in the path\n sum += tValue;\n }\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the IPScalarRange to disregard scalar values, not in the range of interest, from the average computation\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n\n stepsTraveled++;\n }\n\n #if vtkBlendMode == 3 // Average\n sum /= vec4(stepsTraveled, stepsTraveled, stepsTraveled, 1.0);\n #endif\n\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n #endif\n}\n\n//=======================================================================\n// Compute a new start and end point for a given ray based\n// on the provided bounded clipping plane (aka a rectangle)\nvoid getRayPointIntersectionBounds(\n vec3 rayPos, vec3 rayDir,\n vec3 planeDir, float planeDist,\n inout vec2 tbounds, vec3 vPlaneX, vec3 vPlaneY,\n float vSize1, float vSize2)\n{\n float result = dot(rayDir, planeDir);\n if (abs(result) < 1e-6)\n {\n return;\n }\n result = -1.0 * (dot(rayPos, planeDir) + planeDist) / result;\n vec3 xposVC = rayPos + rayDir*result;\n vec3 vxpos = xposVC - vOriginVC;\n vec2 vpos = vec2(\n dot(vxpos, vPlaneX),\n dot(vxpos, vPlaneY));\n\n // on some apple nvidia systems this does not work\n // if (vpos.x < 0.0 || vpos.x > vSize1 ||\n // vpos.y < 0.0 || vpos.y > vSize2)\n // even just\n // if (vpos.x < 0.0 || vpos.y < 0.0)\n // fails\n // so instead we compute a value that represents in and out\n //and then compute the return using this value\n float xcheck = max(0.0, vpos.x * (vpos.x - vSize1)); // 0 means in bounds\n float check = sign(max(xcheck, vpos.y * (vpos.y - vSize2))); // 0 means in bounds, 1 = out\n\n tbounds = mix(\n vec2(min(tbounds.x, result), max(tbounds.y, result)), // in value\n tbounds, // out value\n check); // 0 in 1 out\n}\n\n//=======================================================================\n// given a\n// - ray direction (rayDir)\n// - starting point (vertexVCVSOutput)\n// - bounding planes of the volume\n// - optionally depth buffer values\n// - far clipping plane\n// compute the start/end distances of the ray we need to cast\nvec2 computeRayDistances(vec3 rayDir, vec3 tdims)\n{\n vec2 dists = vec2(100.0*camFar, -1.0);\n\n vec3 vSize = vSpacing*tdims;\n\n // all this is in View Coordinates\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal0, vPlaneDistance0, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal1, vPlaneDistance1, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal2, vPlaneDistance2, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal3, vPlaneDistance3, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal4, vPlaneDistance4, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal5, vPlaneDistance5, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n\n //VTK::ClipPlane::Impl\n\n // do not go behind front clipping plane\n dists.x = max(0.0,dists.x);\n\n // do not go PAST far clipping plane\n float farDist = -camThick/rayDir.z;\n dists.y = min(farDist,dists.y);\n\n // Do not go past the zbuffer value if set\n // This is used for intermixing opaque geometry\n //VTK::ZBuffer::Impl\n\n return dists;\n}\n\n//=======================================================================\n// Compute the index space starting position (pos) and end\n// position\n//\nvoid computeIndexSpaceValues(out vec3 pos, out vec3 endPos, vec3 rayDir, vec2 dists)\n{\n // compute starting and ending values in volume space\n pos = vertexVCVSOutput + dists.x*rayDir;\n pos = pos - vOriginVC;\n // convert to volume basis and origin\n pos = vec3(\n dot(pos, vPlaneNormal0),\n dot(pos, vPlaneNormal2),\n dot(pos, vPlaneNormal4));\n\n endPos = vertexVCVSOutput + dists.y*rayDir;\n endPos = endPos - vOriginVC;\n endPos = vec3(\n dot(endPos, vPlaneNormal0),\n dot(endPos, vPlaneNormal2),\n dot(endPos, vPlaneNormal4));\n\n float delta = length(endPos - pos);\n\n pos *= vVCToIJK;\n endPos *= vVCToIJK;\n\n float delta2 = length(endPos - pos);\n sampleDistanceIS = sampleDistance*delta2/delta;\n #ifdef VolumeShadowOn\n sampleDistanceISVS = sampleDistanceIS * volumeShadowSamplingDistFactor;\n #endif\n}\n\nvoid main()\n{\n\n if (cameraParallel == 1)\n {\n // Camera is parallel, so the rayDir is just the direction of the camera.\n rayDirVC = vec3(0.0, 0.0, -1.0);\n } else {\n // camera is at 0,0,0 so rayDir for perspective is just the vc coord\n rayDirVC = normalize(vertexVCVSOutput);\n }\n\n vec3 tdims = vec3(volumeDimensions);\n\n // compute the start and end points for the ray\n vec2 rayStartEndDistancesVC = computeRayDistances(rayDirVC, tdims);\n\n // do we need to composite? aka does the ray have any length\n // If not, bail out early\n if (rayStartEndDistancesVC.y <= rayStartEndDistancesVC.x)\n {\n discard;\n }\n\n // IS = Index Space\n vec3 posIS;\n vec3 endIS;\n computeIndexSpaceValues(posIS, endIS, rayDirVC, rayStartEndDistancesVC);\n\n // Perform the blending operation along the ray\n applyBlend(posIS, endIS, tdims);\n}\n";
1
+ var vtkVolumeFS = "//VTK::System::Dec\n\n/*=========================================================================\n\n Program: Visualization Toolkit\n Module: vtkVolumeFS.glsl\n\n Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen\n All rights reserved.\n See Copyright.txt or http://www.kitware.com/Copyright.htm for details.\n\n This software is distributed WITHOUT ANY WARRANTY; without even\n the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\n PURPOSE. See the above copyright notice for more information.\n\n=========================================================================*/\n// Template for the volume mappers fragment shader\n\n// the output of this shader\n//VTK::Output::Dec\n\nvarying vec3 vertexVCVSOutput;\n\n// first declare the settings from the mapper\n// that impact the code paths in here\n\n// always set vtkNumComponents 1,2,3,4\n//VTK::NumComponents\n\n// possibly define vtkTrilinearOn\n//VTK::TrilinearOn\n\n// possibly define vtkIndependentComponents\n//VTK::IndependentComponentsOn\n\n// possibly define any \"proportional\" components\n//VTK::vtkProportionalComponents\n\n// Define the blend mode to use\n#define vtkBlendMode //VTK::BlendMode\n\n// Possibly define vtkImageLabelOutlineOn\n//VTK::ImageLabelOutlineOn\n\n#ifdef vtkImageLabelOutlineOn\nuniform int outlineThickness;\nuniform float vpWidth;\nuniform float vpHeight;\nuniform float vpOffsetX;\nuniform float vpOffsetY;\nuniform mat4 PCWCMatrix;\nuniform mat4 vWCtoIDX;\n#endif\n\n// define vtkLightComplexity\n//VTK::LightComplexity\n#if vtkLightComplexity > 0\nuniform float vSpecularPower;\nuniform float vAmbient;\nuniform float vDiffuse;\nuniform float vSpecular;\n//VTK::Light::Dec\n#endif\n\n//VTK::VolumeShadowOn\n//VTK::SurfaceShadowOn\n//VTK::localAmbientOcclusionOn\n//VTK::LAO::Dec\n//VTK::VolumeShadow::Dec\n\n// define vtkComputeNormalFromOpacity\n//VTK::vtkComputeNormalFromOpacity\n\n// possibly define vtkGradientOpacityOn\n//VTK::GradientOpacityOn\n#ifdef vtkGradientOpacityOn\nuniform float goscale0;\nuniform float goshift0;\nuniform float gomin0;\nuniform float gomax0;\n#if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\nuniform float goscale1;\nuniform float goshift1;\nuniform float gomin1;\nuniform float gomax1;\n#if vtkNumComponents >= 3\nuniform float goscale2;\nuniform float goshift2;\nuniform float gomin2;\nuniform float gomax2;\n#endif\n#if vtkNumComponents >= 4\nuniform float goscale3;\nuniform float goshift3;\nuniform float gomin3;\nuniform float gomax3;\n#endif\n#endif\n#endif\n\n// if you want to see the raw tiled\n// data in webgl1 uncomment the following line\n// #define debugtile\n\n// camera values\nuniform float camThick;\nuniform float camNear;\nuniform float camFar;\nuniform int cameraParallel;\n\n// values describing the volume geometry\nuniform vec3 vOriginVC;\nuniform vec3 vSpacing;\nuniform ivec3 volumeDimensions; // 3d texture dimensions\nuniform vec3 vPlaneNormal0;\nuniform float vPlaneDistance0;\nuniform vec3 vPlaneNormal1;\nuniform float vPlaneDistance1;\nuniform vec3 vPlaneNormal2;\nuniform float vPlaneDistance2;\nuniform vec3 vPlaneNormal3;\nuniform float vPlaneDistance3;\nuniform vec3 vPlaneNormal4;\nuniform float vPlaneDistance4;\nuniform vec3 vPlaneNormal5;\nuniform float vPlaneDistance5;\n\n//VTK::ClipPlane::Dec\n\n// opacity and color textures\nuniform sampler2D otexture;\nuniform float oshift0;\nuniform float oscale0;\nuniform sampler2D ctexture;\nuniform float cshift0;\nuniform float cscale0;\n\n// jitter texture\nuniform sampler2D jtexture;\n\n// some 3D texture values\nuniform float sampleDistance;\nuniform vec3 vVCToIJK;\n\n// the heights defined below are the locations\n// for the up to four components of the tfuns\n// the tfuns have a height of 2XnumComps pixels so the\n// values are computed to hit the middle of the two rows\n// for that component\n#ifdef vtkIndependentComponentsOn\n#if vtkNumComponents == 2\nuniform float mix0;\nuniform float mix1;\n#define height0 0.25\n#define height1 0.75\n#endif\n#if vtkNumComponents == 3\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\n#define height0 0.17\n#define height1 0.5\n#define height2 0.83\n#endif\n#if vtkNumComponents == 4\nuniform float mix0;\nuniform float mix1;\nuniform float mix2;\nuniform float mix3;\n#define height0 0.125\n#define height1 0.375\n#define height2 0.625\n#define height3 0.875\n#endif\n#endif\n\n#if vtkNumComponents >= 2\nuniform float oshift1;\nuniform float oscale1;\nuniform float cshift1;\nuniform float cscale1;\n#endif\n#if vtkNumComponents >= 3\nuniform float oshift2;\nuniform float oscale2;\nuniform float cshift2;\nuniform float cscale2;\n#endif\n#if vtkNumComponents >= 4\nuniform float oshift3;\nuniform float oscale3;\nuniform float cshift3;\nuniform float cscale3;\n#endif\n\nuniform vec4 ipScalarRangeMin;\nuniform vec4 ipScalarRangeMax;\n\n// declaration for intermixed geometry\n//VTK::ZBuffer::Dec\n\n//=======================================================================\n// global and custom variables (a temporary section before photorealistics rendering module is complete)\nvec3 rayDirVC;\nfloat sampleDistanceISVS;\nfloat sampleDistanceIS;\n\n#define SQRT3 1.7321\n#define INV4PI 0.0796\n#define EPSILON 0.001\n#define PI 3.1415\n#define PI2 9.8696\n\n//=======================================================================\n// Webgl2 specific version of functions\n#if __VERSION__ == 300\n\nuniform highp sampler3D texture1;\n\nvec4 getTextureValue(vec3 pos)\n{\n vec4 tmp = texture(texture1, pos);\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.a = tmp.g;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n return tmp;\n}\n\n//=======================================================================\n// WebGL1 specific version of functions\n#else\n\nuniform sampler2D texture1;\n\nuniform float texWidth;\nuniform float texHeight;\nuniform int xreps;\nuniform int xstride;\nuniform int ystride;\n\n// if computing trilinear values from multiple z slices\n#ifdef vtkTrilinearOn\nvec4 getTextureValue(vec3 ijk)\n{\n float zoff = 1.0/float(volumeDimensions.z);\n vec4 val1 = getOneTextureValue(ijk);\n vec4 val2 = getOneTextureValue(vec3(ijk.xy, ijk.z + zoff));\n\n float indexZ = float(volumeDimensions)*ijk.z;\n float zmix = indexZ - floor(indexZ);\n\n return mix(val1, val2, zmix);\n}\n\nvec4 getOneTextureValue(vec3 ijk)\n#else // nearest or fast linear\nvec4 getTextureValue(vec3 ijk)\n#endif\n{\n vec3 tdims = vec3(volumeDimensions);\n\n#ifdef debugtile\n vec2 tpos = vec2(ijk.x, ijk.y);\n vec4 tmp = texture2D(texture1, tpos);\n tmp.a = 1.0;\n\n#else\n int z = int(ijk.z * tdims.z);\n int yz = z / xreps;\n int xz = z - yz*xreps;\n\n int tileWidth = volumeDimensions.x/xstride;\n int tileHeight = volumeDimensions.y/ystride;\n\n xz *= tileWidth;\n yz *= tileHeight;\n\n float ni = float(xz) + (ijk.x*float(tileWidth));\n float nj = float(yz) + (ijk.y*float(tileHeight));\n\n vec2 tpos = vec2(ni/texWidth, nj/texHeight);\n\n vec4 tmp = texture2D(texture1, tpos);\n\n#if vtkNumComponents == 1\n tmp.a = tmp.r;\n#endif\n#if vtkNumComponents == 2\n tmp.g = tmp.a;\n#endif\n#if vtkNumComponents == 3\n tmp.a = length(tmp.rgb);\n#endif\n#endif\n\n return tmp;\n}\n\n// End of Webgl1 specific code\n//=======================================================================\n#endif\n\n//=======================================================================\n// transformation between VC and IS space\n\n// convert vector position from idx to vc\n#if vtkLightComplexity > 0\nvec3 IStoVC(vec3 posIS){\n vec3 posVC = posIS / vVCToIJK;\n return posVC.x * vPlaneNormal0 + \n posVC.y * vPlaneNormal2 + \n posVC.z * vPlaneNormal4 + \n vOriginVC;\n}\n\n// convert vector position from vc to idx\nvec3 VCtoIS(vec3 posVC){\n posVC = posVC - vOriginVC;\n posVC = vec3(\n dot(posVC, vPlaneNormal0),\n dot(posVC, vPlaneNormal2),\n dot(posVC, vPlaneNormal4)); \n return posVC * vVCToIJK;\n}\n#endif\n\n//Rotate vector to view coordinate\n#if (vtkLightComplexity > 0) || (defined vtkGradientOpacityOn)\nvoid rotateToViewCoord(inout vec3 dirIS){\n dirIS.xyz =\n dirIS.x * vPlaneNormal0 +\n dirIS.y * vPlaneNormal2 +\n dirIS.z * vPlaneNormal4;\n}\n\n//Rotate vector to idx coordinate\nvec3 rotateToIDX(vec3 dirVC){\n vec3 dirIS;\n dirIS.xyz = vec3(\n dot(dirVC, vPlaneNormal0),\n dot(dirVC, vPlaneNormal2),\n dot(dirVC, vPlaneNormal4)); \n return dirIS;\n}\n#endif\n\n//=======================================================================\n// Given a normal compute the gradient opacity factors\nfloat computeGradientOpacityFactor(\n float normalMag, float goscale, float goshift, float gomin, float gomax)\n{\n#if defined(vtkGradientOpacityOn)\n return clamp(normalMag * goscale + goshift, gomin, gomax);\n#else\n return 1.0;\n#endif\n}\n\n//=======================================================================\n// compute the normal and gradient magnitude for a position, uses forward difference\n#if (vtkLightComplexity > 0) || (defined vtkGradientOpacityOn)\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n vec4 computeNormalForDensity(vec3 pos, float scalar, vec3 tstep, out mat3 scalarInterp, out vec3 secondaryGradientMag)\n {\n vec4 result;\n scalarInterp[0][0] = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a;\n scalarInterp[0][1] = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a;\n scalarInterp[0][2] = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a;\n // look up scalar values for computing secondary gradient\n scalarInterp[1][0] = getTextureValue(pos + vec3(2.0*tstep.x, 0.0, 0.0)).a;\n scalarInterp[1][1] = getTextureValue(pos + vec3(0.0, 2.0*tstep.y, 0.0)).a;\n scalarInterp[1][2] = getTextureValue(pos + vec3(0.0, 0.0, 2.0*tstep.z)).a;\n scalarInterp[2][0] = getTextureValue(pos + vec3(tstep.x, tstep.y, 0.0)).a;\n scalarInterp[2][1] = getTextureValue(pos + vec3(tstep.x, 0.0, tstep.z)).a;\n scalarInterp[2][2] = getTextureValue(pos + vec3(0.0, tstep.y, tstep.z)).a;\n result.x = scalarInterp[0][0] - scalar;\n result.y = scalarInterp[0][1] - scalar;\n result.z = scalarInterp[0][2] - scalar;\n // divide by spacing\n result.xyz /= vSpacing;\n result.w = length(result.xyz);\n rotateToViewCoord(result.xyz);\n secondaryGradientMag.x = length(vec3(scalarInterp[1][0] - scalarInterp[0][0],\n scalarInterp[2][0] - scalarInterp[0][0],\n scalarInterp[2][1] - scalarInterp[0][0]) / vSpacing);\n secondaryGradientMag.y = length(vec3(scalarInterp[2][0] - scalarInterp[0][1],\n scalarInterp[1][1] - scalarInterp[0][1],\n scalarInterp[2][2] - scalarInterp[0][1]) / vSpacing);\n secondaryGradientMag.z = length(vec3(scalarInterp[2][1] - scalarInterp[0][2],\n scalarInterp[2][2] - scalarInterp[0][2],\n scalarInterp[1][2] - scalarInterp[0][2]) / vSpacing);\n if (length(result.xyz) > 0.0) {\n return vec4(normalize(result.xyz),result.w);\n } else {\n return vec4(0.0);\n }\n }\n\n vec4 computeDensityNormal(float scalar, float gradientMag, mat3 scalarInterp, vec3 secondaryGradientMag)\n {\n vec4 opacityG;\n vec3 opacityInterp = vec3(0.0);\n float opacity = texture2D(otexture, vec2(scalar * oscale0 + oshift0, 0.5)).r;\n if (gradientMag >= 0.0){\n opacity *= computeGradientOpacityFactor(gradientMag, goscale0, goshift0, gomin0, gomax0);\n }\n opacityInterp.x = texture2D(otexture, vec2(scalarInterp[0][0] * oscale0 + oshift0, 0.5)).r; \n if (secondaryGradientMag.x >= 0.0){\n opacityInterp.x *= computeGradientOpacityFactor(secondaryGradientMag.x, goscale0, goshift0, gomin0, gomax0);\n }\n \n opacityInterp.y = texture2D(otexture, vec2(scalarInterp[0][1] * oscale0 + oshift0, 0.5)).r;\n if (secondaryGradientMag.y >= 0.0){\n opacityInterp.y *= computeGradientOpacityFactor(secondaryGradientMag.y, goscale0, goshift0, gomin0, gomax0);\n }\n\n opacityInterp.z = texture2D(otexture, vec2(scalarInterp[0][2] * oscale0 + oshift0, 0.5)).r;\n if (secondaryGradientMag.z >= 0.0){\n opacityInterp.z *= computeGradientOpacityFactor(secondaryGradientMag.z, goscale0, goshift0, gomin0, gomax0);\n }\n\n opacityG.xyz = opacityInterp - vec3(opacity,opacity,opacity);\n // divide by spacing\n opacityG.xyz /= vSpacing;\n opacityG.w = length(opacityG.xyz);\n rotateToViewCoord(opacityG.xyz);\n if (length(opacityG.xyz) > 0.0) { \n return vec4(normalize(opacityG.xyz),opacityG.w);\n } else {\n return vec4(0.0);\n }\n } \n\n #else\n //if gradient opacity not on but using density gradient\n vec4 computeDensityNormal(float scalar, vec3 scalarInterp) \n { \n vec4 opacityG; \n float opacity = texture2D(otexture, vec2(scalar * oscale0 + oshift0, 0.5)).r; \n opacityG.x = texture2D(otexture, vec2(scalarInterp.x * oscale0 + oshift0, 0.5)).r - opacity; \n opacityG.y = texture2D(otexture, vec2(scalarInterp.y * oscale0 + oshift0, 0.5)).r - opacity; \n opacityG.z = texture2D(otexture, vec2(scalarInterp.z * oscale0 + oshift0, 0.5)).r - opacity; \n // divide by spacing \n opacityG.xyz /= vSpacing; \n opacityG.w = length(opacityG.xyz); \n // rotate to View Coords \n rotateToViewCoord(opacityG.xyz);\n if (length(opacityG.xyz) > 0.0) { \n return vec4(normalize(opacityG.xyz),opacityG.w); \n } else { \n return vec4(0.0); \n } \n } \n vec4 computeNormalForDensity(vec3 pos, float scalar, vec3 tstep, out vec3 scalarInterp) \n { \n vec4 result; \n scalarInterp.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a; \n scalarInterp.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a; \n scalarInterp.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a; \n result.x = scalarInterp.x - scalar; \n result.y = scalarInterp.y - scalar; \n result.z = scalarInterp.z - scalar; \n // divide by spacing\n result.xyz /= vSpacing;\n result.w = length(result.xyz); \n // rotate to View Coords \n rotateToViewCoord(result.xyz); \n if (length(result.xyz) > 0.0) { \n return vec4(normalize(result.xyz),result.w); \n } else { \n return vec4(0.0); \n } \n } \n #endif\n #endif\n // compute scalar density\n vec4 computeNormal(vec3 pos, float scalar, vec3 tstep) \n { \n vec4 result; \n result.x = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)).a - scalar; \n result.y = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)).a - scalar; \n result.z = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)).a - scalar; \n // divide by spacing \n result.xyz /= vSpacing; \n result.w = length(result.xyz);\n if (result.w > 0.0){\n // rotate to View Coords \n rotateToViewCoord(result.xyz);\n return vec4(normalize(result.xyz),result.w); \n } else {\n return vec4(0.0);\n }\n } \n#endif\n\n#ifdef vtkImageLabelOutlineOn\nvec3 fragCoordToIndexSpace(vec4 fragCoord) {\n vec4 pcPos = vec4(\n (fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,\n (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,\n (fragCoord.z - 0.5) * 2.0,\n 1.0);\n\n vec4 worldCoord = PCWCMatrix * pcPos;\n vec4 vertex = (worldCoord/worldCoord.w);\n\n vec3 index = (vWCtoIDX * vertex).xyz;\n\n // half voxel fix for labelmapOutline \n return (index + vec3(0.5)) / vec3(volumeDimensions);\n}\n#endif\n\n//=======================================================================\n// compute the normals and gradient magnitudes for a position\n// for independent components\nmat4 computeMat4Normal(vec3 pos, vec4 tValue, vec3 tstep)\n{\n mat4 result;\n vec4 distX = getTextureValue(pos + vec3(tstep.x, 0.0, 0.0)) - tValue;\n vec4 distY = getTextureValue(pos + vec3(0.0, tstep.y, 0.0)) - tValue;\n vec4 distZ = getTextureValue(pos + vec3(0.0, 0.0, tstep.z)) - tValue;\n\n // divide by spacing\n distX /= vSpacing.x;\n distY /= vSpacing.y;\n distZ /= vSpacing.z;\n\n mat3 rot;\n rot[0] = vPlaneNormal0;\n rot[1] = vPlaneNormal2;\n rot[2] = vPlaneNormal4;\n\n#if !defined(vtkComponent0Proportional)\n result[0].xyz = vec3(distX.r, distY.r, distZ.r);\n result[0].a = length(result[0].xyz);\n result[0].xyz *= rot;\n if (result[0].w > 0.0)\n {\n result[0].xyz /= result[0].w;\n }\n#endif\n\n// optionally compute the 2nd component\n#if vtkNumComponents >= 2 && !defined(vtkComponent1Proportional)\n result[1].xyz = vec3(distX.g, distY.g, distZ.g);\n result[1].a = length(result[1].xyz);\n result[1].xyz *= rot;\n if (result[1].w > 0.0)\n {\n result[1].xyz /= result[1].w;\n }\n#endif\n\n// optionally compute the 3rd component\n#if vtkNumComponents >= 3 && !defined(vtkComponent2Proportional)\n result[2].xyz = vec3(distX.b, distY.b, distZ.b);\n result[2].a = length(result[2].xyz);\n result[2].xyz *= rot;\n if (result[2].w > 0.0)\n {\n result[2].xyz /= result[2].w;\n }\n#endif\n\n// optionally compute the 4th component\n#if vtkNumComponents >= 4 && !defined(vtkComponent3Proportional)\n result[3].xyz = vec3(distX.a, distY.a, distZ.a);\n result[3].a = length(result[3].xyz);\n result[3].xyz *= rot;\n if (result[3].w > 0.0)\n {\n result[3].xyz /= result[3].w;\n }\n#endif\n\n return result;\n}\n\n//=======================================================================\n// global shadow - secondary ray\n#if defined(VolumeShadowOn) || defined(localAmbientOcclusionOn)\nfloat random()\n{ \n float rand = fract(sin(dot(gl_FragCoord.xy,vec2(12.9898,78.233)))*43758.5453123);\n float jitter=texture2D(jtexture,gl_FragCoord.xy/32.).r;\n uint pcg_state = floatBitsToUint(jitter);\n uint state = pcg_state;\n pcg_state = pcg_state * uint(747796405) + uint(2891336453);\n uint word = ((state >> ((state >> uint(28)) + uint(4))) ^ state) * uint(277803737);\n return (float((((word >> uint(22)) ^ word) >> 1 ))/float(2147483647) + rand)/2.0;\n}\n#endif\n\n#ifdef VolumeShadowOn\n// henyey greenstein phase function\nfloat phase_function(float cos_angle)\n{\n // divide by 2.0 instead of 4pi to increase intensity\n return ((1.0-anisotropy2)/pow(1.0+anisotropy2-2.0*anisotropy*cos_angle, 1.5))/2.0;\n}\n\n// Computes the intersection between a ray and a box\nstruct Hit\n{\n float tmin;\n float tmax;\n};\n\nstruct Ray\n{\n vec3 origin;\n vec3 dir;\n vec3 invDir;\n};\n\nbool BBoxIntersect(vec3 boundMin, vec3 boundMax, const Ray r, out Hit hit)\n{\n vec3 tbot = r.invDir * (boundMin - r.origin);\n vec3 ttop = r.invDir * (boundMax - r.origin);\n vec3 tmin = min(ttop, tbot);\n vec3 tmax = max(ttop, tbot);\n vec2 t = max(tmin.xx, tmin.yz);\n float t0 = max(t.x, t.y);\n t = min(tmax.xx, tmax.yz);\n float t1 = min(t.x, t.y);\n hit.tmin = t0;\n hit.tmax = t1;\n return t1 > max(t0,0.0);\n}\n\n// As BBoxIntersect requires the inverse of the ray coords,\n// this function is used to avoid numerical issues\nvoid safe_0_vector(inout Ray ray)\n{\n if(abs(ray.dir.x) < EPSILON) ray.dir.x = sign(ray.dir.x) * EPSILON;\n if(abs(ray.dir.y) < EPSILON) ray.dir.y = sign(ray.dir.y) * EPSILON;\n if(abs(ray.dir.z) < EPSILON) ray.dir.z = sign(ray.dir.z) * EPSILON;\n}\n\nfloat volume_shadow(vec3 posIS, vec3 lightDirNormIS)\n{\n float shadow = 1.0;\n float opacity = 0.0;\n\n // modify sample distance with a random number between 0.8 and 1.0\n float sampleDistanceISVS_jitter = sampleDistanceISVS * mix(0.8, 1.0, random());\n float opacityPrev = texture2D(otexture, vec2(getTextureValue(posIS).r * oscale0 + oshift0, 0.5)).r;\n \n // in case the first sample near surface has a very tiled light ray, we need to offset start position \n posIS += sampleDistanceISVS_jitter * lightDirNormIS; \n\n // compute the start and end points for the ray\n Ray ray;\n Hit hit; \n ray.origin = posIS;\n ray.dir = lightDirNormIS;\n safe_0_vector(ray);\n ray.invDir = 1.0/ray.dir;\n \n if(!BBoxIntersect(vec3(0.0),vec3(1.0), ray, hit))\n {\n return 1.0;\n }\n vec4 scalar = vec4(0.0);\n float maxdist = hit.tmax;\n if(maxdist < EPSILON) {\n return 1.0;\n }\n\n // interpolate shadow ray length between: 1 unit of sample distance in IS to SQRT3, based on globalIlluminationReach\n float maxgi = mix(sampleDistanceISVS_jitter,SQRT3,giReach);\n maxdist = min(maxdist,maxgi);\n\n // support gradient opacity\n #ifdef vtkGradientOpacityOn\n vec4 normal;\n #endif\n\n vec3 current_step = sampleDistanceISVS_jitter * lightDirNormIS;\n float maxSteps = ceil(maxdist/sampleDistanceISVS_jitter);\n float opacityDelta = 0.0;\n\n for (float i = 0.0; i < maxSteps; i++)\n {\n scalar = getTextureValue(posIS);\n opacity = texture2D(otexture, vec2(scalar.r * oscale0 + oshift0, 0.5)).r;\n #ifdef vtkGradientOpacityOn \n normal = computeNormal(posIS, scalar.a, vec3(1.0/vec3(volumeDimensions))); \n opacity *= computeGradientOpacityFactor(normal.w, goscale0, goshift0, gomin0, gomax0);\n #endif \n shadow *= 1.0 - opacity;\n\n // optimization: early termination\n if (shadow < EPSILON){\n return 0.0;\n }\n\n // optimization: increase/decrease sample distance based on changed in opacity value\n opacityDelta = opacityPrev - opacity;\n opacityPrev = opacity;\n if (opacityDelta > 0.0){\n current_step *= 0.9;\n } else if (opacityDelta < 0.0){\n current_step *= 1.1;\n }\n posIS += current_step;\n }\n\n return shadow; \n}\n\nvec3 applyShadowRay(vec3 tColor, vec3 posIS, vec3 viewDirectionVC)\n{\n vec3 vertLight = vec3(0.0);\n vec3 secondary_contrib = vec3(0.0);\n // here we assume only positional light, no effect of cones\n for (int i = 0; i < lightNum; i++)\n {\n #if(vtkLightComplexity==3)\n if (lightPositional[i] == 1){\n vertLight = lightPositionVC[i] - IStoVC(posIS);\n }else{\n vertLight = - lightDirectionVC[i];\n }\n #else\n vertLight = - lightDirectionVC[i];\n #endif\n // here we assume achromatic light, only intensity\n float dDotL = dot(viewDirectionVC, normalize(vertLight));\n // isotropic scatter returns 0.5 instead of 1/4pi to increase intensity\n float phase_attenuation = 0.5;\n if (abs(anisotropy) > EPSILON){\n phase_attenuation = phase_function(dDotL);\n }\n float vol_shadow = volume_shadow(posIS, normalize(rotateToIDX(vertLight)));\n secondary_contrib += tColor * vDiffuse * lightColor[i] * vol_shadow * phase_attenuation; \n secondary_contrib += tColor * vAmbient;\n } \n return secondary_contrib;\n}\n#endif\n\n//=======================================================================\n// local ambient occlusion\n#ifdef localAmbientOcclusionOn\nvec3 sample_direction_uniform(int i)\n{\n float rand = random() * 0.5;\n float theta = PI2 * (kernelSample[i][0] + rand);\n float phi = acos(2.0 * (kernelSample[i][1] + rand) -1.0) / 2.5;\n return normalize(vec3(cos(theta)*sin(phi), sin(theta)*sin(phi), cos(phi)));\n}\n\n// return a matrix that transform startDir into z axis; startDir should be normalized\nmat3 zBaseRotationalMatrix(vec3 startDir){\n vec3 axis = cross(startDir, vec3(0.0,0.0,1.0));\n float cosA = startDir.z;\n float k = 1.0 / (1.0 + cosA);\n mat3 matrix = mat3((axis.x * axis.x * k) + cosA, (axis.y * axis.x * k) - axis.z, (axis.z * axis.x * k) + axis.y,\n (axis.x * axis.y * k) + axis.z, (axis.y * axis.y * k) + cosA, (axis.z * axis.y * k) - axis.x,\n (axis.x * axis.z * k) - axis.y, (axis.y * axis.z * k) + axis.x, (axis.z * axis.z * k) + cosA);\n return matrix;\n}\n\nfloat computeLAO(vec3 posIS, float opacity, vec3 lightDir, vec4 normal){\n // apply LAO only at selected locations, otherwise return full brightness\n if (normal.w > 0.0 && opacity > 0.05){\n float total_transmittance = 0.0;\n mat3 inverseRotateBasis = inverse(zBaseRotationalMatrix(normalize(-normal.xyz)));\n vec3 currPos, randomDirStep;\n float weight, transmittance, opacity;\n for (int i = 0; i < kernelSize; i++)\n {\n randomDirStep = inverseRotateBasis * sample_direction_uniform(i) * sampleDistanceIS;\n weight = 1.0 - dot(normalize(lightDir), normalize(randomDirStep));\n currPos = posIS;\n transmittance = 1.0;\n for (int j = 0; j < kernelRadius ; j++){\n currPos += randomDirStep;\n // check if it's at clipping plane, if so return full brightness\n if (all(greaterThan(currPos, vec3(EPSILON))) && all(lessThan(currPos,vec3(1.0-EPSILON)))){\n opacity = texture2D(otexture, vec2(getTextureValue(currPos).r * oscale0 + oshift0, 0.5)).r;\n #ifdef vtkGradientOpacityOn\n opacity *= computeGradientOpacityFactor(normal.w, goscale0, goshift0, gomin0, gomax0);\n #endif\n transmittance *= 1.0 - opacity;\n }\n else{\n break;\n }\n }\n total_transmittance += transmittance / float(kernelRadius) * weight;\n\n // early termination if fully translucent\n if (total_transmittance > 1.0 - EPSILON){\n return 1.0;\n }\n }\n // average transmittance and reduce variance\n return clamp(total_transmittance / float(kernelSize), 0.3, 1.0); \n } else {\n return 1.0;\n }\n}\n#endif\n\n//=======================================================================\n// surface light contribution\n#if vtkLightComplexity > 0\n void applyLighting(inout vec3 tColor, vec4 normal)\n {\n vec3 diffuse = vec3(0.0, 0.0, 0.0);\n vec3 specular = vec3(0.0, 0.0, 0.0);\n float df, sf = 0.0;\n for (int i = 0; i < lightNum; i++){\n df = abs(dot(normal.rgb, -lightDirectionVC[i]));\n diffuse += df * lightColor[i];\n sf = pow( abs(dot(lightHalfAngleVC[i],normal.rgb)), vSpecularPower);\n specular += sf * lightColor[i];\n }\n tColor.rgb = tColor.rgb*(diffuse*vDiffuse + vAmbient) + specular*vSpecular;\n }\n #ifdef SurfaceShadowOn\n #if vtkLightComplexity < 3\n vec3 applyLightingDirectional(vec3 posIS, vec4 tColor, vec4 normal)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n #ifdef localAmbientOcclusionOn\n vec3 ambient = vec3(0.0);\n #endif \n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float ndotL,vdotR;\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * lightColor[i];\n }\n }\n #ifdef localAmbientOcclusionOn\n ambient += computeLAO(posIS, tColor.a, vertLightDirection, normal);\n #endif\n } \n #ifdef localAmbientOcclusionOn\n return tColor.rgb * (diffuse * vDiffuse + vAmbient * ambient) + specular*vSpecular;\n #else \n return tColor.rgb * (diffuse * vDiffuse + vAmbient) + specular*vSpecular;\n #endif \n }\n #else\n vec3 applyLightingPositional(vec3 posIS, vec4 tColor, vec4 normal, vec3 posVC)\n {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n #ifdef localAmbientOcclusionOn\n vec3 ambient = vec3(0.0);\n #endif \n vec3 vertLightDirection;\n for (int i = 0; i < lightNum; i++){\n float distance,attenuation,ndotL,vdotR;\n vec3 lightDir;\n if (lightPositional[i] == 1){\n lightDir = lightDirectionVC[i];\n vertLightDirection = posVC - lightPositionVC[i]; \n distance = length(vertLightDirection);\n vertLightDirection = normalize(vertLightDirection);\n attenuation = 1.0 / (lightAttenuation[i].x\n + lightAttenuation[i].y * distance\n + lightAttenuation[i].z * distance * distance);\n // per OpenGL standard cone angle is 90 or less for a spot light\n if (lightConeAngle[i] <= 90.0){\n float coneDot = dot(vertLightDirection, lightDir);\n if (coneDot >= cos(radians(lightConeAngle[i]))){ // if inside cone\n attenuation = attenuation * pow(coneDot, lightExponent[i]);\n }\n else {\n attenuation = 0.0;\n }\n }\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * attenuation * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * attenuation * lightColor[i];\n }\n }\n #ifdef localAmbientOcclusionOn\n ambient += computeLAO(posIS, tColor.a, vertLightDirection, normal);\n #endif \n } else {\n vertLightDirection = lightDirectionVC[i];\n ndotL = dot(normal.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting)\n {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0)\n {\n diffuse += ndotL * lightColor[i];\n //specular\n vdotR = dot(-rayDirVC, normalize(2.0 * ndotL * -normal.xyz + vertLightDirection));\n if (vdotR > 0.0)\n {\n specular += pow(vdotR, vSpecularPower) * lightColor[i];\n }\n }\n #ifdef localAmbientOcclusionOn\n ambient += computeLAO(posIS, tColor.a, vertLightDirection, normal);\n #endif \n }\n }\n #ifdef localAmbientOcclusionOn\n return tColor.rgb * (diffuse * vDiffuse + vAmbient * ambient) + specular*vSpecular;\n #else \n return tColor.rgb * (diffuse * vDiffuse + vAmbient) + specular*vSpecular;\n #endif\n }\n #endif \n #endif\n#endif\n\n//=======================================================================\n// Given a texture value compute the color and opacity\n//\nvec4 getColorForValue(vec4 tValue, vec3 posIS, vec3 tstep)\n{\n#ifdef vtkImageLabelOutlineOn\n vec3 centerPosIS = fragCoordToIndexSpace(gl_FragCoord); // pos in texture space\n vec4 centerValue = getTextureValue(centerPosIS);\n bool pixelOnBorder = false;\n vec4 tColor = texture2D(ctexture, vec2(centerValue.r * cscale0 + cshift0, 0.5));\n\n // Get alpha of segment from opacity function.\n tColor.a = texture2D(otexture, vec2(centerValue.r * oscale0 + oshift0, 0.5)).r;\n\n // Only perform outline check on fragments rendering voxels that aren't invisible.\n // Saves a bunch of needless checks on the background.\n // TODO define epsilon when building shader?\n if (float(tColor.a) > 0.01) {\n for (int i = -outlineThickness; i <= outlineThickness; i++) {\n for (int j = -outlineThickness; j <= outlineThickness; j++) {\n if (i == 0 || j == 0) {\n continue;\n }\n\n vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(i),\n gl_FragCoord.y + float(j),\n gl_FragCoord.z, gl_FragCoord.w);\n\n vec3 neighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);\n vec4 value = getTextureValue(neighborPosIS);\n\n // If any of my neighbours are not the same value as I\n // am, this means I am on the border of the segment.\n // We can break the loops\n if (any(notEqual(value, centerValue))) {\n pixelOnBorder = true;\n break;\n }\n }\n\n if (pixelOnBorder == true) {\n break;\n }\n }\n\n // If I am on the border, I am displayed at full opacity\n if (pixelOnBorder == true) {\n tColor.a = 1.0;\n }\n }\n\n#else\n // compute the normal and gradient magnitude if needed\n // We compute it as a vec4 if possible otherwise a mat4\n //\n vec4 goFactor = vec4(1.0,1.0,1.0,1.0);\n\n // compute the normal vectors as needed\n #if (vtkLightComplexity > 0) || defined(vtkGradientOpacityOn)\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);\n #if !defined(vtkComponent0Proportional)\n vec4 normal0 = normalMat[0];\n #endif\n #if !defined(vtkComponent1Proportional)\n vec4 normal1 = normalMat[1];\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n vec4 normal2 = normalMat[2];\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n vec4 normal3 = normalMat[3];\n #endif\n #endif\n #endif\n #else\n vec4 normalLight;\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n mat3 scalarInterp; \n vec3 secondaryGradientMag; \n vec4 normal0 = computeNormalForDensity(posIS, tValue.a, tstep, scalarInterp, secondaryGradientMag); \n normalLight = computeDensityNormal(tValue.a, normal0.w, scalarInterp,secondaryGradientMag); \n if (length(normalLight) == 0.0){ \n normalLight = normal0; \n } \n #else\n vec3 scalarInterp; \n vec4 normal0 = computeNormalForDensity(posIS, tValue.a, tstep, scalarInterp); \n if (length(normal0)>0.0){ \n normalLight = computeDensityNormal(tValue.a,scalarInterp); \n if (length(normalLight)==0.0){ \n normalLight = normal0; \n } \n } \n #endif\n #else \n vec4 normal0 = computeNormal(posIS, tValue.a, tstep); \n normalLight = normal0; \n #endif\n #endif\n #endif\n\n // compute gradient opacity factors as needed\n #if defined(vtkGradientOpacityOn)\n #if !defined(vtkComponent0Proportional)\n goFactor.x =\n computeGradientOpacityFactor(normal0.a, goscale0, goshift0, gomin0, gomax0);\n #endif\n #if defined(vtkIndependentComponentsOn) && (vtkNumComponents > 1)\n #if !defined(vtkComponent1Proportional)\n goFactor.y =\n computeGradientOpacityFactor(normal1.a, goscale1, goshift1, gomin1, gomax1);\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n goFactor.z =\n computeGradientOpacityFactor(normal2.a, goscale2, goshift2, gomin2, gomax2);\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n goFactor.w =\n computeGradientOpacityFactor(normal3.a, goscale3, goshift3, gomin3, gomax3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n // single component is always independent\n #if vtkNumComponents == 1\n vec4 tColor = texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, 0.5));\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n if (tColor.a < EPSILON){\n return vec4(0.0);\n } \n #endif\n\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n vec4 tColor = mix0*texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, height0));\n #if !defined(vtkComponent0Proportional)\n tColor.a = goFactor.x*mix0*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n tColor *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix0));\n #endif\n\n vec3 tColor1 = mix1*texture2D(ctexture, vec2(tValue.g * cscale1 + cshift1, height1)).rgb;\n #if !defined(vtkComponent1Proportional)\n tColor.a += goFactor.y*mix1*texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n tColor1 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix1));\n #endif\n\n #if vtkNumComponents >= 3\n vec3 tColor2 = mix2*texture2D(ctexture, vec2(tValue.b * cscale2 + cshift2, height2)).rgb;\n #if !defined(vtkComponent2Proportional)\n tColor.a += goFactor.z*mix2*texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n tColor2 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix2));\n #endif\n\n #if vtkNumComponents >= 4\n vec3 tColor3 = mix3*texture2D(ctexture, vec2(tValue.a * cscale3 + cshift3, height3)).rgb;\n #if !defined(vtkComponent3Proportional)\n tColor.a += goFactor.w*mix3*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n #else\n float pwfValue = texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n tColor3 *= pwfValue;\n tColor.a *= mix(pwfValue, 1.0, (1.0 - mix3));\n #endif\n #endif\n #endif\n #else // then not independent\n\n #if vtkNumComponents == 2\n float lum = tValue.r * cscale0 + cshift0;\n float alpha = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale1 + oshift1, 0.5)).r;\n vec4 tColor = vec4(lum, lum, lum, alpha);\n #endif\n #if vtkNumComponents == 3\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale0 + oshift0, 0.5)).r;\n #endif\n #if vtkNumComponents == 4\n vec4 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n tColor.a = goFactor.x*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, 0.5)).r;\n #endif\n #endif // dependent\n\n // apply lighting if requested as appropriate\n #if vtkLightComplexity > 0\n #if !defined(vtkComponent0Proportional) \n #if vtkNumComponents == 1\n \n #ifdef SurfaceShadowOn\n #if vtkLightComplexity < 3\n vec3 tColorS = applyLightingDirectional(posIS, tColor, normalLight);\n #else\n vec3 tColorS = applyLightingPositional(posIS, tColor, normalLight, IStoVC(posIS));\n #endif\n #endif\n\n #ifdef VolumeShadowOn\n vec3 tColorVS = applyShadowRay(tColor.rgb, posIS, rayDirVC);\n #ifdef SurfaceShadowOn\n float vol_coef = volumetricScatteringBlending * (1.0 - tColor.a / 2.0) * (1.0 - atan(normalLight.w) * INV4PI);\n tColor.rgb = (1.0-vol_coef) * tColorS + vol_coef * tColorVS;\n #else\n tColor.rgb = tColorVS;\n #endif\n #else\n tColor.rgb = tColorS;\n #endif\n \n #else\n applyLighting(tColor.rgb, normal0);\n #endif\n #endif\n\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n #if !defined(vtkComponent1Proportional)\n applyLighting(tColor1, normal1);\n #endif\n #if vtkNumComponents >= 3\n #if !defined(vtkComponent2Proportional)\n applyLighting(tColor2, normal2);\n #endif\n #if vtkNumComponents >= 4\n #if !defined(vtkComponent3Proportional)\n applyLighting(tColor3, normal3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n// perform final independent blend as needed\n#if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2\n tColor.rgb += tColor1;\n#if vtkNumComponents >= 3\n tColor.rgb += tColor2;\n#if vtkNumComponents >= 4\n tColor.rgb += tColor3;\n#endif\n#endif\n#endif\n\n#endif\n\n\n\n\n\n\n\nreturn tColor;\n}\n\nbool valueWithinScalarRange(vec4 val, vec4 min, vec4 max) {\n bool withinRange = false;\n #if vtkNumComponents == 1\n if (val.r >= min.r && val.r <= max.r) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents == 2\n if (val.r >= min.r && val.r <= max.r &&\n val.g >= min.g && val.g <= max.g) {\n withinRange = true;\n }\n #endif\n #if defined(vtkIndependentComponentsOn) && vtkNumComponents >= 3\n if (all(greaterThanEqual(val, ipScalarRangeMin)) &&\n all(lessThanEqual(val, ipScalarRangeMax))) {\n withinRange = true;\n }\n #endif\n return withinRange;\n}\n\n//=======================================================================\n// Apply the specified blend mode operation along the ray's path.\n//\nvoid applyBlend(vec3 posIS, vec3 endIS, vec3 tdims)\n{\n vec3 tstep = 1.0/tdims;\n\n // start slightly inside and apply some jitter\n vec3 delta = endIS - posIS;\n vec3 stepIS = normalize(delta)*sampleDistanceIS;\n float raySteps = length(delta)/sampleDistanceIS;\n\n // avoid 0.0 jitter\n float jitter = 0.01 + 0.99*texture2D(jtexture, gl_FragCoord.xy/32.0).r;\n float stepsTraveled = jitter;\n\n // local vars for the loop\n vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n vec4 tValue;\n vec4 tColor;\n\n // if we have less than one step then pick the middle point\n // as our value\n // if (raySteps <= 1.0)\n // {\n // posIS = (posIS + endIS)*0.5;\n // }\n\n // Perform initial step at the volume boundary\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n #if vtkBlendMode == 0 // COMPOSITE_BLEND\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n // handle very thin volumes\n if (raySteps <= 1.0)\n {\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps);\n gl_FragData[0] = tColor;\n return;\n }\n\n tColor.a = 1.0 - pow(1.0 - tColor.a, jitter);\n color = vec4(tColor.rgb*tColor.a, tColor.a);\n posIS += (jitter*stepIS);\n\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n\n float mix = (1.0 - color.a);\n\n // this line should not be needed but nvidia seems to not handle\n // the break correctly on windows/chrome 58 angle\n //mix = mix * sign(max(raySteps - stepsTraveled - 1.0, 0.0));\n\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n stepsTraveled++;\n posIS += stepIS;\n if (color.a > 0.99) { color.a = 1.0; break; }\n }\n\n if (color.a < 0.99 && (raySteps - stepsTraveled) > 0.0)\n {\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // now map through opacity and color\n tColor = getColorForValue(tValue, posIS, tstep);\n tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps - stepsTraveled);\n\n float mix = (1.0 - color.a);\n color = color + vec4(tColor.rgb*tColor.a, tColor.a)*mix;\n }\n\n gl_FragData[0] = vec4(color.rgb/color.a, color.a);\n #endif\n #if vtkBlendMode == 1 || vtkBlendMode == 2\n // MAXIMUM_INTENSITY_BLEND || MINIMUM_INTENSITY_BLEND\n // Find maximum/minimum intensity along the ray.\n\n // Define the operation we will use (min or max)\n #if vtkBlendMode == 1\n #define OP max\n #else\n #define OP min\n #endif\n\n // If the clipping range is shorter than the sample distance\n // we can skip the sampling loop along the ray.\n if (raySteps <= 1.0)\n {\n gl_FragData[0] = getColorForValue(tValue, posIS, tstep);\n return;\n }\n\n vec4 value = tValue;\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // Update the maximum value if necessary\n value = OP(tValue, value);\n\n // Otherwise, continue along the ray\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n tValue = getTextureValue(posIS);\n value = OP(tValue, value);\n\n // Now map through opacity and color\n gl_FragData[0] = getColorForValue(value, posIS, tstep);\n #endif\n #if vtkBlendMode == 3 || vtkBlendMode == 4 //AVERAGE_INTENSITY_BLEND || ADDITIVE_BLEND\n vec4 sum = vec4(0.);\n\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n }\n\n if (raySteps <= 1.0) {\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n return;\n }\n\n posIS += (jitter*stepIS);\n\n // Sample along the ray until MaximumSamplesValue,\n // ending slightly inside the total distance\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the AverageIPScalarRange to disregard scalar values, not in the range of interest, from the average computation.\n // Notes:\n // - We are comparing all values in the texture to see if any of them\n // are outside of the scalar range. In the future we might want to allow\n // scalar ranges for each component.\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n // Sum the values across each step in the path\n sum += tValue;\n }\n stepsTraveled++;\n posIS += stepIS;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posIS = endIS;\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n\n // One can control the scalar range by setting the IPScalarRange to disregard scalar values, not in the range of interest, from the average computation\n if (valueWithinScalarRange(tValue, ipScalarRangeMin, ipScalarRangeMax)) {\n sum += tValue;\n\n stepsTraveled++;\n }\n\n #if vtkBlendMode == 3 // Average\n sum /= vec4(stepsTraveled, stepsTraveled, stepsTraveled, 1.0);\n #endif\n\n gl_FragData[0] = getColorForValue(sum, posIS, tstep);\n #endif\n #if vtkBlendMode == 5 // RADON\n float normalizedRayIntensity = 1.0;\n\n // handle very thin volumes\n if (raySteps <= 1.0)\n {\n tValue = getTextureValue(posIS);\n normalizedRayIntensity = normalizedRayIntensity - sampleDistance*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n gl_FragData[0] = texture2D(ctexture, vec2(normalizedRayIntensity * cscale0 + cshift0, 0.5));\n return;\n }\n\n posIS += (jitter*stepIS);\n\n for (int i = 0; i < //VTK::MaximumSamplesValue ; ++i)\n {\n if (stepsTraveled + 1.0 >= raySteps) { break; }\n\n // compute the scalar value\n tValue = getTextureValue(posIS);\n\n // Convert scalar value to normalizedRayIntensity coefficient and accumulate normalizedRayIntensity\n normalizedRayIntensity = normalizedRayIntensity - sampleDistance*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n\n posIS += stepIS;\n stepsTraveled++;\n }\n\n // map normalizedRayIntensity to color\n gl_FragData[0] = texture2D(ctexture, vec2(normalizedRayIntensity * cscale0 + cshift0, 0.5));\n\n #endif\n}\n\n//=======================================================================\n// Compute a new start and end point for a given ray based\n// on the provided bounded clipping plane (aka a rectangle)\nvoid getRayPointIntersectionBounds(\n vec3 rayPos, vec3 rayDir,\n vec3 planeDir, float planeDist,\n inout vec2 tbounds, vec3 vPlaneX, vec3 vPlaneY,\n float vSize1, float vSize2)\n{\n float result = dot(rayDir, planeDir);\n if (abs(result) < 1e-6)\n {\n return;\n }\n result = -1.0 * (dot(rayPos, planeDir) + planeDist) / result;\n vec3 xposVC = rayPos + rayDir*result;\n vec3 vxpos = xposVC - vOriginVC;\n vec2 vpos = vec2(\n dot(vxpos, vPlaneX),\n dot(vxpos, vPlaneY));\n\n // on some apple nvidia systems this does not work\n // if (vpos.x < 0.0 || vpos.x > vSize1 ||\n // vpos.y < 0.0 || vpos.y > vSize2)\n // even just\n // if (vpos.x < 0.0 || vpos.y < 0.0)\n // fails\n // so instead we compute a value that represents in and out\n //and then compute the return using this value\n float xcheck = max(0.0, vpos.x * (vpos.x - vSize1)); // 0 means in bounds\n float check = sign(max(xcheck, vpos.y * (vpos.y - vSize2))); // 0 means in bounds, 1 = out\n\n tbounds = mix(\n vec2(min(tbounds.x, result), max(tbounds.y, result)), // in value\n tbounds, // out value\n check); // 0 in 1 out\n}\n\n//=======================================================================\n// given a\n// - ray direction (rayDir)\n// - starting point (vertexVCVSOutput)\n// - bounding planes of the volume\n// - optionally depth buffer values\n// - far clipping plane\n// compute the start/end distances of the ray we need to cast\nvec2 computeRayDistances(vec3 rayDir, vec3 tdims)\n{\n vec2 dists = vec2(100.0*camFar, -1.0);\n\n vec3 vSize = vSpacing*tdims;\n\n // all this is in View Coordinates\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal0, vPlaneDistance0, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal1, vPlaneDistance1, dists, vPlaneNormal2, vPlaneNormal4,\n vSize.y, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal2, vPlaneDistance2, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal3, vPlaneDistance3, dists, vPlaneNormal0, vPlaneNormal4,\n vSize.x, vSize.z);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal4, vPlaneDistance4, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n getRayPointIntersectionBounds(vertexVCVSOutput, rayDir,\n vPlaneNormal5, vPlaneDistance5, dists, vPlaneNormal0, vPlaneNormal2,\n vSize.x, vSize.y);\n\n //VTK::ClipPlane::Impl\n\n // do not go behind front clipping plane\n dists.x = max(0.0,dists.x);\n\n // do not go PAST far clipping plane\n float farDist = -camThick/rayDir.z;\n dists.y = min(farDist,dists.y);\n\n // Do not go past the zbuffer value if set\n // This is used for intermixing opaque geometry\n //VTK::ZBuffer::Impl\n\n return dists;\n}\n\n//=======================================================================\n// Compute the index space starting position (pos) and end\n// position\n//\nvoid computeIndexSpaceValues(out vec3 pos, out vec3 endPos, vec3 rayDir, vec2 dists)\n{\n // compute starting and ending values in volume space\n pos = vertexVCVSOutput + dists.x*rayDir;\n pos = pos - vOriginVC;\n // convert to volume basis and origin\n pos = vec3(\n dot(pos, vPlaneNormal0),\n dot(pos, vPlaneNormal2),\n dot(pos, vPlaneNormal4));\n\n endPos = vertexVCVSOutput + dists.y*rayDir;\n endPos = endPos - vOriginVC;\n endPos = vec3(\n dot(endPos, vPlaneNormal0),\n dot(endPos, vPlaneNormal2),\n dot(endPos, vPlaneNormal4));\n\n float delta = length(endPos - pos);\n\n pos *= vVCToIJK;\n endPos *= vVCToIJK;\n\n float delta2 = length(endPos - pos);\n sampleDistanceIS = sampleDistance*delta2/delta;\n #ifdef VolumeShadowOn\n sampleDistanceISVS = sampleDistanceIS * volumeShadowSamplingDistFactor;\n #endif\n}\n\nvoid main()\n{\n\n if (cameraParallel == 1)\n {\n // Camera is parallel, so the rayDir is just the direction of the camera.\n rayDirVC = vec3(0.0, 0.0, -1.0);\n } else {\n // camera is at 0,0,0 so rayDir for perspective is just the vc coord\n rayDirVC = normalize(vertexVCVSOutput);\n }\n\n vec3 tdims = vec3(volumeDimensions);\n\n // compute the start and end points for the ray\n vec2 rayStartEndDistancesVC = computeRayDistances(rayDirVC, tdims);\n\n // do we need to composite? aka does the ray have any length\n // If not, bail out early\n if (rayStartEndDistancesVC.y <= rayStartEndDistancesVC.x)\n {\n discard;\n }\n\n // IS = Index Space\n vec3 posIS;\n vec3 endIS;\n computeIndexSpaceValues(posIS, endIS, rayDirVC, rayStartEndDistancesVC);\n\n // Perform the blending operation along the ray\n applyBlend(posIS, endIS, tdims);\n}\n";
2
2
 
3
3
  export { vtkVolumeFS as v };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "25.7.2",
3
+ "version": "25.8.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",
package/types.d.ts CHANGED
@@ -22,7 +22,6 @@ declare type double = number;
22
22
  declare type int = number;
23
23
 
24
24
  declare type TypedArray =
25
- | number[]
26
25
  | Uint32Array
27
26
  | Uint16Array
28
27
  | Uint8Array