zincjs 1.16.2 → 1.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/zinc.frontend.js +1 -1
- package/build/zinc.js +50 -49
- package/build/zinc.js.map +1 -1
- package/package.json +1 -1
- package/src/primitives/lines2.js +4 -3
- package/src/primitives/pointset.js +8 -2
- package/src/primitives/textureVolume.js +317 -0
- package/src/primitives/zincObject.js +53 -50
- package/src/shaders/volumeRender_old.js +287 -0
|
@@ -11,7 +11,7 @@ const getUniqueId = function () {
|
|
|
11
11
|
/**
|
|
12
12
|
* Provides the base object for other primitive types.
|
|
13
13
|
* This class contains multiple base methods.
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* @class
|
|
16
16
|
* @author Alan Wu
|
|
17
17
|
* @return {ZincObject}
|
|
@@ -33,7 +33,7 @@ const ZincObject = function() {
|
|
|
33
33
|
this.mixer = undefined;
|
|
34
34
|
this.animationGroup = undefined;
|
|
35
35
|
/**
|
|
36
|
-
* Total duration of the animation, this value interacts with the
|
|
36
|
+
* Total duration of the animation, this value interacts with the
|
|
37
37
|
* {@link Renderer#playRate} to produce the actual duration of the
|
|
38
38
|
* animation. Actual time in second = duration / playRate.
|
|
39
39
|
*/
|
|
@@ -69,7 +69,7 @@ const ZincObject = function() {
|
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Set the duration of the animation of this object.
|
|
72
|
-
*
|
|
72
|
+
*
|
|
73
73
|
* @param {Number} durationIn - Duration of the animation.
|
|
74
74
|
*/
|
|
75
75
|
ZincObject.prototype.setDuration = function(durationIn) {
|
|
@@ -81,7 +81,7 @@ ZincObject.prototype.setDuration = function(durationIn) {
|
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Get the duration of the animation of this object.
|
|
84
|
-
*
|
|
84
|
+
*
|
|
85
85
|
* @return {Number}
|
|
86
86
|
*/
|
|
87
87
|
ZincObject.prototype.getDuration = function() {
|
|
@@ -99,7 +99,7 @@ ZincObject.prototype.setRegion = function(region) {
|
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Get the region this object belongs to.
|
|
102
|
-
*
|
|
102
|
+
*
|
|
103
103
|
* @return {Region}
|
|
104
104
|
*/
|
|
105
105
|
ZincObject.prototype.getRegion = function() {
|
|
@@ -107,8 +107,8 @@ ZincObject.prototype.getRegion = function() {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
|
-
* Get the threejs object3D.
|
|
111
|
-
*
|
|
110
|
+
* Get the threejs object3D.
|
|
111
|
+
*
|
|
112
112
|
* @return {Object}
|
|
113
113
|
*/
|
|
114
114
|
ZincObject.prototype.getMorph = function() {
|
|
@@ -117,8 +117,8 @@ ZincObject.prototype.getRegion = function() {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
|
-
* Get the threejs object3D.
|
|
121
|
-
*
|
|
120
|
+
* Get the threejs object3D.
|
|
121
|
+
*
|
|
122
122
|
* @return {Object}
|
|
123
123
|
*/
|
|
124
124
|
ZincObject.prototype.getGroup = function() {
|
|
@@ -126,7 +126,7 @@ ZincObject.prototype.getRegion = function() {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/**
|
|
129
|
-
* Set the internal threejs object3D.
|
|
129
|
+
* Set the internal threejs object3D.
|
|
130
130
|
*/
|
|
131
131
|
ZincObject.prototype.setMorph = function(mesh) {
|
|
132
132
|
this.morph = mesh;
|
|
@@ -147,7 +147,7 @@ ZincObject.prototype.checkTransparentMesh = function() {
|
|
|
147
147
|
|
|
148
148
|
/**
|
|
149
149
|
* Set the mesh function for zincObject.
|
|
150
|
-
*
|
|
150
|
+
*
|
|
151
151
|
* @param {THREE.Mesh} mesh - Mesh to be set for this zinc object.
|
|
152
152
|
* @param {Boolean} localTimeEnabled - A flag to indicate either the mesh is
|
|
153
153
|
* time dependent.
|
|
@@ -201,7 +201,7 @@ ZincObject.prototype.setMesh = function(mesh, localTimeEnabled, localMorphColour
|
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
203
|
* Set isPickable for this ZincObject.
|
|
204
|
-
*
|
|
204
|
+
*
|
|
205
205
|
* @param {String} isPickable - Boolean to enable object pick.
|
|
206
206
|
*/
|
|
207
207
|
ZincObject.prototype.setIsPickable = function(isPickable) {
|
|
@@ -213,7 +213,7 @@ ZincObject.prototype.setIsPickable = function(isPickable) {
|
|
|
213
213
|
|
|
214
214
|
/**
|
|
215
215
|
* Set the anatomicalId for this ZincObject.
|
|
216
|
-
*
|
|
216
|
+
*
|
|
217
217
|
* @param {String} anatomicalId - Id to be set.
|
|
218
218
|
*/
|
|
219
219
|
ZincObject.prototype.setAnatomicalId = function(anatomicalId) {
|
|
@@ -222,7 +222,7 @@ ZincObject.prototype.setAnatomicalId = function(anatomicalId) {
|
|
|
222
222
|
|
|
223
223
|
/**
|
|
224
224
|
* Set the name for this ZincObject.
|
|
225
|
-
*
|
|
225
|
+
*
|
|
226
226
|
* @param {String} groupNameIn - Name to be set.
|
|
227
227
|
*/
|
|
228
228
|
ZincObject.prototype.setName = function(groupNameIn) {
|
|
@@ -231,9 +231,9 @@ ZincObject.prototype.setName = function(groupNameIn) {
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
|
-
* Get the local time of this geometry, it returns a value between
|
|
234
|
+
* Get the local time of this geometry, it returns a value between
|
|
235
235
|
* 0 and the duration.
|
|
236
|
-
*
|
|
236
|
+
*
|
|
237
237
|
* @return {Number}
|
|
238
238
|
*/
|
|
239
239
|
ZincObject.prototype.getCurrentTime = function() {
|
|
@@ -247,7 +247,7 @@ ZincObject.prototype.getCurrentTime = function() {
|
|
|
247
247
|
|
|
248
248
|
/**
|
|
249
249
|
* Set the local time of this geometry.
|
|
250
|
-
*
|
|
250
|
+
*
|
|
251
251
|
* @param {Number} time - Can be any value between 0 to duration.
|
|
252
252
|
*/
|
|
253
253
|
ZincObject.prototype.setMorphTime = function(time) {
|
|
@@ -264,7 +264,7 @@ ZincObject.prototype.setMorphTime = function(time) {
|
|
|
264
264
|
this.mixer.update( 0.0 );
|
|
265
265
|
}
|
|
266
266
|
} else {
|
|
267
|
-
let newTime = time;
|
|
267
|
+
let newTime = time;
|
|
268
268
|
if (time > this.duration)
|
|
269
269
|
newTime = this.duration;
|
|
270
270
|
else if (0 > time)
|
|
@@ -286,7 +286,7 @@ ZincObject.prototype.setMorphTime = function(time) {
|
|
|
286
286
|
|
|
287
287
|
/**
|
|
288
288
|
* Check if the geometry is time varying.
|
|
289
|
-
*
|
|
289
|
+
*
|
|
290
290
|
* @return {Boolean}
|
|
291
291
|
*/
|
|
292
292
|
ZincObject.prototype.isTimeVarying = function() {
|
|
@@ -297,7 +297,7 @@ ZincObject.prototype.isTimeVarying = function() {
|
|
|
297
297
|
|
|
298
298
|
/**
|
|
299
299
|
* Get the visibility of this Geometry.
|
|
300
|
-
*
|
|
300
|
+
*
|
|
301
301
|
*/
|
|
302
302
|
ZincObject.prototype.getVisibility = function() {
|
|
303
303
|
return this.visible;
|
|
@@ -305,8 +305,8 @@ ZincObject.prototype.getVisibility = function() {
|
|
|
305
305
|
|
|
306
306
|
/**
|
|
307
307
|
* Set the visibility of this Geometry.
|
|
308
|
-
*
|
|
309
|
-
* @param {Boolean} visible - a boolean flag indicate the visibility to be set
|
|
308
|
+
*
|
|
309
|
+
* @param {Boolean} visible - a boolean flag indicate the visibility to be set
|
|
310
310
|
*/
|
|
311
311
|
ZincObject.prototype.setVisibility = function(visible) {
|
|
312
312
|
if (visible !== this.visible) {
|
|
@@ -319,8 +319,8 @@ ZincObject.prototype.setVisibility = function(visible) {
|
|
|
319
319
|
/**
|
|
320
320
|
* Set the opacity of this Geometry. This function will also set the isTransparent
|
|
321
321
|
* flag according to the provided alpha value.
|
|
322
|
-
*
|
|
323
|
-
* @param {Number} alpah - Alpha value to set for this geometry,
|
|
322
|
+
*
|
|
323
|
+
* @param {Number} alpah - Alpha value to set for this geometry,
|
|
324
324
|
* can be any value between from 0 to 1.0.
|
|
325
325
|
*/
|
|
326
326
|
ZincObject.prototype.setAlpha = function(alpha) {
|
|
@@ -337,7 +337,7 @@ ZincObject.prototype.setAlpha = function(alpha) {
|
|
|
337
337
|
* The rendering will be culled if it is outside of the frustrum
|
|
338
338
|
* when this flag is set to true, it should be set to false if
|
|
339
339
|
* morphing is enabled.
|
|
340
|
-
*
|
|
340
|
+
*
|
|
341
341
|
* @param {Boolean} flag - Set frustrum culling on/off based on this flag.
|
|
342
342
|
*/
|
|
343
343
|
ZincObject.prototype.setFrustumCulled = function(flag) {
|
|
@@ -348,7 +348,7 @@ ZincObject.prototype.setFrustumCulled = function(flag) {
|
|
|
348
348
|
/**
|
|
349
349
|
* Set rather a zinc object should be displayed using per vertex colour or
|
|
350
350
|
* not.
|
|
351
|
-
*
|
|
351
|
+
*
|
|
352
352
|
* @param {Boolean} vertexColors - Set display with vertex color on/off.
|
|
353
353
|
*/
|
|
354
354
|
ZincObject.prototype.setVertexColors = function(vertexColors) {
|
|
@@ -359,7 +359,7 @@ ZincObject.prototype.setVertexColors = function(vertexColors) {
|
|
|
359
359
|
|
|
360
360
|
/**
|
|
361
361
|
* Get the colour of the mesh.
|
|
362
|
-
*
|
|
362
|
+
*
|
|
363
363
|
* @return {THREE.Color}
|
|
364
364
|
*/
|
|
365
365
|
ZincObject.prototype.getColour = function() {
|
|
@@ -367,10 +367,10 @@ ZincObject.prototype.getColour = function() {
|
|
|
367
367
|
return this._lod._material.color;
|
|
368
368
|
return undefined;
|
|
369
369
|
}
|
|
370
|
-
|
|
370
|
+
|
|
371
371
|
/**
|
|
372
372
|
* Set the colour of the mesh.
|
|
373
|
-
*
|
|
373
|
+
*
|
|
374
374
|
* @param {THREE.Color} colour - Colour to be set for this geometry.
|
|
375
375
|
*/
|
|
376
376
|
ZincObject.prototype.setColour = function(colour) {
|
|
@@ -379,7 +379,7 @@ ZincObject.prototype.setColour = function(colour) {
|
|
|
379
379
|
|
|
380
380
|
/**
|
|
381
381
|
* Set the colour of the mesh.
|
|
382
|
-
*
|
|
382
|
+
*
|
|
383
383
|
* @param {THREE.Color} colour - Colour to be set for this geometry.
|
|
384
384
|
*/
|
|
385
385
|
ZincObject.prototype.setGreyScale = function(flag) {
|
|
@@ -406,7 +406,7 @@ ZincObject.prototype.setGreyScale = function(flag) {
|
|
|
406
406
|
|
|
407
407
|
/**
|
|
408
408
|
* Get the colour of the mesh in hex string form.
|
|
409
|
-
*
|
|
409
|
+
*
|
|
410
410
|
* @return {String}
|
|
411
411
|
*/
|
|
412
412
|
ZincObject.prototype.getColourHex = function() {
|
|
@@ -419,7 +419,7 @@ ZincObject.prototype.getColourHex = function() {
|
|
|
419
419
|
|
|
420
420
|
/**
|
|
421
421
|
* Set the colour of the mesh using hex in string form.
|
|
422
|
-
*
|
|
422
|
+
*
|
|
423
423
|
* @param {String} hex - The colour value in hex form.
|
|
424
424
|
*/
|
|
425
425
|
ZincObject.prototype.setColourHex = function(hex) {
|
|
@@ -431,7 +431,7 @@ ZincObject.prototype.setColourHex = function(hex) {
|
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
433
|
* Set the emissive rgb of the mesh using rgb.
|
|
434
|
-
*
|
|
434
|
+
*
|
|
435
435
|
* @param {String} colour - The colour value in rgb form.
|
|
436
436
|
*/
|
|
437
437
|
ZincObject.prototype.setEmissiveRGB = function(colour) {
|
|
@@ -446,7 +446,7 @@ ZincObject.prototype.setEmissiveRGB = function(colour) {
|
|
|
446
446
|
|
|
447
447
|
/**
|
|
448
448
|
* Set the material of the geometry.
|
|
449
|
-
*
|
|
449
|
+
*
|
|
450
450
|
* @param {THREE.Material} material - Material to be set for this geometry.
|
|
451
451
|
*/
|
|
452
452
|
ZincObject.prototype.setMaterial = function(material) {
|
|
@@ -455,17 +455,17 @@ ZincObject.prototype.setMaterial = function(material) {
|
|
|
455
455
|
|
|
456
456
|
/**
|
|
457
457
|
* Get the index of the closest vertex to centroid.
|
|
458
|
-
*
|
|
458
|
+
*
|
|
459
459
|
* @return {Number} - integer index in the array
|
|
460
460
|
*/
|
|
461
461
|
ZincObject.prototype.getClosestVertexIndex = function() {
|
|
462
462
|
let closestIndex = -1;
|
|
463
463
|
const morph = this.getMorph();
|
|
464
|
-
if (morph && morph.
|
|
464
|
+
if (morph && morph.geometry) {
|
|
465
465
|
let position = morph.geometry.attributes.position;
|
|
466
|
-
this._b1.setFromBufferAttribute(position);
|
|
467
|
-
this._b1.getCenter(this._v1);
|
|
468
466
|
if (position) {
|
|
467
|
+
this._b1.setFromBufferAttribute(position);
|
|
468
|
+
this._b1.getCenter(this._v1);
|
|
469
469
|
let distance = -1;
|
|
470
470
|
let currentDistance = 0;
|
|
471
471
|
for (let i = 0; i < position.count; i++) {
|
|
@@ -485,7 +485,7 @@ ZincObject.prototype.getClosestVertexIndex = function() {
|
|
|
485
485
|
|
|
486
486
|
/**
|
|
487
487
|
* Get the closest vertex to centroid.
|
|
488
|
-
*
|
|
488
|
+
*
|
|
489
489
|
* @return {THREE.Vector3}
|
|
490
490
|
*/
|
|
491
491
|
ZincObject.prototype.getClosestVertex = function(applyMatrixWorld) {
|
|
@@ -516,14 +516,15 @@ ZincObject.prototype.getClosestVertex = function(applyMatrixWorld) {
|
|
|
516
516
|
return applyMatrixWorld ? position.applyMatrix4(morph.matrixWorld) : position;
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
|
+
//Matrix world has already been applied
|
|
519
520
|
this.getBoundingBox();
|
|
520
521
|
position.copy(this.center);
|
|
521
|
-
return
|
|
522
|
+
return position;
|
|
522
523
|
}
|
|
523
524
|
|
|
524
525
|
/**
|
|
525
526
|
* Get the bounding box of this geometry.
|
|
526
|
-
*
|
|
527
|
+
*
|
|
527
528
|
* @return {THREE.Box3}.
|
|
528
529
|
*/
|
|
529
530
|
ZincObject.prototype.getBoundingBox = function() {
|
|
@@ -558,13 +559,13 @@ ZincObject.prototype.dispose = function() {
|
|
|
558
559
|
}
|
|
559
560
|
|
|
560
561
|
/**
|
|
561
|
-
* Check if marker is enabled based on the objects settings with
|
|
562
|
+
* Check if marker is enabled based on the objects settings with
|
|
562
563
|
* the provided scene options.
|
|
563
|
-
*
|
|
564
|
-
* @return {Boolean}
|
|
564
|
+
*
|
|
565
|
+
* @return {Boolean}
|
|
565
566
|
*/
|
|
566
567
|
ZincObject.prototype.markerIsRequired = function(options) {
|
|
567
|
-
if (this.visible &&
|
|
568
|
+
if (this.visible &&
|
|
568
569
|
(this.markerMode === "on" || (options && options.displayMarkers &&
|
|
569
570
|
(this.markerMode === "inherited")))) {
|
|
570
571
|
return true;
|
|
@@ -573,7 +574,7 @@ ZincObject.prototype.markerIsRequired = function(options) {
|
|
|
573
574
|
}
|
|
574
575
|
|
|
575
576
|
/**
|
|
576
|
-
* Update the marker's position and size based on current viewport.
|
|
577
|
+
* Update the marker's position and size based on current viewport.
|
|
577
578
|
*/
|
|
578
579
|
ZincObject.prototype.updateMarker = function(playAnimation, options) {
|
|
579
580
|
if ((playAnimation == false) &&
|
|
@@ -594,7 +595,7 @@ ZincObject.prototype.updateMarker = function(playAnimation, options) {
|
|
|
594
595
|
}
|
|
595
596
|
if (!this.marker.isEnabled()) {
|
|
596
597
|
if (options.markersList &&
|
|
597
|
-
(!(this.marker.uuid in options.markersList))) {
|
|
598
|
+
(!(this.marker.uuid in options.markersList))) {
|
|
598
599
|
ndcToBeUpdated = true;
|
|
599
600
|
options.markersList[this.marker.uuid] = this.marker;
|
|
600
601
|
}
|
|
@@ -647,7 +648,7 @@ ZincObject.prototype.setRenderOrder = function(renderOrder) {
|
|
|
647
648
|
|
|
648
649
|
/**
|
|
649
650
|
* Get the windows coordinates.
|
|
650
|
-
*
|
|
651
|
+
*
|
|
651
652
|
* @return {Object} - position and rather the closest vertex is on screen.
|
|
652
653
|
*/
|
|
653
654
|
ZincObject.prototype.getClosestVertexDOMElementCoords = function(scene) {
|
|
@@ -675,8 +676,8 @@ ZincObject.prototype.getClosestVertexDOMElementCoords = function(scene) {
|
|
|
675
676
|
* "off" - marker is disabled regardless of settings of scene
|
|
676
677
|
* "inherited" - Marker settings on scene will determine the visibility
|
|
677
678
|
* of the marker.
|
|
678
|
-
*
|
|
679
|
-
* @return {Boolean}
|
|
679
|
+
*
|
|
680
|
+
* @return {Boolean}
|
|
680
681
|
*/
|
|
681
682
|
ZincObject.prototype.setMarkerMode = function(mode, options) {
|
|
682
683
|
if (mode !== this.markerMode) {
|
|
@@ -764,7 +765,9 @@ ZincObject.prototype.setPosition = function(x, y, z) {
|
|
|
764
765
|
const group = this.getGroup();
|
|
765
766
|
if (group) {
|
|
766
767
|
group.position.set(x, y, z);
|
|
768
|
+
group.matrixWorldNeedsUpdate = true;
|
|
767
769
|
group.updateMatrix();
|
|
770
|
+
group.updateWorldMatrix(true, true);
|
|
768
771
|
this.boundingBoxUpdateRequired = true;
|
|
769
772
|
}
|
|
770
773
|
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
const THREE = require('three');
|
|
2
|
+
|
|
3
|
+
const glslVersion = null;
|
|
4
|
+
|
|
5
|
+
const fs =
|
|
6
|
+
`
|
|
7
|
+
precision highp float;
|
|
8
|
+
precision highp sampler2DArray;
|
|
9
|
+
precision highp sampler2D;
|
|
10
|
+
|
|
11
|
+
uniform vec3 u_size;
|
|
12
|
+
uniform int u_renderstyle;
|
|
13
|
+
uniform float u_renderthreshold;
|
|
14
|
+
uniform vec2 u_clim;
|
|
15
|
+
|
|
16
|
+
uniform sampler2DArray u_data;
|
|
17
|
+
uniform sampler2D u_cmdata;
|
|
18
|
+
|
|
19
|
+
varying vec3 v_position;
|
|
20
|
+
varying vec4 v_nearpos;
|
|
21
|
+
varying vec4 v_farpos;
|
|
22
|
+
|
|
23
|
+
// The maximum distance through our rendering volume is sqrt(3).
|
|
24
|
+
const int MAX_STEPS = 887; // 887 for 512^3, 1774 for 1024^3
|
|
25
|
+
const int REFINEMENT_STEPS = 4;
|
|
26
|
+
const float relative_step_size = 1.0;
|
|
27
|
+
const vec4 ambient_color = vec4(0.2, 0.4, 0.2, 1.0);
|
|
28
|
+
const vec4 diffuse_color = vec4(0.8, 0.2, 0.2, 1.0);
|
|
29
|
+
const vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);
|
|
30
|
+
const float shininess = 40.0;
|
|
31
|
+
|
|
32
|
+
void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);
|
|
33
|
+
void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);
|
|
34
|
+
|
|
35
|
+
vec3 sample1(vec3 texcoords);
|
|
36
|
+
vec4 apply_colormap(float val);
|
|
37
|
+
vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray);
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
void main() {
|
|
41
|
+
// Normalize clipping plane info
|
|
42
|
+
vec3 farpos = v_farpos.xyz / v_farpos.w;
|
|
43
|
+
vec3 nearpos = v_nearpos.xyz / v_nearpos.w;
|
|
44
|
+
|
|
45
|
+
// Calculate unit vector pointing in the view direction through this fragment.
|
|
46
|
+
vec3 view_ray = normalize(nearpos.xyz - farpos.xyz);
|
|
47
|
+
|
|
48
|
+
// Compute the (negative) distance to the front surface or near clipping plane.
|
|
49
|
+
// v_position is the back face of the cuboid, so the initial distance calculated in the dot
|
|
50
|
+
// product below is the distance from near clip plane to the back of the cuboid
|
|
51
|
+
float distance = dot(nearpos - v_position, view_ray);
|
|
52
|
+
distance = max(distance, min((-0.5 - v_position.x) / view_ray.x,
|
|
53
|
+
(u_size.x - 0.5 - v_position.x) / view_ray.x));
|
|
54
|
+
distance = max(distance, min((-0.5 - v_position.y) / view_ray.y,
|
|
55
|
+
(u_size.y - 0.5 - v_position.y) / view_ray.y));
|
|
56
|
+
distance = max(distance, min((-0.5 - v_position.z) / view_ray.z,
|
|
57
|
+
(u_size.z - 0.5 - v_position.z) / view_ray.z));
|
|
58
|
+
|
|
59
|
+
// Now we have the starting position on the front surface
|
|
60
|
+
vec3 front = v_position + view_ray * distance;
|
|
61
|
+
|
|
62
|
+
// Decide how many steps to take
|
|
63
|
+
int nsteps = int(-distance / relative_step_size + 0.5);
|
|
64
|
+
if ( nsteps < 1 )
|
|
65
|
+
discard;
|
|
66
|
+
|
|
67
|
+
// Get starting location and step vector in texture coordinates
|
|
68
|
+
vec3 step = ((v_position - front) / u_size) / float(nsteps);
|
|
69
|
+
vec3 start_loc = front / u_size;
|
|
70
|
+
|
|
71
|
+
// For testing: show the number of steps. This helps to establish
|
|
72
|
+
// whether the rays are correctly oriented
|
|
73
|
+
//gl_FragColor = vec4(0.0, float(nsteps) / 1.0 / u_size.x, 1.0, 1.0);
|
|
74
|
+
//return;
|
|
75
|
+
|
|
76
|
+
if (u_renderstyle == 0)
|
|
77
|
+
cast_mip(start_loc, step, nsteps, view_ray);
|
|
78
|
+
else if (u_renderstyle == 1)
|
|
79
|
+
cast_iso(start_loc, step, nsteps, view_ray);
|
|
80
|
+
|
|
81
|
+
if (gl_FragColor.a < 0.05)
|
|
82
|
+
discard;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
vec3 sample1(vec3 texcoords) {
|
|
87
|
+
/* Sample float value from a 3D texture. Assumes intensity data. */
|
|
88
|
+
return texture(u_data, texcoords.xyz).rgb;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
vec4 apply_colormap(float val) {
|
|
93
|
+
val = (val - u_clim[0]) / (u_clim[1] - u_clim[0]);
|
|
94
|
+
return texture2D(u_cmdata, vec2(val, 0.5));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {
|
|
99
|
+
|
|
100
|
+
float max_val = -1e6;
|
|
101
|
+
int max_i = 100;
|
|
102
|
+
vec3 loc = start_loc;
|
|
103
|
+
|
|
104
|
+
// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
|
|
105
|
+
// non-constant expression. So we use a hard-coded max, and an additional condition
|
|
106
|
+
// inside the loop.
|
|
107
|
+
for (int iter=0; iter<MAX_STEPS; iter++) {
|
|
108
|
+
if (iter >= nsteps)
|
|
109
|
+
break;
|
|
110
|
+
// Sample from the 3D texture
|
|
111
|
+
vec3 val = sample1(loc);
|
|
112
|
+
float avg = (val.x + val.y + val.z) / 3.0;
|
|
113
|
+
// Apply MIP operation
|
|
114
|
+
if (val > max_val) {
|
|
115
|
+
max_val = val;
|
|
116
|
+
max_i = iter;
|
|
117
|
+
}
|
|
118
|
+
// Advance location deeper into the volume
|
|
119
|
+
loc += step;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Refine location, gives crispier images
|
|
123
|
+
vec3 iloc = start_loc + step * (float(max_i) - 0.5);
|
|
124
|
+
vec3 istep = step / float(REFINEMENT_STEPS);
|
|
125
|
+
for (int i=0; i<REFINEMENT_STEPS; i++) {
|
|
126
|
+
max_val = max(max_val, sample1(iloc));
|
|
127
|
+
iloc += istep;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Resolve final color
|
|
131
|
+
gl_FragColor = apply_colormap(max_val);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {
|
|
136
|
+
|
|
137
|
+
gl_FragColor = vec4(0.0); // init transparent
|
|
138
|
+
vec4 color3 = vec4(0.0); // final color
|
|
139
|
+
vec3 dstep = 1.5 / u_size; // step to sample derivative
|
|
140
|
+
vec3 loc = start_loc;
|
|
141
|
+
|
|
142
|
+
float low_threshold = u_renderthreshold - 0.02 * (u_clim[1] - u_clim[0]);
|
|
143
|
+
|
|
144
|
+
// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
|
|
145
|
+
// non-constant expression. So we use a hard-coded max, and an additional condition
|
|
146
|
+
// inside the loop.
|
|
147
|
+
for (int iter=0; iter<MAX_STEPS; iter++) {
|
|
148
|
+
if (iter >= nsteps)
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
// Sample from the 3D texture
|
|
152
|
+
float val = sample1(loc);
|
|
153
|
+
|
|
154
|
+
if (val > low_threshold) {
|
|
155
|
+
// Take the last interval in smaller steps
|
|
156
|
+
vec3 iloc = loc - 0.5 * step;
|
|
157
|
+
vec3 istep = step / float(REFINEMENT_STEPS);
|
|
158
|
+
for (int i=0; i<REFINEMENT_STEPS; i++) {
|
|
159
|
+
val = sample1(iloc);
|
|
160
|
+
if (val > u_renderthreshold) {
|
|
161
|
+
gl_FragColor = add_lighting(val, iloc, dstep, view_ray);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
iloc += istep;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Advance location deeper into the volume
|
|
169
|
+
loc += step;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray)
|
|
175
|
+
{
|
|
176
|
+
// Calculate color by incorporating lighting
|
|
177
|
+
|
|
178
|
+
// View direction
|
|
179
|
+
vec3 V = normalize(view_ray);
|
|
180
|
+
|
|
181
|
+
// calculate normal vector from gradient
|
|
182
|
+
vec3 N;
|
|
183
|
+
float val1, val2;
|
|
184
|
+
val1 = sample1(loc + vec3(-step[0], 0.0, 0.0));
|
|
185
|
+
val2 = sample1(loc + vec3(+step[0], 0.0, 0.0));
|
|
186
|
+
N[0] = val1 - val2;
|
|
187
|
+
val = max(max(val1, val2), val);
|
|
188
|
+
val1 = sample1(loc + vec3(0.0, -step[1], 0.0));
|
|
189
|
+
val2 = sample1(loc + vec3(0.0, +step[1], 0.0));
|
|
190
|
+
N[1] = val1 - val2;
|
|
191
|
+
val = max(max(val1, val2), val);
|
|
192
|
+
val1 = sample1(loc + vec3(0.0, 0.0, -step[2]));
|
|
193
|
+
val2 = sample1(loc + vec3(0.0, 0.0, +step[2]));
|
|
194
|
+
N[2] = val1 - val2;
|
|
195
|
+
val = max(max(val1, val2), val);
|
|
196
|
+
|
|
197
|
+
float gm = length(N); // gradient magnitude
|
|
198
|
+
N = normalize(N);
|
|
199
|
+
|
|
200
|
+
// Flip normal so it points towards viewer
|
|
201
|
+
float Nselect = float(dot(N, V) > 0.0);
|
|
202
|
+
N = (2.0 * Nselect - 1.0) * N; // == Nselect * N - (1.0-Nselect)*N;
|
|
203
|
+
|
|
204
|
+
// Init colors
|
|
205
|
+
vec4 ambient_color = vec4(0.0, 0.0, 0.0, 0.0);
|
|
206
|
+
vec4 diffuse_color = vec4(0.0, 0.0, 0.0, 0.0);
|
|
207
|
+
vec4 specular_color = vec4(0.0, 0.0, 0.0, 0.0);
|
|
208
|
+
|
|
209
|
+
// note: could allow multiple lights
|
|
210
|
+
for (int i=0; i<1; i++)
|
|
211
|
+
{
|
|
212
|
+
// Get light direction (make sure to prevent zero devision)
|
|
213
|
+
vec3 L = normalize(view_ray); //lightDirs[i];
|
|
214
|
+
float lightEnabled = float( length(L) > 0.0 );
|
|
215
|
+
L = normalize(L + (1.0 - lightEnabled));
|
|
216
|
+
|
|
217
|
+
// Calculate lighting properties
|
|
218
|
+
float lambertTerm = clamp(dot(N, L), 0.0, 1.0);
|
|
219
|
+
vec3 H = normalize(L+V); // Halfway vector
|
|
220
|
+
float specularTerm = pow(max(dot(H, N), 0.0), shininess);
|
|
221
|
+
|
|
222
|
+
// Calculate mask
|
|
223
|
+
float mask1 = lightEnabled;
|
|
224
|
+
|
|
225
|
+
// Calculate colors
|
|
226
|
+
ambient_color += mask1 * ambient_color; // * gl_LightSource[i].ambient;
|
|
227
|
+
diffuse_color += mask1 * lambertTerm;
|
|
228
|
+
specular_color += mask1 * specularTerm * specular_color;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Calculate final color by componing different components
|
|
232
|
+
vec4 final_color;
|
|
233
|
+
vec4 color = apply_colormap(val);
|
|
234
|
+
final_color = color * (ambient_color + diffuse_color) + specular_color;
|
|
235
|
+
final_color.a = color.a;
|
|
236
|
+
return final_color;
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
|
|
240
|
+
const vs =
|
|
241
|
+
`
|
|
242
|
+
varying vec4 v_nearpos;
|
|
243
|
+
varying vec4 v_farpos;
|
|
244
|
+
varying vec3 v_position;
|
|
245
|
+
|
|
246
|
+
void main() {
|
|
247
|
+
// Prepare transforms to map to "camera view". See also:
|
|
248
|
+
// https://threejs.org/docs/#api/renderers/webgl/WebGLProgram
|
|
249
|
+
mat4 viewtransformf = modelViewMatrix;
|
|
250
|
+
mat4 viewtransformi = inverse(modelViewMatrix);
|
|
251
|
+
|
|
252
|
+
// Project local vertex coordinate to camera position. Then do a step
|
|
253
|
+
// backward (in cam coords) to the near clipping plane, and project back. Do
|
|
254
|
+
// the same for the far clipping plane. This gives us all the information we
|
|
255
|
+
// need to calculate the ray and truncate it to the viewing cone.
|
|
256
|
+
vec4 position4 = vec4(position, 1.0);
|
|
257
|
+
vec4 pos_in_cam = viewtransformf * position4;
|
|
258
|
+
|
|
259
|
+
// Intersection of ray and near clipping plane (z = -1 in clip coords)
|
|
260
|
+
pos_in_cam.z = -pos_in_cam.w;
|
|
261
|
+
v_nearpos = viewtransformi * pos_in_cam;
|
|
262
|
+
|
|
263
|
+
// Intersection of ray and far clipping plane (z = +1 in clip coords)
|
|
264
|
+
pos_in_cam.z = pos_in_cam.w;
|
|
265
|
+
v_farpos = viewtransformi * pos_in_cam;
|
|
266
|
+
|
|
267
|
+
// Set varyings and output pos
|
|
268
|
+
v_position = position;
|
|
269
|
+
gl_Position = projectionMatrix * viewMatrix * modelMatrix * position4;
|
|
270
|
+
}
|
|
271
|
+
`;
|
|
272
|
+
|
|
273
|
+
const getUniforms = function() {
|
|
274
|
+
return {
|
|
275
|
+
u_size: { value: new THREE.Vector3( 1, 1, 1 ) },
|
|
276
|
+
u_renderstyle: { value: 0 },
|
|
277
|
+
u_renderthreshold: { value: 0.5 },
|
|
278
|
+
u_clim: { value: new THREE.Vector2( 1, 1 ) },
|
|
279
|
+
u_data: { value: null },
|
|
280
|
+
u_cmdata: { value: null },
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
exports.fs = fs;
|
|
285
|
+
exports.vs = vs;
|
|
286
|
+
exports.glslVersion = glslVersion;
|
|
287
|
+
exports.getUniforms = getUniforms;
|