@kitware/vtk.js 29.7.3 → 29.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Rendering/Core/ImageCPRMapper/Constants.d.ts +10 -0
- package/Rendering/Core/ImageCPRMapper/Constants.js +10 -0
- package/Rendering/Core/ImageCPRMapper.d.ts +46 -0
- package/Rendering/Core/ImageCPRMapper.js +11 -3
- package/Rendering/Core/VolumeMapper.js +2 -2
- package/Rendering/Core/VolumeProperty/Constants.d.ts +17 -0
- package/Rendering/Core/VolumeProperty/Constants.js +8 -2
- package/Rendering/Core/VolumeProperty.d.ts +20 -1
- package/Rendering/Core/VolumeProperty.js +2 -1
- package/Rendering/OpenGL/ImageCPRMapper.js +154 -76
- package/Rendering/OpenGL/VolumeMapper.js +89 -21
- package/Rendering/OpenGL/glsl/vtkVolumeFS.glsl.js +1 -1
- package/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import vtkDataArray from './../../Common/Core/DataArray';
|
|
|
6
6
|
import vtkImageData from './../../Common/DataModel/ImageData';
|
|
7
7
|
import vtkPolyData from './../../Common/DataModel/PolyData';
|
|
8
8
|
import vtkPolyLine from './../../Common/DataModel/PolyLine';
|
|
9
|
+
import { ProjectionMode } from './ImageCPRMapper/Constants';
|
|
9
10
|
|
|
10
11
|
interface ICoincidentTopology {
|
|
11
12
|
factor: number;
|
|
@@ -157,6 +158,51 @@ export interface vtkImageCPRMapper extends vtkAbstractMapper3D {
|
|
|
157
158
|
*/
|
|
158
159
|
setDirectionMatrix(mat: mat3): boolean;
|
|
159
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Thickness of the projection slab in image coordinates (NOT in voxels)
|
|
163
|
+
* Usually in millimeters if the spacing of the input image is set from a DICOM
|
|
164
|
+
*/
|
|
165
|
+
getProjectionSlabThickness(): number;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @see getProjectionSlabThickness
|
|
169
|
+
* @param projectionSlabThickness
|
|
170
|
+
*/
|
|
171
|
+
setProjectionSlabThickness(ProjectionSlabThickness: number): boolean;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Total number of samples of the volume done by the projection mode
|
|
175
|
+
* If this number is equal or less than 1, projection is disabled
|
|
176
|
+
* Using an odd number is advised
|
|
177
|
+
* If this number is even, the center of the slab will not be sampled
|
|
178
|
+
*/
|
|
179
|
+
getProjectionSlabNumberOfSamples(): number;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @see getProjectionSlabNumberOfSamples
|
|
183
|
+
* @param projectionSlabNumberOfSamples
|
|
184
|
+
*/
|
|
185
|
+
setProjectionSlabNumberOfSamples(projectionSlabNumberOfSamples: number): boolean;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Returns wether projection is enabled
|
|
189
|
+
* It is based on the number of samples
|
|
190
|
+
* @see getProjectionSlabNumberOfSamples
|
|
191
|
+
*/
|
|
192
|
+
isProjectionEnabled(): boolean;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The different modes of projection
|
|
196
|
+
* Available modes include MIP, MinIP and AverageIP
|
|
197
|
+
*/
|
|
198
|
+
getProjectionMode(): ProjectionMode;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @see getProjectionMode
|
|
202
|
+
* @param projectionMode
|
|
203
|
+
*/
|
|
204
|
+
setProjectionMode(projectionMode: ProjectionMode): boolean;
|
|
205
|
+
|
|
160
206
|
/**
|
|
161
207
|
* Find the data array to use for orientation in the input polydata ( @see getOrientationArrayName )
|
|
162
208
|
*/
|
|
@@ -4,6 +4,7 @@ import vtkAbstractImageMapper from './AbstractImageMapper.js';
|
|
|
4
4
|
import { m as macro } from '../../macros2.js';
|
|
5
5
|
import vtkPoints from '../../Common/Core/Points.js';
|
|
6
6
|
import vtkPolyLine from '../../Common/DataModel/PolyLine.js';
|
|
7
|
+
import { ProjectionMode } from './ImageCPRMapper/Constants.js';
|
|
7
8
|
|
|
8
9
|
const {
|
|
9
10
|
vtkErrorMacro
|
|
@@ -88,7 +89,10 @@ function vtkImageCPRMapper(publicAPI, model) {
|
|
|
88
89
|
const numComps = model.useUniformOrientation ? model.uniformOrientation.length : orientationDataArray.getNumberOfComponents();
|
|
89
90
|
switch (numComps) {
|
|
90
91
|
case 16:
|
|
91
|
-
convert =
|
|
92
|
+
convert = (outQuat, inMat) => {
|
|
93
|
+
mat4.getRotation(outQuat, inMat);
|
|
94
|
+
quat.normalize(outQuat, outQuat);
|
|
95
|
+
};
|
|
92
96
|
break;
|
|
93
97
|
case 9:
|
|
94
98
|
convert = (outQuat, inMat) => {
|
|
@@ -250,6 +254,7 @@ function vtkImageCPRMapper(publicAPI, model) {
|
|
|
250
254
|
return Math.sqrt(d2 - x * x);
|
|
251
255
|
});
|
|
252
256
|
};
|
|
257
|
+
publicAPI.isProjectionEnabled = () => model.projectionSlabNumberOfSamples > 1;
|
|
253
258
|
publicAPI.setCenterlineData = centerlineData => publicAPI.setInputData(centerlineData, 1);
|
|
254
259
|
publicAPI.setCenterlineConnection = centerlineConnection => publicAPI.setInputConnection(centerlineConnection, 1);
|
|
255
260
|
publicAPI.setImageData = imageData => publicAPI.setInputData(imageData, 0);
|
|
@@ -274,7 +279,10 @@ const DEFAULT_VALUES = {
|
|
|
274
279
|
orientationArrayName: null,
|
|
275
280
|
tangentDirection: [1, 0, 0],
|
|
276
281
|
bitangentDirection: [0, 1, 0],
|
|
277
|
-
normalDirection: [0, 0, 1]
|
|
282
|
+
normalDirection: [0, 0, 1],
|
|
283
|
+
projectionSlabThickness: 1,
|
|
284
|
+
projectionSlabNumberOfSamples: 1,
|
|
285
|
+
projectionMode: ProjectionMode.MAX
|
|
278
286
|
};
|
|
279
287
|
|
|
280
288
|
// ----------------------------------------------------------------------------
|
|
@@ -294,7 +302,7 @@ function extend(publicAPI, model) {
|
|
|
294
302
|
});
|
|
295
303
|
|
|
296
304
|
// Setters and getters
|
|
297
|
-
macro.setGet(publicAPI, model, ['width', 'uniformOrientation', 'useUniformOrientation', 'centerPoint', 'preferSizeOverAccuracy', 'orientationArrayName', 'tangentDirection', 'bitangentDirection', 'normalDirection']);
|
|
305
|
+
macro.setGet(publicAPI, model, ['width', 'uniformOrientation', 'useUniformOrientation', 'centerPoint', 'preferSizeOverAccuracy', 'orientationArrayName', 'tangentDirection', 'bitangentDirection', 'normalDirection', 'projectionSlabThickness', 'projectionSlabNumberOfSamples', 'projectionMode']);
|
|
298
306
|
CoincidentTopologyHelper.implementCoincidentTopologyMethods(publicAPI, model);
|
|
299
307
|
|
|
300
308
|
// Object methods
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { m as macro } from '../../macros2.js';
|
|
2
2
|
import { F as createUninitializedBounds, C as clampValue, J as floor } from '../../Common/Core/Math/index.js';
|
|
3
3
|
import Constants from './VolumeMapper/Constants.js';
|
|
4
|
-
import
|
|
4
|
+
import vtkAbstractMapper3D from './AbstractMapper3D.js';
|
|
5
5
|
import vtkPiecewiseFunction from '../../Common/DataModel/PiecewiseFunction.js';
|
|
6
6
|
|
|
7
7
|
const {
|
|
@@ -132,7 +132,7 @@ const DEFAULT_VALUES = {
|
|
|
132
132
|
function extend(publicAPI, model) {
|
|
133
133
|
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
134
134
|
Object.assign(model, DEFAULT_VALUES, initialValues);
|
|
135
|
-
|
|
135
|
+
vtkAbstractMapper3D.extend(publicAPI, model, initialValues);
|
|
136
136
|
macro.setGet(publicAPI, model, ['sampleDistance', 'imageSampleDistance', 'maximumSamplesPerRay', 'autoAdjustSampleDistances', 'initialInteractionScale', 'interactionSampleDistanceFactor', 'blendMode', 'filterMode', 'preferSizeOverAccuracy', 'computeNormalFromOpacity', 'volumetricScatteringBlending', 'globalIlluminationReach', 'volumeShadowSamplingDistFactor', 'anisotropy', 'localAmbientOcclusion', 'LAOKernelSize', 'LAOKernelRadius']);
|
|
137
137
|
macro.setGetArray(publicAPI, model, ['ipScalarRange'], 2);
|
|
138
138
|
macro.event(publicAPI, model, 'lightingActivated');
|
|
@@ -9,8 +9,25 @@ export declare enum OpacityMode {
|
|
|
9
9
|
PROPORTIONAL = 1,
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export declare enum ColorMixPreset {
|
|
13
|
+
// Add a `//VTK::CustomColorMix` tag to the Fragment shader
|
|
14
|
+
// See usage in file `testColorMix` and in function `setColorMixPreset`
|
|
15
|
+
CUSTOM = 0,
|
|
16
|
+
|
|
17
|
+
// Two components preset
|
|
18
|
+
// Out color: sum of colors weighted by opacity
|
|
19
|
+
// Out opacity: sum of opacities
|
|
20
|
+
ADDITIVE = 1,
|
|
21
|
+
|
|
22
|
+
// Two components preset
|
|
23
|
+
// Out color: color of the first component, colorized by second component with an intensity that is the second component's opacity
|
|
24
|
+
// Out opacity: opacity of the first component
|
|
25
|
+
COLORIZE = 2,
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
declare const _default: {
|
|
13
29
|
InterpolationType: typeof InterpolationType;
|
|
14
30
|
OpacityMode: typeof OpacityMode;
|
|
31
|
+
ColorMixPreset: typeof ColorMixPreset;
|
|
15
32
|
};
|
|
16
33
|
export default _default;
|
|
@@ -7,9 +7,15 @@ const OpacityMode = {
|
|
|
7
7
|
FRACTIONAL: 0,
|
|
8
8
|
PROPORTIONAL: 1
|
|
9
9
|
};
|
|
10
|
+
const ColorMixPreset = {
|
|
11
|
+
CUSTOM: 0,
|
|
12
|
+
ADDITIVE: 1,
|
|
13
|
+
COLORIZE: 2
|
|
14
|
+
};
|
|
10
15
|
var Constants = {
|
|
11
16
|
InterpolationType,
|
|
12
|
-
OpacityMode
|
|
17
|
+
OpacityMode,
|
|
18
|
+
ColorMixPreset
|
|
13
19
|
};
|
|
14
20
|
|
|
15
|
-
export { InterpolationType, OpacityMode, Constants as default };
|
|
21
|
+
export { ColorMixPreset, InterpolationType, OpacityMode, Constants as default };
|
|
@@ -2,7 +2,7 @@ import vtkPiecewiseFunction from './../../Common/DataModel/PiecewiseFunction';
|
|
|
2
2
|
import { vtkObject } from './../../interfaces';
|
|
3
3
|
import { Nullable } from './../../types';
|
|
4
4
|
import vtkColorTransferFunction from './ColorTransferFunction';
|
|
5
|
-
import { InterpolationType, OpacityMode } from './VolumeProperty/Constants';
|
|
5
|
+
import { ColorMixPreset, InterpolationType, OpacityMode } from './VolumeProperty/Constants';
|
|
6
6
|
|
|
7
7
|
export interface IVolumePropertyInitialValues {
|
|
8
8
|
independentComponents?: boolean;
|
|
@@ -69,6 +69,11 @@ export interface vtkVolumeProperty extends vtkObject {
|
|
|
69
69
|
*/
|
|
70
70
|
getGradientOpacityMinimumValue(index: number): number;
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
*
|
|
74
|
+
*/
|
|
75
|
+
getColorMixPreset(): Nullable<ColorMixPreset>;
|
|
76
|
+
|
|
72
77
|
/**
|
|
73
78
|
*
|
|
74
79
|
*/
|
|
@@ -182,6 +187,20 @@ export interface vtkVolumeProperty extends vtkObject {
|
|
|
182
187
|
*/
|
|
183
188
|
setGrayTransferFunction(index: number, func: vtkPiecewiseFunction): boolean;
|
|
184
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Set the color mix code to a preset value
|
|
192
|
+
* Set to null to use no preset
|
|
193
|
+
* See the test `testColorMix` for an example on how to use this preset.
|
|
194
|
+
*
|
|
195
|
+
* If set to `CUSTOM`, a tag `//VTK::CustomColorMix` is made available to the
|
|
196
|
+
* user who can user shader replacements to put its own code. The given code
|
|
197
|
+
* will be used to mix the colors from each component.
|
|
198
|
+
* Each component is available as a rgba vec4: `comp0`, `comp1`...
|
|
199
|
+
* There are other useful functions or variable available. To find them,
|
|
200
|
+
* see `//VTK::CustomComponentsColorMix::Impl` tag in `vtkVolumeFS.glsl`.
|
|
201
|
+
*/
|
|
202
|
+
setColorMixPreset(preset: Nullable<ColorMixPreset>): boolean;
|
|
203
|
+
|
|
185
204
|
/**
|
|
186
205
|
* Does the data have independent components, or do some define color only?
|
|
187
206
|
* If IndependentComponents is On (the default) then each component will be
|
|
@@ -204,6 +204,7 @@ function vtkVolumeProperty(publicAPI, model) {
|
|
|
204
204
|
// ----------------------------------------------------------------------------
|
|
205
205
|
|
|
206
206
|
const DEFAULT_VALUES = {
|
|
207
|
+
colorMixPreset: null,
|
|
207
208
|
independentComponents: true,
|
|
208
209
|
interpolationType: InterpolationType.FAST_LINEAR,
|
|
209
210
|
shade: false,
|
|
@@ -243,7 +244,7 @@ function extend(publicAPI, model) {
|
|
|
243
244
|
});
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
|
-
macro.setGet(publicAPI, model, ['independentComponents', 'interpolationType', 'shade', 'ambient', 'diffuse', 'specular', 'specularPower', 'useLabelOutline', 'labelOutlineOpacity']);
|
|
247
|
+
macro.setGet(publicAPI, model, ['colorMixPreset', 'independentComponents', 'interpolationType', 'shade', 'ambient', 'diffuse', 'specular', 'specularPower', 'useLabelOutline', 'labelOutlineOpacity']);
|
|
247
248
|
macro.setGetArray(publicAPI, model, ['labelOutlineThickness']);
|
|
248
249
|
|
|
249
250
|
// Object methods
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { m as macro } from '../../macros2.js';
|
|
2
2
|
import { mat4, vec3 } from 'gl-matrix';
|
|
3
|
-
import
|
|
3
|
+
import { Filter } from './Texture/Constants.js';
|
|
4
|
+
import { InterpolationType } from '../Core/ImageProperty/Constants.js';
|
|
5
|
+
import { ProjectionMode } from '../Core/ImageCPRMapper/Constants.js';
|
|
6
|
+
import { Representation } from '../Core/Property/Constants.js';
|
|
7
|
+
import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js';
|
|
8
|
+
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
4
9
|
import vtkHelper from './Helper.js';
|
|
10
|
+
import vtkOpenGLTexture from './Texture.js';
|
|
5
11
|
import vtkReplacementShaderMapper from './ReplacementShaderMapper.js';
|
|
6
12
|
import vtkShaderProgram from './ShaderProgram.js';
|
|
7
|
-
import
|
|
8
|
-
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
9
|
-
import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js';
|
|
10
|
-
import { Representation } from '../Core/Property/Constants.js';
|
|
11
|
-
import { Filter } from './Texture/Constants.js';
|
|
12
|
-
import { InterpolationType } from '../Core/ImageProperty/Constants.js';
|
|
13
|
+
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
13
14
|
import { v as vtkPolyDataVS } from './glsl/vtkPolyDataVS.glsl.js';
|
|
14
15
|
import { v as vtkPolyDataFS } from './glsl/vtkPolyDataFS.glsl.js';
|
|
15
16
|
import { registerOverride } from './ViewNodeFactory.js';
|
|
@@ -364,40 +365,35 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
364
365
|
});
|
|
365
366
|
const customAttributes = [centerlinePosition, quadIndex];
|
|
366
367
|
if (!model.renderable.getUseUniformOrientation()) {
|
|
367
|
-
// For each
|
|
368
|
-
// Send
|
|
369
|
-
// The interpolation will occur in the fragment shader (slerp)
|
|
370
|
-
const
|
|
371
|
-
const
|
|
372
|
-
const
|
|
373
|
-
for (let
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
for (let
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
// Bot array
|
|
384
|
-
centerlineBotDirectionArray[offset + 0] = directions[baseDirectionIdx + 3];
|
|
385
|
-
centerlineBotDirectionArray[offset + 1] = directions[baseDirectionIdx + 4];
|
|
386
|
-
centerlineBotDirectionArray[offset + 2] = directions[baseDirectionIdx + 5];
|
|
387
|
-
offset += 3;
|
|
368
|
+
// For each quad (i.e. centerline segment), a top and bottom quaternion give the orientation
|
|
369
|
+
// Send both quaternions to each vertex and use flat interpolation to get them "as is" in the fragment shader
|
|
370
|
+
// The interpolation of the quaternions will occur in the fragment shader (slerp)
|
|
371
|
+
const orientationQuats = model.renderable.getOrientedCenterline().getOrientations() ?? [];
|
|
372
|
+
const centerlineTopOrientationArray = new Float32Array(4 * nPts);
|
|
373
|
+
const centerlineBotOrientationArray = new Float32Array(4 * nPts);
|
|
374
|
+
for (let quadIdx = 0; quadIdx < nLines; ++quadIdx) {
|
|
375
|
+
// All vertices of a given quad have the same topDir and botDir
|
|
376
|
+
// Polyline goes from top to bottom
|
|
377
|
+
const topQuat = orientationQuats[quadIdx];
|
|
378
|
+
const botQuat = orientationQuats[quadIdx + 1];
|
|
379
|
+
for (let pointInQuadIdx = 0; pointInQuadIdx < 4; ++pointInQuadIdx) {
|
|
380
|
+
const pointIdx = pointInQuadIdx + 4 * quadIdx;
|
|
381
|
+
const quaternionArrayOffset = 4 * pointIdx;
|
|
382
|
+
centerlineTopOrientationArray.set(topQuat, quaternionArrayOffset);
|
|
383
|
+
centerlineBotOrientationArray.set(botQuat, quaternionArrayOffset);
|
|
388
384
|
}
|
|
389
385
|
}
|
|
390
|
-
const
|
|
391
|
-
numberOfComponents:
|
|
392
|
-
values:
|
|
393
|
-
name: '
|
|
386
|
+
const centerlineTopOrientation = vtkDataArray.newInstance({
|
|
387
|
+
numberOfComponents: 4,
|
|
388
|
+
values: centerlineTopOrientationArray,
|
|
389
|
+
name: 'centerlineTopOrientation'
|
|
394
390
|
});
|
|
395
|
-
const
|
|
396
|
-
numberOfComponents:
|
|
397
|
-
values:
|
|
398
|
-
name: '
|
|
391
|
+
const centerlineBotOrientation = vtkDataArray.newInstance({
|
|
392
|
+
numberOfComponents: 4,
|
|
393
|
+
values: centerlineBotOrientationArray,
|
|
394
|
+
name: 'centerlineBotOrientation'
|
|
399
395
|
});
|
|
400
|
-
customAttributes.push(
|
|
396
|
+
customAttributes.push(centerlineTopOrientation, centerlineBotOrientation);
|
|
401
397
|
}
|
|
402
398
|
model.tris.getCABO().createVBO(cells, 'polys', Representation.SURFACE, {
|
|
403
399
|
points,
|
|
@@ -420,9 +416,11 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
420
416
|
const iComp = actor.getProperty().getIndependentComponents();
|
|
421
417
|
const useCenterPoint = !!model.renderable.getCenterPoint();
|
|
422
418
|
const useUniformOrientation = model.renderable.getUseUniformOrientation();
|
|
423
|
-
|
|
419
|
+
const projectionMode = model.renderable.isProjectionEnabled() && model.renderable.getProjectionMode();
|
|
420
|
+
if (cellBO.getProgram() === 0 || model.lastUseCenterPoint !== useCenterPoint || model.lastUseUniformOrientation !== useUniformOrientation || model.lastProjectionMode !== projectionMode || model.lastHaveSeenDepthRequest !== model.haveSeenDepthRequest || model.lastTextureComponents !== tNumComp || model.lastIndependentComponents !== iComp) {
|
|
424
421
|
model.lastUseCenterPoint = useCenterPoint;
|
|
425
422
|
model.lastUseUniformOrientation = useUniformOrientation;
|
|
423
|
+
model.lastProjectionMode = projectionMode;
|
|
426
424
|
model.lastHaveSeenDepthRequest = model.haveSeenDepthRequest;
|
|
427
425
|
model.lastTextureComponents = tNumComp;
|
|
428
426
|
model.lastIndependentComponents = iComp;
|
|
@@ -438,15 +436,22 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
438
436
|
let VSSource = shaders.Vertex;
|
|
439
437
|
let FSSource = shaders.Fragment;
|
|
440
438
|
|
|
439
|
+
// https://glmatrix.net/docs/vec3.js.html#line522
|
|
440
|
+
const applyQuaternionToVecShaderFunction = ['vec3 applyQuaternionToVec(vec4 q, vec3 v) {', ' float uvx = q.y * v.z - q.z * v.y;', ' float uvy = q.z * v.x - q.x * v.z;', ' float uvz = q.x * v.y - q.y * v.x;', ' float uuvx = q.y * uvz - q.z * uvy;', ' float uuvy = q.z * uvx - q.x * uvz;', ' float uuvz = q.x * uvy - q.y * uvx;', ' float w2 = q.w * 2.0;', ' uvx *= w2;', ' uvy *= w2;', ' uvz *= w2;', ' uuvx *= 2.0;', ' uuvy *= 2.0;', ' uuvz *= 2.0;', ' return vec3(v.x + uvx + uuvx, v.y + uvy + uuvy, v.z + uvz + uuvz);', '}'];
|
|
441
|
+
|
|
441
442
|
// Vertex shader main replacements
|
|
442
443
|
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Camera::Dec', ['uniform mat4 MCPCMatrix;']).result;
|
|
443
444
|
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::PositionVC::Impl', [' gl_Position = MCPCMatrix * vertexMC;']).result;
|
|
444
445
|
const vsColorDec = ['attribute vec3 centerlinePosition;', 'attribute float quadIndex;', 'uniform float width;', 'out vec2 quadOffsetVSOutput;', 'out vec3 centerlinePosVSOutput;'];
|
|
446
|
+
const useProjection = model.renderable.isProjectionEnabled();
|
|
445
447
|
const isDirectionUniform = model.renderable.getUseUniformOrientation();
|
|
446
448
|
if (isDirectionUniform) {
|
|
447
|
-
vsColorDec.push('out vec3
|
|
449
|
+
vsColorDec.push('out vec3 samplingDirVSOutput;', 'uniform vec4 centerlineOrientation;', 'uniform vec3 tangentDirection;', ...applyQuaternionToVecShaderFunction);
|
|
450
|
+
if (useProjection) {
|
|
451
|
+
vsColorDec.push('out vec3 projectionDirVSOutput;', 'uniform vec3 bitangentDirection;');
|
|
452
|
+
}
|
|
448
453
|
} else {
|
|
449
|
-
vsColorDec.push('out
|
|
454
|
+
vsColorDec.push('out vec4 centerlineTopOrientationVSOutput;', 'out vec4 centerlineBotOrientationVSOutput;', 'attribute vec4 centerlineTopOrientation;', 'attribute vec4 centerlineBotOrientation;');
|
|
450
455
|
}
|
|
451
456
|
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::Dec', vsColorDec).result;
|
|
452
457
|
const vsColorImpl = [
|
|
@@ -454,15 +459,12 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
454
459
|
// quadOffsetVSOutput.y: bottom = 0.0; top = 1.0;
|
|
455
460
|
'quadOffsetVSOutput = vec2(width * (mod(quadIndex, 2.0) == 0.0 ? -0.5 : 0.5), quadIndex > 1.0 ? 0.0 : 1.0);', 'centerlinePosVSOutput = centerlinePosition;'];
|
|
456
461
|
if (isDirectionUniform) {
|
|
457
|
-
vsColorImpl.push('
|
|
462
|
+
vsColorImpl.push('samplingDirVSOutput = applyQuaternionToVec(centerlineOrientation, tangentDirection);');
|
|
463
|
+
if (useProjection) {
|
|
464
|
+
vsColorImpl.push('projectionDirVSOutput = applyQuaternionToVec(centerlineOrientation, bitangentDirection);');
|
|
465
|
+
}
|
|
458
466
|
} else {
|
|
459
|
-
vsColorImpl.push(
|
|
460
|
-
// When u and v are unit vectors: uvAngle = 2 * atan2(|| u - v ||, || u + v ||)
|
|
461
|
-
// When u != -v: || u + v || > 0
|
|
462
|
-
// When x > 0: atan2(y, x) = atan(y/x)
|
|
463
|
-
// Thus: dirAngle = 2 * atan(|| topDir - botDir || / || topDir + botDir ||)
|
|
464
|
-
// This is more stable and should not be to slow compared to acos(dot(u, v))
|
|
465
|
-
'vec3 sumVec = centerlineTopDirection + centerlineBotDirection;', 'float sumLen2 = dot(sumVec, sumVec);', 'float diffLen2 = 4.0 - sumLen2;', 'if (diffLen2 < 0.001) {', ' // vectors are too close to each other, use lerp', ' centerlineAngleVSOutput = -1.0; // use negative angle as a flag for lerp', ' centerlineTopDirVSOutput = centerlineTopDirection;', ' centerlineBotDirVSOutput = centerlineBotDirection;', '} else if (sumLen2 == 0.0) {', " // vector are opposite to each other, don't make a choice for the user", ' // use slerp without direction, it will display the centerline color on each row of pixel', ' centerlineAngleVSOutput = 0.0;', ' centerlineTopDirVSOutput = vec3(0.0);', ' centerlineBotDirVSOutput = vec3(0.0);', '} else {', ' // use slerp', ' centerlineAngleVSOutput = 2.0 * atan(sqrt(diffLen2/sumLen2));', ' float sinAngle = sin(centerlineAngleVSOutput);', ' centerlineTopDirVSOutput = centerlineTopDirection / sinAngle;', ' centerlineBotDirVSOutput = centerlineBotDirection / sinAngle;', '}');
|
|
467
|
+
vsColorImpl.push('centerlineTopOrientationVSOutput = centerlineTopOrientation;', 'centerlineBotOrientationVSOutput = centerlineBotOrientation;');
|
|
466
468
|
}
|
|
467
469
|
VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Color::Impl', vsColorImpl).result;
|
|
468
470
|
|
|
@@ -484,10 +486,19 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
484
486
|
`uniform float cshift0;`, `uniform float cscale0;`,
|
|
485
487
|
// weighting shift and scale
|
|
486
488
|
`uniform float pwfshift0;`, `uniform float pwfscale0;`];
|
|
489
|
+
if (useProjection) {
|
|
490
|
+
tcoordFSDec.push('uniform vec3 spacing;', 'uniform int projectionSlabNumberOfSamples;', 'uniform float projectionConstantOffset;', 'uniform float projectionStepLength;');
|
|
491
|
+
}
|
|
487
492
|
if (isDirectionUniform) {
|
|
488
|
-
tcoordFSDec.push('in vec3
|
|
493
|
+
tcoordFSDec.push('in vec3 samplingDirVSOutput;');
|
|
494
|
+
if (useProjection) {
|
|
495
|
+
tcoordFSDec.push('in vec3 projectionDirVSOutput;');
|
|
496
|
+
}
|
|
489
497
|
} else {
|
|
490
|
-
tcoordFSDec.push('
|
|
498
|
+
tcoordFSDec.push('uniform vec3 tangentDirection;', 'in vec4 centerlineTopOrientationVSOutput;', 'in vec4 centerlineBotOrientationVSOutput;', ...applyQuaternionToVecShaderFunction);
|
|
499
|
+
if (useProjection) {
|
|
500
|
+
tcoordFSDec.push('uniform vec3 bitangentDirection;');
|
|
501
|
+
}
|
|
491
502
|
}
|
|
492
503
|
const centerPoint = model.renderable.getCenterPoint();
|
|
493
504
|
if (centerPoint) {
|
|
@@ -526,18 +537,62 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
526
537
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::TCoord::Dec', tcoordFSDec).result;
|
|
527
538
|
let tcoordFSImpl = [];
|
|
528
539
|
if (isDirectionUniform) {
|
|
529
|
-
tcoordFSImpl.push('vec3
|
|
540
|
+
tcoordFSImpl.push('vec3 samplingDirection = samplingDirVSOutput;');
|
|
541
|
+
if (useProjection) {
|
|
542
|
+
tcoordFSImpl.push('vec3 projectionDirection = projectionDirVSOutput;');
|
|
543
|
+
}
|
|
530
544
|
} else {
|
|
531
545
|
// Slerp or lerp between centerlineTopDirVSOutput and centerlineBotDirVSOutput
|
|
532
546
|
// We use quadOffsetVSOutput.y: bottom = 0.0; top = 1.0;
|
|
533
|
-
tcoordFSImpl.push(
|
|
547
|
+
tcoordFSImpl.push(
|
|
548
|
+
// Slerp / Lerp
|
|
549
|
+
'vec4 q0 = centerlineBotOrientationVSOutput;', 'vec4 q1 = centerlineTopOrientationVSOutput;', 'float qCosAngle = dot(q0, q1);', 'vec4 interpolatedOrientation;', 'if (qCosAngle > 0.999 || qCosAngle < -0.999) {', ' // Use LERP instead of SLERP when the two quaternions are close or opposite', ' interpolatedOrientation = normalize(mix(q0, q1, quadOffsetVSOutput.y));', '} else {', ' float omega = acos(qCosAngle);', ' interpolatedOrientation = normalize(sin((1.0 - quadOffsetVSOutput.y) * omega) * q0 + sin(quadOffsetVSOutput.y * omega) * q1);', '}', 'vec3 samplingDirection = applyQuaternionToVec(interpolatedOrientation, tangentDirection);');
|
|
550
|
+
if (useProjection) {
|
|
551
|
+
tcoordFSImpl.push('vec3 projectionDirection = applyQuaternionToVec(interpolatedOrientation, bitangentDirection);');
|
|
552
|
+
}
|
|
534
553
|
}
|
|
535
554
|
if (centerPoint) {
|
|
536
|
-
tcoordFSImpl.push('float baseOffset = dot(
|
|
555
|
+
tcoordFSImpl.push('float baseOffset = dot(samplingDirection, globalCenterPoint - centerlinePosVSOutput);', 'float horizontalOffset = quadOffsetVSOutput.x + baseOffset;');
|
|
537
556
|
} else {
|
|
538
557
|
tcoordFSImpl.push('float horizontalOffset = quadOffsetVSOutput.x;');
|
|
539
558
|
}
|
|
540
|
-
tcoordFSImpl.push('vec3 volumePosMC = centerlinePosVSOutput + horizontalOffset *
|
|
559
|
+
tcoordFSImpl.push('vec3 volumePosMC = centerlinePosVSOutput + horizontalOffset * samplingDirection;', 'vec3 volumePosTC = (MCTCMatrix * vec4(volumePosMC, 1.0)).xyz;', 'if (any(lessThan(volumePosTC, vec3(0.0))) || any(greaterThan(volumePosTC, vec3(1.0))))', '{', ' // set the background color and exit', ' gl_FragData[0] = backgroundColor;', ' return;', '}');
|
|
560
|
+
if (useProjection) {
|
|
561
|
+
const projectionMode = model.renderable.getProjectionMode();
|
|
562
|
+
switch (projectionMode) {
|
|
563
|
+
case ProjectionMode.MIN:
|
|
564
|
+
tcoordFSImpl.push('const vec4 initialProjectionTextureValue = vec4(1.0);');
|
|
565
|
+
break;
|
|
566
|
+
case ProjectionMode.MAX:
|
|
567
|
+
case ProjectionMode.AVERAGE:
|
|
568
|
+
default:
|
|
569
|
+
tcoordFSImpl.push('const vec4 initialProjectionTextureValue = vec4(0.0);');
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Loop on all the samples of the projection
|
|
574
|
+
tcoordFSImpl.push('vec3 projectionScaledDirection = projectionDirection / spacing;', 'vec3 projectionStep = projectionStepLength * projectionScaledDirection;', 'vec3 projectionStartPosition = volumePosTC + projectionConstantOffset * projectionScaledDirection;', 'vec4 tvalue = initialProjectionTextureValue;', 'for (int projectionSampleIdx = 0; projectionSampleIdx < projectionSlabNumberOfSamples; ++projectionSampleIdx) {', ' vec3 projectionSamplePosition = projectionStartPosition + float(projectionSampleIdx) * projectionStep;', ' vec4 sampledTextureValue = texture(volumeTexture, projectionSamplePosition);');
|
|
575
|
+
switch (projectionMode) {
|
|
576
|
+
case ProjectionMode.MAX:
|
|
577
|
+
tcoordFSImpl.push(' tvalue = max(tvalue, sampledTextureValue);');
|
|
578
|
+
break;
|
|
579
|
+
case ProjectionMode.MIN:
|
|
580
|
+
tcoordFSImpl.push(' tvalue = min(tvalue, sampledTextureValue);');
|
|
581
|
+
break;
|
|
582
|
+
case ProjectionMode.AVERAGE:
|
|
583
|
+
default:
|
|
584
|
+
tcoordFSImpl.push(' tvalue = tvalue + sampledTextureValue;');
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
tcoordFSImpl.push('}');
|
|
588
|
+
|
|
589
|
+
// Process the total if needed
|
|
590
|
+
if (projectionMode === ProjectionMode.AVERAGE) {
|
|
591
|
+
tcoordFSImpl.push('tvalue = tvalue / float(projectionSlabNumberOfSamples);');
|
|
592
|
+
}
|
|
593
|
+
} else {
|
|
594
|
+
tcoordFSImpl.push('vec4 tvalue = texture(volumeTexture, volumePosTC);');
|
|
595
|
+
}
|
|
541
596
|
if (iComps) {
|
|
542
597
|
const rgba = ['r', 'g', 'b', 'a'];
|
|
543
598
|
for (let comp = 0; comp < tNumComp; ++comp) {
|
|
@@ -610,32 +665,55 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
610
665
|
shaders.Geometry = '';
|
|
611
666
|
};
|
|
612
667
|
publicAPI.setMapperShaderParameters = (cellBO, ren, actor) => {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
668
|
+
const program = cellBO.getProgram();
|
|
669
|
+
const cellArrayBufferObject = cellBO.getCABO();
|
|
670
|
+
if (cellArrayBufferObject.getElementCount() && (model.VBOBuildTime.getMTime() > cellBO.getAttributeUpdateTime().getMTime() || cellBO.getShaderSourceTime().getMTime() > cellBO.getAttributeUpdateTime().getMTime())) {
|
|
671
|
+
if (program.isAttributeUsed('vertexMC')) {
|
|
672
|
+
if (!cellBO.getVAO().addAttributeArray(program, cellArrayBufferObject, 'vertexMC', cellArrayBufferObject.getVertexOffset(), cellArrayBufferObject.getStride(), model.context.FLOAT, 3, model.context.FALSE)) {
|
|
616
673
|
vtkErrorMacro('Error setting vertexMC in shader VAO.');
|
|
617
674
|
}
|
|
618
675
|
}
|
|
619
676
|
// Custom data of the CABO (centerlinePosition, centerlineTopDirection,
|
|
620
677
|
// centerlineBotDirection, quadIndex and user defined custom data)
|
|
621
678
|
cellBO.getCABO().getCustomData().forEach(data => {
|
|
622
|
-
if (data &&
|
|
679
|
+
if (data && program.isAttributeUsed(data.name) && !cellBO.getVAO().addAttributeArray(program, cellArrayBufferObject, data.name, data.offset, cellArrayBufferObject.getStride(), model.context.FLOAT, data.components, model.context.FALSE)) {
|
|
623
680
|
vtkErrorMacro(`Error setting ${data.name} in shader VAO.`);
|
|
624
681
|
}
|
|
625
682
|
});
|
|
626
683
|
cellBO.getAttributeUpdateTime().modified();
|
|
627
684
|
}
|
|
628
685
|
const texUnit = model.volumeTexture.getTextureUnit();
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
cellBO.getProgram().
|
|
632
|
-
if (
|
|
633
|
-
const
|
|
634
|
-
cellBO.getProgram().setUniform3fArray('
|
|
635
|
-
}
|
|
636
|
-
if (
|
|
686
|
+
program.setUniformi('volumeTexture', texUnit);
|
|
687
|
+
program.setUniformf('width', model.renderable.getWidth());
|
|
688
|
+
cellBO.getProgram().setUniform4fv('backgroundColor', model.renderable.getBackgroundColor());
|
|
689
|
+
if (program.isUniformUsed('tangentDirection')) {
|
|
690
|
+
const tangentDirection = model.renderable.getTangentDirection();
|
|
691
|
+
cellBO.getProgram().setUniform3fArray('tangentDirection', tangentDirection);
|
|
692
|
+
}
|
|
693
|
+
if (program.isUniformUsed('bitangentDirection')) {
|
|
694
|
+
const bitangentDirection = model.renderable.getBitangentDirection();
|
|
695
|
+
cellBO.getProgram().setUniform3fArray('bitangentDirection', bitangentDirection);
|
|
696
|
+
}
|
|
697
|
+
if (program.isUniformUsed('centerlineOrientation')) {
|
|
698
|
+
const uniformOrientation = model.renderable.getUniformOrientation();
|
|
699
|
+
cellBO.getProgram().setUniform4fv('centerlineOrientation', uniformOrientation);
|
|
700
|
+
}
|
|
701
|
+
if (program.isUniformUsed('globalCenterPoint')) {
|
|
637
702
|
const centerPoint = model.renderable.getCenterPoint();
|
|
638
|
-
|
|
703
|
+
program.setUniform3fArray('globalCenterPoint', centerPoint);
|
|
704
|
+
}
|
|
705
|
+
// Projection uniforms
|
|
706
|
+
if (model.renderable.isProjectionEnabled()) {
|
|
707
|
+
const image = model.currentImageDataInput;
|
|
708
|
+
const spacing = image.getSpacing();
|
|
709
|
+
const projectionSlabThickness = model.renderable.getProjectionSlabThickness();
|
|
710
|
+
const projectionSlabNumberOfSamples = model.renderable.getProjectionSlabNumberOfSamples();
|
|
711
|
+
program.setUniform3fArray('spacing', spacing);
|
|
712
|
+
program.setUniformi('projectionSlabNumberOfSamples', projectionSlabNumberOfSamples);
|
|
713
|
+
const constantOffset = -0.5 * projectionSlabThickness;
|
|
714
|
+
program.setUniformf('projectionConstantOffset', constantOffset);
|
|
715
|
+
const stepLength = projectionSlabThickness / (projectionSlabNumberOfSamples - 1);
|
|
716
|
+
program.setUniformf('projectionStepLength', stepLength);
|
|
639
717
|
}
|
|
640
718
|
|
|
641
719
|
// Model coordinates to image space
|
|
@@ -646,7 +724,7 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
646
724
|
const MCICMatrix = image.getWorldToIndex();
|
|
647
725
|
const ICTCMatrix = mat4.fromScaling(new Float32Array(16), vec3.inverse([], image.getDimensions()));
|
|
648
726
|
const MCTCMatrix = mat4.mul(ICTCMatrix, ICTCMatrix, MCICMatrix);
|
|
649
|
-
|
|
727
|
+
program.setUniformMatrix('MCTCMatrix', MCTCMatrix);
|
|
650
728
|
if (model.haveSeenDepthRequest) {
|
|
651
729
|
cellBO.getProgram().setUniformi('depthRequest', model.renderDepth ? 1 : 0);
|
|
652
730
|
}
|
|
@@ -657,8 +735,8 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
657
735
|
macro.vtkErrorMacro('OpenGL has a limit of 6 clipping planes');
|
|
658
736
|
numClipPlanes = 6;
|
|
659
737
|
}
|
|
660
|
-
const shiftScaleEnabled =
|
|
661
|
-
const inverseShiftScaleMatrix = shiftScaleEnabled ?
|
|
738
|
+
const shiftScaleEnabled = cellArrayBufferObject.getCoordShiftAndScaleEnabled();
|
|
739
|
+
const inverseShiftScaleMatrix = shiftScaleEnabled ? cellArrayBufferObject.getInverseShiftAndScaleMatrix() : null;
|
|
662
740
|
const mat = inverseShiftScaleMatrix ? mat4.copy(model.imagematinv, actor.getMatrix()) : actor.getMatrix();
|
|
663
741
|
if (inverseShiftScaleMatrix) {
|
|
664
742
|
mat4.transpose(mat, mat);
|
|
@@ -677,17 +755,17 @@ function vtkOpenGLImageCPRMapper(publicAPI, model) {
|
|
|
677
755
|
planeEquations.push(planeEquation[j]);
|
|
678
756
|
}
|
|
679
757
|
}
|
|
680
|
-
|
|
681
|
-
|
|
758
|
+
program.setUniformi('numClipPlanes', numClipPlanes);
|
|
759
|
+
program.setUniform4fv('clipPlanes', planeEquations);
|
|
682
760
|
}
|
|
683
761
|
|
|
684
762
|
// handle coincident
|
|
685
|
-
if (
|
|
763
|
+
if (program.isUniformUsed('coffset')) {
|
|
686
764
|
const cp = publicAPI.getCoincidentParameters(ren, actor);
|
|
687
|
-
|
|
765
|
+
program.setUniformf('coffset', cp.offset);
|
|
688
766
|
// cfactor isn't always used when coffset is.
|
|
689
|
-
if (
|
|
690
|
-
|
|
767
|
+
if (program.isUniformUsed('cfactor')) {
|
|
768
|
+
program.setUniformf('cfactor', cp.factor);
|
|
691
769
|
}
|
|
692
770
|
}
|
|
693
771
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { n as newInstance$1, o as obj, e as setGet, h as chain, c as macro } from '../../macros2.js';
|
|
2
|
+
import DeepEqual from 'fast-deep-equal';
|
|
2
3
|
import { mat4, mat3, vec3 } from 'gl-matrix';
|
|
3
4
|
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
4
5
|
import { VtkDataTypes } from '../../Common/Core/DataArray/Constants.js';
|
|
@@ -6,12 +7,13 @@ import vtkHelper from './Helper.js';
|
|
|
6
7
|
import { u as uninitializeBounds } from '../../Common/Core/Math/index.js';
|
|
7
8
|
import vtkOpenGLFramebuffer from './Framebuffer.js';
|
|
8
9
|
import vtkOpenGLTexture from './Texture.js';
|
|
10
|
+
import vtkReplacementShaderMapper from './ReplacementShaderMapper.js';
|
|
9
11
|
import vtkShaderProgram from './ShaderProgram.js';
|
|
10
12
|
import vtkVertexArrayObject from './VertexArrayObject.js';
|
|
11
13
|
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
12
14
|
import { Representation } from '../Core/Property/Constants.js';
|
|
13
15
|
import { Wrap, Filter } from './Texture/Constants.js';
|
|
14
|
-
import { InterpolationType, OpacityMode } from '../Core/VolumeProperty/Constants.js';
|
|
16
|
+
import { InterpolationType, OpacityMode, ColorMixPreset } from '../Core/VolumeProperty/Constants.js';
|
|
15
17
|
import { BlendMode } from '../Core/VolumeMapper/Constants.js';
|
|
16
18
|
import { v as vtkVolumeVS } from './glsl/vtkVolumeVS.glsl.js';
|
|
17
19
|
import { v as vtkVolumeFS } from './glsl/vtkVolumeFS.glsl.js';
|
|
@@ -25,18 +27,7 @@ const {
|
|
|
25
27
|
// ----------------------------------------------------------------------------
|
|
26
28
|
// helper methods
|
|
27
29
|
// ----------------------------------------------------------------------------
|
|
28
|
-
|
|
29
|
-
function arrayEquals(a, b) {
|
|
30
|
-
if (a.length !== b.length) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
for (let i = 0; i < a.length; ++i) {
|
|
34
|
-
if (a[i] !== b[i]) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
30
|
+
|
|
40
31
|
function computeFnToString(property, pwfun, numberOfComponents) {
|
|
41
32
|
if (pwfun) {
|
|
42
33
|
const iComps = property.getIndependentComponents();
|
|
@@ -44,6 +35,81 @@ function computeFnToString(property, pwfun, numberOfComponents) {
|
|
|
44
35
|
}
|
|
45
36
|
return '0';
|
|
46
37
|
}
|
|
38
|
+
function getColorCodeFromPreset(colorMixPreset) {
|
|
39
|
+
switch (colorMixPreset) {
|
|
40
|
+
case ColorMixPreset.CUSTOM:
|
|
41
|
+
return '//VTK::CustomColorMix';
|
|
42
|
+
case ColorMixPreset.ADDITIVE:
|
|
43
|
+
return `
|
|
44
|
+
// compute normals
|
|
45
|
+
mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);
|
|
46
|
+
#if (vtkLightComplexity > 0) && defined(vtkComputeNormalFromOpacity)
|
|
47
|
+
vec3 scalarInterp0[2];
|
|
48
|
+
vec4 normalLight0 = computeNormalForDensity(posIS, tstep, scalarInterp0, 0);
|
|
49
|
+
scalarInterp0[0] = scalarInterp0[0] * oscale0 + oshift0;
|
|
50
|
+
scalarInterp0[1] = scalarInterp0[1] * oscale0 + oshift0;
|
|
51
|
+
normalLight0 = computeDensityNormal(scalarInterp0, height0, 1.0);
|
|
52
|
+
|
|
53
|
+
vec3 scalarInterp1[2];
|
|
54
|
+
vec4 normalLight1 = computeNormalForDensity(posIS, tstep, scalarInterp1, 1);
|
|
55
|
+
scalarInterp1[0] = scalarInterp1[0] * oscale1 + oshift1;
|
|
56
|
+
scalarInterp1[1] = scalarInterp1[1] * oscale1 + oshift1;
|
|
57
|
+
normalLight1 = computeDensityNormal(scalarInterp1, height1, 1.0);
|
|
58
|
+
#else
|
|
59
|
+
vec4 normalLight0 = normalMat[0];
|
|
60
|
+
vec4 normalLight1 = normalMat[1];
|
|
61
|
+
#endif
|
|
62
|
+
|
|
63
|
+
// compute opacities
|
|
64
|
+
float opacity0 = pwfValue0;
|
|
65
|
+
float opacity1 = pwfValue1;
|
|
66
|
+
#ifdef vtkGradientOpacityOn
|
|
67
|
+
float gof0 = computeGradientOpacityFactor(normalMat[0].a, goscale0, goshift0, gomin0, gomax0);
|
|
68
|
+
opacity0 *= gof0;
|
|
69
|
+
float gof1 = computeGradientOpacityFactor(normalMat[1].a, goscale1, goshift1, gomin1, gomax1);
|
|
70
|
+
opacity1 *= gof1;
|
|
71
|
+
#endif
|
|
72
|
+
float opacitySum = opacity0 + opacity1;
|
|
73
|
+
if (opacitySum <= 0.0) {
|
|
74
|
+
return vec4(0.0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// mix the colors and opacities
|
|
78
|
+
tColor0 = applyAllLightning(tColor0, opacity0, posIS, normalLight0);
|
|
79
|
+
tColor1 = applyAllLightning(tColor1, opacity1, posIS, normalLight1);
|
|
80
|
+
vec3 mixedColor = (opacity0 * tColor0 + opacity1 * tColor1) / opacitySum;
|
|
81
|
+
return vec4(mixedColor, min(1.0, opacitySum));
|
|
82
|
+
`;
|
|
83
|
+
case ColorMixPreset.COLORIZE:
|
|
84
|
+
return `
|
|
85
|
+
// compute normals
|
|
86
|
+
mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);
|
|
87
|
+
#if (vtkLightComplexity > 0) && defined(vtkComputeNormalFromOpacity)
|
|
88
|
+
vec3 scalarInterp0[2];
|
|
89
|
+
vec4 normalLight0 = computeNormalForDensity(posIS, tstep, scalarInterp0, 0);
|
|
90
|
+
scalarInterp0[0] = scalarInterp0[0] * oscale0 + oshift0;
|
|
91
|
+
scalarInterp0[1] = scalarInterp0[1] * oscale0 + oshift0;
|
|
92
|
+
normalLight0 = computeDensityNormal(scalarInterp0, height0, 1.0);
|
|
93
|
+
#else
|
|
94
|
+
vec4 normalLight0 = normalMat[0];
|
|
95
|
+
#endif
|
|
96
|
+
|
|
97
|
+
// compute opacities
|
|
98
|
+
float opacity0 = pwfValue0;
|
|
99
|
+
#ifdef vtkGradientOpacityOn
|
|
100
|
+
float gof0 = computeGradientOpacityFactor(normalMat[0].a, goscale0, goshift0, gomin0, gomax0);
|
|
101
|
+
opacity0 *= gof0;
|
|
102
|
+
#endif
|
|
103
|
+
|
|
104
|
+
// mix the colors and opacities
|
|
105
|
+
vec3 color = tColor0 * mix(vec3(1.0), tColor1, pwfValue1);
|
|
106
|
+
color = applyAllLightning(color, opacity0, posIS, normalLight0);
|
|
107
|
+
return vec4(color, opacity0);
|
|
108
|
+
`;
|
|
109
|
+
default:
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
47
113
|
|
|
48
114
|
// ----------------------------------------------------------------------------
|
|
49
115
|
// vtkOpenGLVolumeMapper methods
|
|
@@ -90,10 +156,6 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
90
156
|
publicAPI.renderPiece(ren, actor);
|
|
91
157
|
}
|
|
92
158
|
};
|
|
93
|
-
publicAPI.buildShaders = (shaders, ren, actor) => {
|
|
94
|
-
publicAPI.getShaderTemplate(shaders, ren, actor);
|
|
95
|
-
publicAPI.replaceShaderValues(shaders, ren, actor);
|
|
96
|
-
};
|
|
97
159
|
publicAPI.getShaderTemplate = (shaders, ren, actor) => {
|
|
98
160
|
shaders.Vertex = vtkVolumeVS;
|
|
99
161
|
shaders.Fragment = vtkVolumeFS;
|
|
@@ -128,6 +190,12 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
128
190
|
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::vtkProportionalComponents', proportionalComponents.join('\n')).result;
|
|
129
191
|
}
|
|
130
192
|
}
|
|
193
|
+
const colorMixPreset = actor.getProperty().getColorMixPreset();
|
|
194
|
+
const colorMixCode = getColorCodeFromPreset(colorMixPreset);
|
|
195
|
+
if (colorMixCode) {
|
|
196
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::CustomComponentsColorMixOn', '#define vtkCustomComponentsColorMix').result;
|
|
197
|
+
FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::CustomComponentsColorMix::Impl', colorMixCode).result;
|
|
198
|
+
}
|
|
131
199
|
|
|
132
200
|
// WebGL only supports loops over constants
|
|
133
201
|
// and does not support while loops so we
|
|
@@ -273,6 +341,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
273
341
|
vec3.set(vsize, (ext[1] - ext[0]) * spc[0], (ext[3] - ext[2]) * spc[1], (ext[5] - ext[4]) * spc[2]);
|
|
274
342
|
const maxSamples = vec3.length(vsize) / publicAPI.getCurrentSampleDistance(ren);
|
|
275
343
|
const state = {
|
|
344
|
+
colorMixPreset: actor.getProperty().getColorMixPreset(),
|
|
276
345
|
interpolationType: actor.getProperty().getInterpolationType(),
|
|
277
346
|
useLabelOutline: actor.getProperty().getUseLabelOutline(),
|
|
278
347
|
numComp,
|
|
@@ -286,10 +355,8 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
|
|
|
286
355
|
|
|
287
356
|
// We only need to rebuild the shader if one of these variables has changed,
|
|
288
357
|
// since they are used in the shader template replacement step.
|
|
289
|
-
if (!model.previousState ||
|
|
290
|
-
model.previousState =
|
|
291
|
-
...state
|
|
292
|
-
};
|
|
358
|
+
if (!model.previousState || !DeepEqual(model.previousState, state)) {
|
|
359
|
+
model.previousState = state;
|
|
293
360
|
return true;
|
|
294
361
|
}
|
|
295
362
|
|
|
@@ -1217,6 +1284,7 @@ function extend(publicAPI, model) {
|
|
|
1217
1284
|
|
|
1218
1285
|
// Inheritance
|
|
1219
1286
|
vtkViewNode.extend(publicAPI, model, initialValues);
|
|
1287
|
+
vtkReplacementShaderMapper.implementBuildShadersWithReplacements(publicAPI, model, initialValues);
|
|
1220
1288
|
model.VBOBuildTime = {};
|
|
1221
1289
|
obj(model.VBOBuildTime, {
|
|
1222
1290
|
mtime: 0
|
|
@@ -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\n\nuniform float outlineOpacity;\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;\nuniform sampler2D ttexture;\n\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) || (defined vtkClippingPlanesOn)\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 vtkClippingPlanesOn\n void adjustClippedVoxelValues(vec3 pos, vec3 texPos[3], inout vec3 g1)\n {\n vec3 g1VC[3];\n for (int i = 0; i < 3; ++i)\n {\n g1VC[i] = IStoVC(texPos[i]);\n }\n vec3 posVC = IStoVC(pos);\n for (int i = 0; i < clip_numPlanes; ++i)\n {\n for (int j = 0; j < 3; ++j)\n {\n if(dot(vec3(vClipPlaneOrigins[i] - g1VC[j].xyz), vClipPlaneNormals[i]) > 0.0)\n {\n g1[j] = 0.0;\n }\n }\n }\n }\n#endif\n\n #ifdef vtkComputeNormalFromOpacity\n #ifdef vtkGradientOpacityOn\n vec4 computeDensityNormal(float gradientMag, vec3 scalarInterp[2])\n {\n #else\n //if gradient opacity not on but using density gradient\n vec4 computeDensityNormal(vec3 scalarInterp[2])\n {\n #endif\n vec3 opacityG1, opacityG2;\n opacityG1.x = texture2D(otexture, vec2(scalarInterp[0].x * oscale0 + oshift0, 0.5)).r;\n opacityG1.y = texture2D(otexture, vec2(scalarInterp[0].y * oscale0 + oshift0, 0.5)).r;\n opacityG1.z = texture2D(otexture, vec2(scalarInterp[0].z * oscale0 + oshift0, 0.5)).r;\n opacityG2.x = texture2D(otexture, vec2(scalarInterp[1].x * oscale0 + oshift0, 0.5)).r;\n opacityG2.y = texture2D(otexture, vec2(scalarInterp[1].y * oscale0 + oshift0, 0.5)).r;\n opacityG2.z = texture2D(otexture, vec2(scalarInterp[1].z * oscale0 + oshift0, 0.5)).r;\n #ifdef vtkGradientOpacityOn\n float gradOpacityFactor = 1.0f;\n if (gradientMag >= 0.0){\n gradOpacityFactor = computeGradientOpacityFactor(gradientMag, goscale0, goshift0, gomin0, gomax0);\n }\n opacityG1.xyz *= gradOpacityFactor;\n opacityG2.xyz *= gradOpacityFactor;\n #endif\n\n vec4 opacityG = vec4(opacityG1 - opacityG2, 1.0f);\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\n vec4 computeNormalForDensity(vec3 pos, vec3 tstep, out vec3 scalarInterp[2])\n {\n vec3 xvec = vec3(tstep.x, 0.0, 0.0);\n vec3 yvec = vec3(0.0, tstep.y, 0.0);\n vec3 zvec = vec3(0.0, 0.0, tstep.z);\n vec3 texPosPVec[3];\n texPosPVec[0] = pos + xvec;\n texPosPVec[1] = pos + yvec;\n texPosPVec[2] = pos + zvec;\n vec3 texPosNVec[3];\n texPosNVec[0] = pos - xvec;\n texPosNVec[1] = pos - yvec;\n texPosNVec[2] = pos - zvec;\n vec3 g1, g2;\n\n scalarInterp[0].x = getTextureValue(texPosPVec[0]).a;\n scalarInterp[0].y = getTextureValue(texPosPVec[1]).a;\n scalarInterp[0].z = getTextureValue(texPosPVec[2]).a;\n scalarInterp[1].x = getTextureValue(texPosNVec[0]).a;\n scalarInterp[1].y = getTextureValue(texPosNVec[1]).a;\n scalarInterp[1].z = getTextureValue(texPosNVec[2]).a;\n\n #ifdef vtkClippingPlanesOn\n adjustClippedVoxelValues(pos, texPosPVec, scalarInterp[0]);\n adjustClippedVoxelValues(pos, texPosNVec, scalarInterp[1]);\n #endif\n vec4 result;\n result.x = scalarInterp[0].x - scalarInterp[1].x;\n result.y = scalarInterp[0].y - scalarInterp[1].y;\n result.z = scalarInterp[0].z - scalarInterp[1].z;\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\n vec4 computeNormal(vec3 pos, vec3 tstep)\n {\n vec3 xvec = vec3(tstep.x, 0.0, 0.0);\n vec3 yvec = vec3(0.0, tstep.y, 0.0);\n vec3 zvec = vec3(0.0, 0.0, tstep.z);\n vec3 texPosPVec[3];\n texPosPVec[0] = pos + xvec;\n texPosPVec[1] = pos + yvec;\n texPosPVec[2] = pos + zvec;\n vec3 texPosNVec[3];\n texPosNVec[0] = pos - xvec;\n texPosNVec[1] = pos - yvec;\n texPosNVec[2] = pos - zvec;\n vec3 g1, g2;\n g1.x = getTextureValue(texPosPVec[0]).a;\n g1.y = getTextureValue(texPosPVec[1]).a;\n g1.z = getTextureValue(texPosPVec[2]).a;\n g2.x = getTextureValue(texPosNVec[0]).a;\n g2.y = getTextureValue(texPosNVec[1]).a;\n g2.z = getTextureValue(texPosNVec[2]).a;\n #ifdef vtkClippingPlanesOn\n adjustClippedVoxelValues(pos, texPosPVec, g1);\n adjustClippedVoxelValues(pos, texPosNVec, g2);\n #endif\n vec4 result;\n result = vec4(g1 - g2, -1.0);\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 1.5 and 3.0\n float sampleDistanceISVS_jitter = sampleDistanceISVS * mix(1.5, 3.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 float maxdist = hit.tmax;\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 if(maxdist < EPSILON) {\n return 1.0;\n }\n\n // support gradient opacity\n #ifdef vtkGradientOpacityOn\n vec4 normal;\n #endif\n\n float current_dist = 0.0;\n float current_step = length(sampleDistanceISVS_jitter * lightDirNormIS);\n float clamped_step = 0.0;\n\n vec4 scalar = vec4(0.0);\n while(current_dist < maxdist)\n {\n#ifdef vtkClippingPlanesOn\n vec3 posVC = IStoVC(posIS);\n for (int i = 0; i < clip_numPlanes; ++i)\n {\n if (dot(vec3(vClipPlaneOrigins[i] - posVC), vClipPlaneNormals[i]) > 0.0)\n {\n current_dist = maxdist;\n }\n }\n#endif\n scalar = getTextureValue(posIS);\n opacity = texture2D(otexture, vec2(scalar.r * oscale0 + oshift0, 0.5)).r;\n #ifdef vtkGradientOpacityOn\n normal = computeNormal(posIS, 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 clamped_step = min(maxdist - current_dist, current_step);\n posIS += clamped_step * lightDirNormIS;\n current_dist += 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 op, vec3 lightDir, vec4 normal){\n // apply LAO only at selected locations, otherwise return full brightness\n if (normal.w > 0.0 && op > 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 int segmentIndex = int(centerValue.r * 255.0);\n \n // Use texture sampling for outlineThickness\n float textureCoordinate = float(segmentIndex - 1) / 1024.0;\n float textureValue = texture2D(ttexture, vec2(textureCoordinate, 0.5)).r;\n\n int actualThickness = int(textureValue * 255.0);\n\n if (actualThickness == 0) {\n return vec4(0, 0, 1, 1);\n }\n\n // If it is the background (segment index 0), we should quickly bail out. \n // Previously, this was determined by tColor.a, which was incorrect as it\n // prevented the outline from appearing when the fill is 0.\n if (segmentIndex == 0){\n return vec4(0, 0, 0, 0);\n }\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 for (int i = -actualThickness; i <= actualThickness; i++) {\n for (int j = -actualThickness; j <= actualThickness; 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 = outlineOpacity;\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 vec3 scalarInterp[2];\n vec4 normal0 = computeNormalForDensity(posIS, tstep, scalarInterp);\n if (length(normal0)>0.0){\n #ifdef vtkGradientOpacityOn\n normalLight = computeDensityNormal(normal0.w, scalarInterp);\n #else\n normalLight = computeDensityNormal(scalarInterp);\n #endif\n if (length(normalLight) == 0.0){\n normalLight = normal0;\n }\n }\n #else\n vec4 normal0 = computeNormal(posIS, 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 #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\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, 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 , 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";
|
|
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 vtkCustomComponentsColorMix\n//VTK::CustomComponentsColorMixOn\n\n// possibly define if independent components are actually used \n#if (defined(vtkIndependentComponentsOn) && vtkNumComponents >= 2) || defined(vtkCustomComponentsColorMix)\n #define UseIndependentComponents\n#endif\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\n\nuniform float outlineOpacity;\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#ifdef UseIndependentComponents\n#if vtkNumComponents > 1\nuniform float goscale1;\nuniform float goshift1;\nuniform float gomin1;\nuniform float gomax1;\n#if vtkNumComponents > 2\nuniform float goscale2;\nuniform float goshift2;\nuniform float gomin2;\nuniform float gomax2;\n#if vtkNumComponents > 3\nuniform float goscale3;\nuniform float goshift3;\nuniform float gomin3;\nuniform float gomax3;\n#endif\n#endif\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#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\n// jitter texture\nuniform sampler2D jtexture;\nuniform sampler2D ttexture;\n\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 UseIndependentComponents\n#if vtkNumComponents == 1\nuniform float mix0;\n#define height0 0.5\n#endif\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\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#ifndef UseIndependentComponents\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#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) || (defined vtkClippingPlanesOn)\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 return clamp(normalMag * goscale + goshift, gomin, gomax);\n}\n\n//=======================================================================\n// compute the normal and gradient magnitude for a position, uses forward difference\n#if (vtkLightComplexity > 0) || (defined vtkGradientOpacityOn)\n #ifdef vtkClippingPlanesOn\n void adjustClippedVoxelValues(vec3 pos, vec3 texPos[3], inout vec3 g1)\n {\n vec3 g1VC[3];\n for (int i = 0; i < 3; ++i)\n {\n g1VC[i] = IStoVC(texPos[i]);\n }\n vec3 posVC = IStoVC(pos);\n for (int i = 0; i < clip_numPlanes; ++i)\n {\n for (int j = 0; j < 3; ++j)\n {\n if(dot(vec3(vClipPlaneOrigins[i] - g1VC[j].xyz), vClipPlaneNormals[i]) > 0.0)\n {\n g1[j] = 0.0;\n }\n }\n }\n }\n #endif\n\n #ifdef vtkComputeNormalFromOpacity\n vec4 computeDensityNormal(vec3 opacityUCoords[2], float opactityTextureHeight, float gradientOpacity) {\n vec3 opacityG1, opacityG2;\n opacityG1.x = texture2D(otexture, vec2(opacityUCoords[0].x, opactityTextureHeight)).r;\n opacityG1.y = texture2D(otexture, vec2(opacityUCoords[0].y, opactityTextureHeight)).r;\n opacityG1.z = texture2D(otexture, vec2(opacityUCoords[0].z, opactityTextureHeight)).r;\n opacityG2.x = texture2D(otexture, vec2(opacityUCoords[1].x, opactityTextureHeight)).r;\n opacityG2.y = texture2D(otexture, vec2(opacityUCoords[1].y, opactityTextureHeight)).r;\n opacityG2.z = texture2D(otexture, vec2(opacityUCoords[1].z, opactityTextureHeight)).r;\n opacityG1.xyz *= gradientOpacity;\n opacityG2.xyz *= gradientOpacity;\n\n vec4 opacityG = vec4(opacityG1 - opacityG2, 1.0f);\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 (!all(equal(opacityG.xyz, vec3(0.0)))) {\n return vec4(normalize(opacityG.xyz),opacityG.w);\n } else {\n return vec4(0.0);\n }\n }\n\n vec4 computeNormalForDensity(vec3 pos, vec3 tstep, out vec3 scalarInterp[2], const int opacityComponent)\n {\n vec3 xvec = vec3(tstep.x, 0.0, 0.0);\n vec3 yvec = vec3(0.0, tstep.y, 0.0);\n vec3 zvec = vec3(0.0, 0.0, tstep.z);\n vec3 texPosPVec[3];\n texPosPVec[0] = pos + xvec;\n texPosPVec[1] = pos + yvec;\n texPosPVec[2] = pos + zvec;\n vec3 texPosNVec[3];\n texPosNVec[0] = pos - xvec;\n texPosNVec[1] = pos - yvec;\n texPosNVec[2] = pos - zvec;\n vec3 g1, g2;\n\n scalarInterp[0].x = getTextureValue(texPosPVec[0])[opacityComponent];\n scalarInterp[0].y = getTextureValue(texPosPVec[1])[opacityComponent];\n scalarInterp[0].z = getTextureValue(texPosPVec[2])[opacityComponent];\n scalarInterp[1].x = getTextureValue(texPosNVec[0])[opacityComponent];\n scalarInterp[1].y = getTextureValue(texPosNVec[1])[opacityComponent];\n scalarInterp[1].z = getTextureValue(texPosNVec[2])[opacityComponent];\n\n #ifdef vtkClippingPlanesOn\n adjustClippedVoxelValues(pos, texPosPVec, scalarInterp[0]);\n adjustClippedVoxelValues(pos, texPosNVec, scalarInterp[1]);\n #endif\n vec4 result;\n result.x = scalarInterp[0].x - scalarInterp[1].x;\n result.y = scalarInterp[0].y - scalarInterp[1].y;\n result.z = scalarInterp[0].z - scalarInterp[1].z;\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\n // only works with dependent components\n vec4 computeNormal(vec3 pos, vec3 tstep)\n {\n vec3 xvec = vec3(tstep.x, 0.0, 0.0);\n vec3 yvec = vec3(0.0, tstep.y, 0.0);\n vec3 zvec = vec3(0.0, 0.0, tstep.z);\n vec3 texPosPVec[3];\n texPosPVec[0] = pos + xvec;\n texPosPVec[1] = pos + yvec;\n texPosPVec[2] = pos + zvec;\n vec3 texPosNVec[3];\n texPosNVec[0] = pos - xvec;\n texPosNVec[1] = pos - yvec;\n texPosNVec[2] = pos - zvec;\n vec3 g1, g2;\n g1.x = getTextureValue(texPosPVec[0]).a;\n g1.y = getTextureValue(texPosPVec[1]).a;\n g1.z = getTextureValue(texPosPVec[2]).a;\n g2.x = getTextureValue(texPosNVec[0]).a;\n g2.y = getTextureValue(texPosNVec[1]).a;\n g2.z = getTextureValue(texPosNVec[2]).a;\n #ifdef vtkClippingPlanesOn\n adjustClippedVoxelValues(pos, texPosPVec, g1);\n adjustClippedVoxelValues(pos, texPosNVec, g2);\n #endif\n vec4 result;\n result = vec4(g1 - g2, -1.0);\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 1.5 and 3.0\n float sampleDistanceISVS_jitter = sampleDistanceISVS * mix(1.5, 3.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 float maxdist = hit.tmax;\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 if(maxdist < EPSILON) {\n return 1.0;\n }\n\n float current_dist = 0.0;\n float current_step = length(sampleDistanceISVS_jitter * lightDirNormIS);\n float clamped_step = 0.0;\n\n vec4 scalar = vec4(0.0);\n while(current_dist < maxdist)\n {\n#ifdef vtkClippingPlanesOn\n vec3 posVC = IStoVC(posIS);\n for (int i = 0; i < clip_numPlanes; ++i)\n {\n if (dot(vec3(vClipPlaneOrigins[i] - posVC), vClipPlaneNormals[i]) > 0.0)\n {\n current_dist = maxdist;\n }\n }\n#endif\n scalar = getTextureValue(posIS);\n opacity = texture2D(otexture, vec2(scalar.r * oscale0 + oshift0, 0.5)).r;\n #if defined(vtkGradientOpacityOn) && !defined(UseIndependentComponents)\n vec4 normal = computeNormal(posIS, 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 clamped_step = min(maxdist - current_dist, current_step);\n posIS += clamped_step * lightDirNormIS;\n current_dist += 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 op, vec3 lightDir, vec4 normal){\n // apply LAO only at selected locations, otherwise return full brightness\n if (normal.w > 0.0 && op > 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// LAO of surface shadows and volume shadows only work with dependent components\nvec3 applyAllLightning(vec3 tColor, float alpha, vec3 posIS, vec4 normalLight) {\n #if vtkLightComplexity > 0\n // surface shadows if needed\n #ifdef SurfaceShadowOn\n #if vtkLightComplexity < 3\n vec3 tColorS = applyLightingDirectional(posIS, vec4(tColor, alpha), normalLight);\n #else\n vec3 tColorS = applyLightingPositional(posIS, vec4(tColor, alpha), normalLight, IStoVC(posIS));\n #endif\n #endif\n\n // volume shadows if needed\n #ifdef VolumeShadowOn\n vec3 tColorVS = applyShadowRay(tColor, posIS, rayDirVC);\n #endif\n\n // merge\n #ifdef VolumeShadowOn\n #ifdef SurfaceShadowOn\n // surface shadows + volumetric shadows\n float vol_coef = volumetricScatteringBlending * (1.0 - alpha / 2.0) * (1.0 - atan(normalLight.w) * INV4PI);\n tColor = (1.0-vol_coef) * tColorS + vol_coef * tColorVS;\n #else\n // volumetric shadows only\n tColor = tColorVS;\n #endif\n #else\n #ifdef SurfaceShadowOn\n // surface shadows only\n tColor = tColorS;\n #else\n // no shadows\n applyLighting(tColor, normal3);\n #endif\n #endif\n #endif\n return tColor;\n}\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 int segmentIndex = int(centerValue.r * 255.0);\n \n // Use texture sampling for outlineThickness\n float textureCoordinate = float(segmentIndex - 1) / 1024.0;\n float textureValue = texture2D(ttexture, vec2(textureCoordinate, 0.5)).r;\n\n int actualThickness = int(textureValue * 255.0);\n\n if (actualThickness == 0) {\n return vec4(0, 0, 1, 1);\n }\n\n // If it is the background (segment index 0), we should quickly bail out. \n // Previously, this was determined by tColor.a, which was incorrect as it\n // prevented the outline from appearing when the fill is 0.\n if (segmentIndex == 0){\n return vec4(0, 0, 0, 0);\n }\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 for (int i = -actualThickness; i <= actualThickness; i++) {\n for (int j = -actualThickness; j <= actualThickness; 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 = outlineOpacity;\n }\n\n return tColor;\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 #ifdef UseIndependentComponents\n // sample textures\n vec3 tColor0 = texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, height0)).rgb;\n float pwfValue0 = texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, height0)).r;\n\n #if vtkNumComponents > 1\n vec3 tColor1 = texture2D(ctexture, vec2(tValue.g * cscale1 + cshift1, height1)).rgb;\n float pwfValue1 = texture2D(otexture, vec2(tValue.g * oscale1 + oshift1, height1)).r;\n\n #if vtkNumComponents > 2\n vec3 tColor2 = texture2D(ctexture, vec2(tValue.b * cscale2 + cshift2, height2)).rgb;\n float pwfValue2 = texture2D(otexture, vec2(tValue.b * oscale2 + oshift2, height2)).r;\n\n #if vtkNumComponents > 3\n vec3 tColor3 = texture2D(ctexture, vec2(tValue.a * cscale3 + cshift3, height3)).rgb;\n float pwfValue3 = texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, height3)).r;\n #endif\n #endif\n #endif\n\n #if !defined(vtkCustomComponentsColorMix)\n // default path for component color mix\n\n // compute the normal vectors as needed\n #if (vtkLightComplexity > 0) || defined(vtkGradientOpacityOn)\n mat4 normalMat = computeMat4Normal(posIS, tValue, tstep);\n #endif\n\n // compute gradient opacity factors as needed\n vec4 goFactor = vec4(1.0, 1.0 ,1.0 ,1.0);\n #if defined(vtkGradientOpacityOn)\n #if !defined(vtkComponent0Proportional)\n goFactor.x =\n computeGradientOpacityFactor(normalMat[0].a, goscale0, goshift0, gomin0, gomax0);\n #endif\n #if vtkNumComponents > 1\n #if !defined(vtkComponent1Proportional)\n goFactor.y =\n computeGradientOpacityFactor(normalMat[1].a, goscale1, goshift1, gomin1, gomax1);\n #endif\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n goFactor.z =\n computeGradientOpacityFactor(normalMat[2].a, goscale2, goshift2, gomin2, gomax2);\n #endif\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n goFactor.w =\n computeGradientOpacityFactor(normalMat[3].a, goscale3, goshift3, gomin3, gomax3);\n #endif\n #endif\n #endif\n #endif\n #endif\n\n // process color and opacity for each component\n #if !defined(vtkComponent0Proportional)\n float alpha = goFactor.x*mix0*pwfValue0;\n #if vtkLightComplexity > 0\n applyLighting(tColor0, normalMat[0]);\n #endif\n #else\n tColor0 *= pwfValue0;\n float alpha = mix(pwfValue0, 1.0, (1.0 - mix0));\n #endif\n\n #if vtkNumComponents > 1\n #if !defined(vtkComponent1Proportional)\n alpha += goFactor.y*mix1*pwfValue1;\n #if vtkLightComplexity > 0\n applyLighting(tColor1, normalMat[1]);\n #endif\n #else\n tColor1 *= pwfValue1;\n alpha *= mix(pwfValue1, 1.0, (1.0 - mix1));\n #endif\n\n #if vtkNumComponents > 2\n #if !defined(vtkComponent2Proportional)\n alpha += goFactor.z*mix2*pwfValue2;\n #if vtkLightComplexity > 0\n applyLighting(tColor2, normalMat[2]);\n #endif\n #else\n tColor2 *= pwfValue2;\n alpha *= mix(pwfValue2, 1.0, (1.0 - mix2));\n #endif\n #endif\n\n #if vtkNumComponents > 3\n #if !defined(vtkComponent3Proportional)\n alpha += goFactor.w*mix3*pwfValue3;\n #if vtkLightComplexity > 0\n applyLighting(tColor3, normalMat[3]);\n #endif\n #else\n tColor3 *= pwfValue3;\n alpha *= mix(pwfValue3, 1.0, (1.0 - mix3));\n #endif\n #endif\n #endif\n\n // perform final independent blend\n vec3 tColor = mix0 * tColor0;\n #if vtkNumComponents > 1\n tColor += mix1 * tColor1;\n #if vtkNumComponents > 2\n tColor += mix2 * tColor2;\n #if vtkNumComponents > 3\n tColor += mix3 * tColor3;\n #endif\n #endif\n #endif\n\n return vec4(tColor, alpha);\n #else\n /*\n * Mix the color information from all the independent components to get a single rgba output\n * Gradient opactity factors and normals are not computed\n *\n * You can compute these using:\n * - computeMat4Normal: always available, compute normal only for non proportional components, used by default independent component mix\n * - computeDensityNormal & computeNormalForDensity: available if ((LightComplexity > 0) || GradientOpacityOn) && ComputeNormalFromOpacity),\n * used by dependent component color mix, see code for Additive preset in OpenGl/VolumeMapper\n * - computeGradientOpacityFactor: always available, used in a lot of places\n *\n * Using applyAllLightning() is advised for shading but some features don't work well with it (volume shadows, LAO)\n * mix0, mix1, ... are defined for each component that is used and correspond to the componentWeight\n */\n //VTK::CustomComponentsColorMix::Impl\n #endif\n #else\n // dependent components\n\n // compute normal if needed\n #if (vtkLightComplexity > 0) || defined(vtkGradientOpacityOn)\n // use component 3 of the opacity texture as getTextureValue() sets alpha to the opacity value\n #ifdef vtkComputeNormalFromOpacity\n vec3 scalarInterp[2];\n vec4 normal0 = computeNormalForDensity(posIS, tstep, scalarInterp, 3);\n #else\n vec4 normal0 = computeNormal(posIS, tstep);\n #endif\n #endif\n\n // compute gradient opacity factor enabled\n #if defined(vtkGradientOpacityOn)\n float gradientOpacity = computeGradientOpacityFactor(normal0.a, goscale0, goshift0, gomin0, gomax0);\n #else\n const float gradientOpacity = 1.0;\n #endif\n\n // get color and opacity\n #if vtkNumComponents == 1\n vec3 tColor = texture2D(ctexture, vec2(tValue.r * cscale0 + cshift0, 0.5)).rgb;\n float alpha = gradientOpacity*texture2D(otexture, vec2(tValue.r * oscale0 + oshift0, 0.5)).r;\n if (alpha < EPSILON){\n return vec4(0.0);\n }\n #endif\n #if vtkNumComponents == 2\n vec3 tColor = vec3(tValue.r * cscale0 + cshift0);\n float alpha = gradientOpacity*texture2D(otexture, vec2(tValue.a * oscale1 + oshift1, 0.5)).r;\n #endif\n #if vtkNumComponents == 3\n vec3 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n float alpha = gradientOpacity*texture2D(otexture, vec2(tValue.a * oscale0 + oshift0, 0.5)).r;\n #endif\n #if vtkNumComponents == 4\n vec3 tColor;\n tColor.r = tValue.r * cscale0 + cshift0;\n tColor.g = tValue.g * cscale1 + cshift1;\n tColor.b = tValue.b * cscale2 + cshift2;\n float alpha = gradientOpacity*texture2D(otexture, vec2(tValue.a * oscale3 + oshift3, 0.5)).r;\n #endif\n\n // lighting\n #if (vtkLightComplexity > 0)\n #ifdef vtkComputeNormalFromOpacity\n vec4 normalLight;\n if (!all(equal(normal0, vec4(0.0)))) {\n scalarInterp[0] = scalarInterp[0] * oscale0 + oshift0;\n scalarInterp[1] = scalarInterp[1] * oscale0 + oshift0;\n normalLight = computeDensityNormal(scalarInterp, 0.5, gradientOpacity);\n if (all(equal(normalLight, vec4(0.0)))) {\n normalLight = normal0;\n }\n }\n #else\n vec4 normalLight = normal0;\n #endif\n tColor = applyAllLightning(tColor, alpha, posIS, normalLight);\n #endif\n\n return vec4(tColor, alpha);\n #endif // dependent\n#endif\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 #else\n #ifdef UseIndependentComponents\n #if 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 #else\n if (all(greaterThanEqual(val, ipScalarRangeMin)) &&\n all(lessThanEqual(val, ipScalarRangeMax))) {\n withinRange = true;\n }\n #endif\n #endif\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, 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 , 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/index.d.ts
CHANGED
|
@@ -160,6 +160,7 @@
|
|
|
160
160
|
/// <reference path="./Rendering/Core/Glyph3DMapper.d.ts" />
|
|
161
161
|
/// <reference path="./Rendering/Core/HardwareSelector.d.ts" />
|
|
162
162
|
/// <reference path="./Rendering/Core/ImageArrayMapper.d.ts" />
|
|
163
|
+
/// <reference path="./Rendering/Core/ImageCPRMapper/Constants.d.ts" />
|
|
163
164
|
/// <reference path="./Rendering/Core/ImageCPRMapper.d.ts" />
|
|
164
165
|
/// <reference path="./Rendering/Core/ImageMapper/Constants.d.ts" />
|
|
165
166
|
/// <reference path="./Rendering/Core/ImageMapper.d.ts" />
|