@kitware/vtk.js 33.3.2 → 34.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BREAKING_CHANGES.md +10 -0
- package/IO/Geometry/GLTFImporter/Decoder.js +21 -35
- package/IO/Geometry/GLTFImporter/ORMTexture.worker.js +42 -0
- package/IO/Geometry/GLTFImporter/Parser.js +6 -15
- package/IO/Geometry/GLTFImporter/Reader.js +10 -7
- package/IO/Geometry/GLTFImporter/Utils.js +27 -9
- package/Interaction/Manipulators/KeyboardCameraManipulator.d.ts +113 -0
- package/Rendering/Core/Actor.d.ts +5 -20
- package/Rendering/Core/Actor.js +5 -68
- package/Rendering/Core/ImageCPRMapper.d.ts +1 -20
- package/Rendering/Core/ImageCPRMapper.js +1 -2
- package/Rendering/Core/ImageProperty.d.ts +20 -1
- package/Rendering/Core/ImageProperty.js +7 -5
- package/Rendering/Core/ImageResliceMapper.d.ts +1 -20
- package/Rendering/Core/ImageResliceMapper.js +1 -2
- package/Rendering/Core/ImageSlice.d.ts +7 -23
- package/Rendering/Core/ImageSlice.js +9 -68
- package/Rendering/Core/Prop3D.d.ts +39 -2
- package/Rendering/Core/Prop3D.js +81 -2
- package/Rendering/Core/RenderWindowInteractor.d.ts +6 -0
- package/Rendering/Core/RenderWindowInteractor.js +7 -5
- package/Rendering/Core/Volume.d.ts +5 -20
- package/Rendering/Core/Volume.js +2 -70
- package/Rendering/Core/VolumeMapper/Constants.d.ts +0 -7
- package/Rendering/Core/VolumeMapper/Constants.js +2 -8
- package/Rendering/Core/VolumeMapper.d.ts +16 -173
- package/Rendering/Core/VolumeMapper.js +16 -51
- package/Rendering/Core/VolumeProperty/Constants.d.ts +12 -3
- package/Rendering/Core/VolumeProperty/Constants.js +11 -4
- package/Rendering/Core/VolumeProperty.d.ts +140 -5
- package/Rendering/Core/VolumeProperty.js +54 -7
- package/Rendering/OpenGL/Framebuffer.js +7 -1
- package/Rendering/OpenGL/ImageCPRMapper.js +72 -27
- package/Rendering/OpenGL/ImageMapper.js +71 -33
- package/Rendering/OpenGL/ImageResliceMapper.js +306 -183
- package/Rendering/OpenGL/OrderIndependentTranslucentPass.js +20 -3
- package/Rendering/OpenGL/PolyDataMapper.js +8 -9
- package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.d.ts +3 -3
- package/Rendering/OpenGL/RenderWindow/resourceSharingHelper.js +8 -5
- package/Rendering/OpenGL/SurfaceLIC/LineIntegralConvolution2D/pingpong.js +7 -1
- package/Rendering/OpenGL/SurfaceLIC/SurfaceLICInterface.js +20 -3
- package/Rendering/OpenGL/Texture.d.ts +110 -62
- package/Rendering/OpenGL/Texture.js +145 -37
- package/Rendering/OpenGL/VolumeMapper.js +763 -783
- package/Rendering/OpenGL/glsl/vtkVolumeFS.glsl.js +1 -1
- package/Rendering/WebGPU/CellArrayMapper.js +17 -17
- package/Rendering/WebGPU/PolyDataMapper.js +15 -35
- package/Rendering/WebGPU/Renderer.js +1 -1
- package/Rendering/WebGPU/Texture.js +12 -13
- package/Rendering/WebGPU/TextureManager.js +7 -12
- package/Rendering/WebGPU/VolumePassFSQ.js +2 -2
- package/_virtual/rollup-plugin-worker-loader__module_Sources/IO/Geometry/GLTFImporter/ORMTexture.worker.js +296 -0
- package/index.d.ts +1 -0
- package/macros.js +1 -1
- package/macros2.js +8 -3
- package/package.json +1 -1
|
@@ -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 UseIndependentComponents\n//VTK::IndependentComponentsOn\n\n// possibly define vtkCustomComponentsColorMix\n//VTK::CustomComponentsColorMixOn\n\n// possibly define any \"proportional\" components\n//VTK::vtkProportionalComponents\n\n// possibly define any components that are forced to nearest interpolation\n//VTK::vtkForceNearestComponents\n\n// Define the blend mode to use\n#define vtkBlendMode //VTK::BlendMode\n\n// Possibly define vtkImageLabelOutlineOn\n//VTK::ImageLabelOutlineOn\n\n// Possibly define vtkLabelEdgeProjectionOn\n//VTK::LabelEdgeProjectionOn\n\n\n#ifdef vtkImageLabelOutlineOn\n uniform float outlineOpacity;\n uniform float vpWidth;\n uniform float vpHeight;\n uniform float vpOffsetX;\n uniform float vpOffsetY;\n uniform mat4 PCWCMatrix;\n uniform mat4 vWCtoIDX;\n\n const int MAX_SEGMENT_INDEX = 256; // Define as per expected maximum\n // bool seenSegmentsByOriginalPos[MAX_SEGMENT_INDEX];\n #define MAX_SEGMENTS 256\n #define UINT_SIZE 32\n #define BITMASK_SIZE ((MAX_SEGMENTS + UINT_SIZE - 1) / UINT_SIZE)\n\n uint bitmask[BITMASK_SIZE];\n\n // Set the corresponding bit in the bitmask\n void setBit(int segmentIndex) {\n int index = segmentIndex / UINT_SIZE;\n int bitIndex = segmentIndex % UINT_SIZE;\n bitmask[index] |= 1u << bitIndex;\n }\n\n // Check if a bit is set in the bitmask\n bool isBitSet(int segmentIndex) {\n int index = segmentIndex / UINT_SIZE;\n int bitIndex = segmentIndex % UINT_SIZE;\n return ((bitmask[index] & (1u << bitIndex)) != 0u);\n }\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;\nuniform vec3 volumeSpacings; // spacing in the world coorindates\n\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\n #if defined(vtkComponent0ForceNearest) || \\\n defined(vtkComponent1ForceNearest) || \\\n defined(vtkComponent2ForceNearest) || \\\n defined(vtkComponent3ForceNearest)\n vec3 nearestPos = (floor(pos * vec3(volumeDimensions)) + 0.5) / vec3(volumeDimensions);\n vec4 nearestValue = texture(texture1, nearestPos);\n #ifdef vtkComponent0ForceNearest\n tmp[0] = nearestValue[0];\n #endif\n #ifdef vtkComponent1ForceNearest\n tmp[1] = nearestValue[1];\n #endif\n #ifdef vtkComponent2ForceNearest\n tmp[2] = nearestValue[2];\n #endif\n #ifdef vtkComponent3ForceNearest\n tmp[3] = nearestValue[3];\n #endif\n #endif\n\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\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\n#ifdef vtkImageLabelOutlineOn\n vec4 fragCoordToPCPos(vec4 fragCoord) {\n return 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\n vec4 pcPosToWorldCoord(vec4 pcPos) {\n return PCWCMatrix * pcPos;\n }\n\n vec3 fragCoordToIndexSpace(vec4 fragCoord) {\n vec4 pcPos = fragCoordToPCPos(fragCoord);\n vec4 worldCoord = pcPosToWorldCoord(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\n vec3 fragCoordToWorld(vec4 fragCoord) {\n vec4 pcPos = fragCoordToPCPos(fragCoord);\n vec4 worldCoord = pcPosToWorldCoord(pcPos);\n return worldCoord.xyz;\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 \nvec4 getColorForValue(vec4 tValue, vec3 posIS, vec3 tstep)\n{\n\n// If labeloutline and not the edge labelmap, since in the edge labelmap blend\n// we need the underlying data to sample through\n#if defined(vtkImageLabelOutlineOn) && !defined(vtkLabelEdgeProjectionOn)\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\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\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#if vtkBlendMode == 6 \nbool checkOnEdgeForNeighbor(int i, int j, int s, vec3 stepIS) {\n vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(i), gl_FragCoord.y + float(j), gl_FragCoord.z, gl_FragCoord.w);\n vec3 originalNeighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);\n\n bool justSawIt = false;\n\n vec3 neighborPosIS = originalNeighborPosIS;\n\n float stepsTraveled = 0.0;\n\n\n // float neighborValue;\n for (int k = 0; k < //VTK::MaximumSamplesValue /2 ; ++k) {\n ivec3 texCoord = ivec3(neighborPosIS * vec3(volumeDimensions));\n vec4 texValue = texelFetch(texture1, texCoord, 0);\n\n if (int(texValue.g) == s) {\n justSawIt = true;\n break;\n }\n neighborPosIS += stepIS;\n }\n\n if (justSawIt){\n return false;\n }\n\n \n neighborPosIS = originalNeighborPosIS;\n for (int k = 0; k < //VTK::MaximumSamplesValue /2 ; ++k) {\n ivec3 texCoord = ivec3(neighborPosIS * vec3(volumeDimensions));\n vec4 texValue = texelFetch(texture1, texCoord, 0);\n\n if (int(texValue.g) == s) {\n justSawIt = true;\n break;\n }\n neighborPosIS -= stepIS;\n }\n\n\n if (!justSawIt) {\n // onedge\n vec3 tColorSegment = texture2D(ctexture, vec2(float(s) * cscale1 + cshift1, height1)).rgb;\n float pwfValueSegment = texture2D(otexture, vec2(float(s) * oscale1 + oshift1, height1)).r;\n gl_FragData[0] = vec4(tColorSegment, pwfValueSegment);\n return true;\n }\n\n // not on edge\n return false;\n}\n\n#endif\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 // Initialize arrays to false\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 == 6 \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 vec3 maxPosIS = posIS; // Store the position of the max value\n int segmentIndex = int(value.g);\n bool originalPosHasSeenNonZero = false;\n\n uint bitmask = 0u;\n\n if (segmentIndex != 0) {\n // Tried using the segment index in an boolean array but reading \n // from the array by dynamic indexing was horrondously slow\n // so use bit masking instead and assign 1 to the bit corresponding to the segment index\n // and later check if the bit is set via bit operations\n setBit(segmentIndex);\n }\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 segmentIndex = int(tValue.g);\n\n if (segmentIndex != 0) {\n originalPosHasSeenNonZero = true;\n setBit(segmentIndex);\n }\n\n if (tValue.r > value.r) {\n value = tValue; // Update the max value\n maxPosIS = posIS; // Update the position where max occurred\n }\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\n if (tValue.r > value.r) {\n value = tValue; // Update the max value\n maxPosIS = posIS; // Update the position where max occurred\n } \n\n // If we have not seen any non-zero segments, we can return early\n // and grab color from the actual center value first component (image)\n if (!originalPosHasSeenNonZero) {\n gl_FragData[0] = getColorForValue(value, maxPosIS, tstep);\n return;\n }\n\n // probably we can make this configurable but for now we will use the same\n // sample distance as the original sample distance\n float neighborSampleDistanceIS = sampleDistanceIS;\n\n vec3 neighborRayStepsIS = stepIS;\n float neighborRaySteps = raySteps;\n bool shouldLookInAllNeighbors = false;\n\n float minVoxelSpacing = min(volumeSpacings[0], min(volumeSpacings[1], volumeSpacings[2]));\n vec4 base = vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);\n\n vec4 baseXPlus = vec4(gl_FragCoord.x + 1.0, gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);\n vec4 baseYPlus = vec4(gl_FragCoord.x, gl_FragCoord.y + 1.0, gl_FragCoord.z, gl_FragCoord.w);\n\n vec3 baseWorld = fragCoordToWorld(base);\n vec3 baseXPlusWorld = fragCoordToWorld(baseXPlus);\n vec3 baseYPlusWorld = fragCoordToWorld(baseYPlus);\n\n float XPlusDiff = length(baseXPlusWorld - baseWorld);\n float YPlusDiff = length(baseYPlusWorld - baseWorld);\n\n float minFragSpacingWorld = min(XPlusDiff, YPlusDiff);\n\n for (int s = 1; s < MAX_SEGMENT_INDEX; s++) {\n // bail out quickly if the segment index has not \n // been seen by the center segment\n if (!isBitSet(s)) {\n continue;\n }\n\n // Use texture sampling for outlineThickness so that we can have \n // per segment thickness\n float textureCoordinate = float(s - 1) / 1024.0;\n float textureValue = texture2D(ttexture, vec2(textureCoordinate, 0.5)).r;\n\n int actualThickness = int(textureValue * 255.0);\n\n // check the extreme points in the neighborhood since there is a better\n // chance of finding the edge there, so that we can bail out \n // faster if we find the edge\n bool onEdge =\n checkOnEdgeForNeighbor(-actualThickness, -actualThickness, s, stepIS) ||\n checkOnEdgeForNeighbor(actualThickness, actualThickness, s, stepIS) ||\n checkOnEdgeForNeighbor(actualThickness, -actualThickness, s, stepIS) ||\n checkOnEdgeForNeighbor(-actualThickness, +actualThickness, s, stepIS);\n\n if (onEdge) {\n return;\n }\n\n // since the next step is computationally expensive, we need to perform\n // some optimizations to avoid it if possible. One of the optimizations\n // is to check the whether the minimum of the voxel spacing is greater than \n // the 2 * the thickness of the outline segment. If that is the case\n // then we can safely skip the next step since we can be sure that the\n // the previous 4 checks on the extreme points would caught the entirety \n // of the all the fragments inside. i.e., this happens when we zoom out, \n if (minVoxelSpacing > (2.0 * float(actualThickness) - 1.0) * minFragSpacingWorld) {\n continue;\n }\n \n // Loop through the rest, skipping the processed extremes and the center\n for (int i = -actualThickness; i <= actualThickness; i++) {\n for (int j = -actualThickness; j <= actualThickness; j++) {\n if (i == 0 && j == 0) continue; // Skip the center\n if (abs(i) == actualThickness && abs(j) == actualThickness) continue; // Skip corners\n if (checkOnEdgeForNeighbor(i, j, s, stepIS )) {\n return;\n }\n }\n }\n }\n\n vec3 tColor0 = texture2D(ctexture, vec2(value.r * cscale0 + cshift0, height0)).rgb;\n float pwfValue0 = texture2D(otexture, vec2(value.r * oscale0 + oshift0, height0)).r;\n gl_FragData[0] = vec4(tColor0, pwfValue0);\n #endif\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\nconst float infinity = 3.402823466e38;\n\n// the output of this shader\n//VTK::Output::Dec\n\nin vec3 vertexVCVSOutput;\n\n// From Sources\\Rendering\\Core\\VolumeProperty\\Constants.js\n#define COMPOSITE_BLEND 0\n#define MAXIMUM_INTENSITY_BLEND 1\n#define MINIMUM_INTENSITY_BLEND 2\n#define AVERAGE_INTENSITY_BLEND 3\n#define ADDITIVE_INTENSITY_BLEND 4\n#define RADON_TRANSFORM_BLEND 5\n#define LABELMAP_EDGE_PROJECTION_BLEND 6\n\n#define vtkNumberOfLights //VTK::NumberOfLights\n#define vtkMaxLaoKernelSize //VTK::MaxLaoKernelSize\n#define vtkNumberOfComponents //VTK::NumberOfComponents\n#define vtkBlendMode //VTK::BlendMode\n#define vtkMaximumNumberOfSamples //VTK::MaximumNumberOfSamples\n\n//VTK::EnabledColorFunctions\n\n//VTK::EnabledLightings\n\n//VTK::EnabledMultiTexturePerVolume\n\n//VTK::EnabledGradientOpacity\n\n//VTK::EnabledIndependentComponents\n\n//VTK::vtkProportionalComponents\n\n//VTK::vtkForceNearestComponents\n\nuniform int twoSidedLighting;\n\n#if vtkMaxLaoKernelSize > 0\n vec2 kernelSample[vtkMaxLaoKernelSize];\n#endif\n\n// Textures\n#ifdef EnabledMultiTexturePerVolume\n #define vtkNumberOfVolumeTextures vtkNumberOfComponents\n#else\n #define vtkNumberOfVolumeTextures 1\n#endif\nuniform highp sampler3D volumeTexture[vtkNumberOfVolumeTextures];\nuniform sampler2D colorTexture;\nuniform sampler2D opacityTexture;\nuniform sampler2D jtexture;\nuniform sampler2D labelOutlineThicknessTexture;\n\nstruct Volume {\n // ---- Volume geometry settings ----\n\n vec3 originVC; // in VC\n vec3 spacing; // in VC per IC\n vec3 inverseSpacing; // 1/spacing\n ivec3 dimensions; // in IC\n vec3 inverseDimensions; // 1/vec3(dimensions)\n mat3 vecISToVCMatrix; // convert from IS to VC without translation\n mat3 vecVCToISMatrix; // convert from VC to IS without translation\n mat4 PCWCMatrix;\n mat4 worldToIndex;\n float diagonalLength; // in VC, this is: length(size)\n\n // ---- Texture settings ----\n\n // Texture shift and scale\n vec4 colorTextureScale;\n vec4 colorTextureShift;\n vec4 opacityTextureScale;\n vec4 opacityTextureShift;\n\n // The heights defined below are the locations for the up to four components\n // of the transfer functions. The transfer functions have a height of (2 *\n // numberOfComponents) pixels so the values are computed to hit the middle of\n // the two rows for that component\n vec4 transferFunctionsSampleHeight;\n\n // ---- Mode specific settings ----\n\n // Independent component default preset settings per component\n vec4 independentComponentMix;\n\n // Additive / average blending mode settings\n vec4 ipScalarRangeMin;\n vec4 ipScalarRangeMax;\n\n // ---- Rendering settings ----\n\n // Lighting\n float ambient;\n float diffuse;\n float specular;\n float specularPower;\n int computeNormalFromOpacity;\n\n // Gradient opacity\n vec4 gradientOpacityScale;\n vec4 gradientOpacityShift;\n vec4 gradientOpacityMin;\n vec4 gradientOpacityMax;\n\n // Volume shadow\n float volumetricScatteringBlending;\n float globalIlluminationReach;\n float anisotropy;\n float anisotropySquared;\n\n // LAO\n int kernelSize;\n int kernelRadius;\n\n // Label outline\n float outlineOpacity;\n};\nuniform Volume volume;\n\nstruct Light {\n vec3 color;\n vec3 positionVC;\n vec3 directionVC; // normalized\n vec3 halfAngleVC;\n vec3 attenuation;\n float exponent;\n float coneAngle;\n int isPositional;\n};\n#if vtkNumberOfLights > 0\n uniform Light lights[vtkNumberOfLights];\n#endif\n\nuniform float vpWidth;\nuniform float vpHeight;\nuniform float vpOffsetX;\nuniform float vpOffsetY;\n\n// Bitmasks for label outline\nconst int MAX_SEGMENT_INDEX = 256; // Define as per expected maximum\n#define MAX_SEGMENTS 256\n#define UINT_SIZE 32\n// We add UINT_SIZE - 1, as we want the ceil of the division instead of the\n// floor\n#define BITMASK_SIZE ((MAX_SEGMENTS + UINT_SIZE - 1) / UINT_SIZE)\nuint labelOutlineBitmasks[BITMASK_SIZE];\n\n// Set the corresponding bit in the bitmask\nvoid setLabelOutlineBit(int segmentIndex) {\n int arrayIndex = segmentIndex / UINT_SIZE;\n int bitIndex = segmentIndex % UINT_SIZE;\n labelOutlineBitmasks[arrayIndex] |= 1u << bitIndex;\n}\n\n// Check if a bit is set in the bitmask\nbool isLabelOutlineBitSet(int segmentIndex) {\n int arrayIndex = segmentIndex / UINT_SIZE;\n int bitIndex = segmentIndex % UINT_SIZE;\n return ((labelOutlineBitmasks[arrayIndex] & (1u << bitIndex)) != 0u);\n}\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//VTK::ClipPlane::Dec\n\n// A random number between 0 and 1 that only depends on the fragment\n// It uses the jtexture, so this random seed repeats by blocks of 32 fragments\n// in screen space\nfloat fragmentSeed;\n\n// sample texture is global\nuniform float sampleDistance;\nuniform float volumeShadowSampleDistance;\n\n// declaration for intermixed geometry\n//VTK::ZBuffer::Dec\n\n//=======================================================================\n// global and custom variables (a temporary section before photorealistics\n// rendering module is complete)\nvec3 rayDirVC;\n\n#define INV4PI 0.0796\n#define EPSILON 0.001\n#define PI 3.1415\n#define PI2 9.8696\n\nvec4 rawSampleTexture(vec3 pos) {\n #ifdef EnabledMultiTexturePerVolume\n vec4 rawSample;\n rawSample[0] = texture(volumeTexture[0], pos)[0];\n #if vtkNumberOfComponents > 1\n rawSample[1] = texture(volumeTexture[1], pos)[0];\n #endif\n #if vtkNumberOfComponents > 2\n rawSample[2] = texture(volumeTexture[2], pos)[0];\n #endif\n #if vtkNumberOfComponents > 3\n rawSample[3] = texture(volumeTexture[3], pos)[0];\n #endif\n return rawSample;\n #else\n return texture(volumeTexture[0], pos);\n #endif\n}\n\nvec4 rawFetchTexture(ivec3 pos) {\n #ifdef EnabledMultiTexturePerVolume\n vec4 rawSample;\n #if vtkNumberOfComponents > 0\n rawSample[0] = texelFetch(volumeTexture[0], pos, 0)[0];\n #endif\n #if vtkNumberOfComponents > 1\n rawSample[1] = texelFetch(volumeTexture[1], pos, 0)[0];\n #endif\n #if vtkNumberOfComponents > 2\n rawSample[2] = texelFetch(volumeTexture[2], pos, 0)[0];\n #endif\n #if vtkNumberOfComponents > 3\n rawSample[3] = texelFetch(volumeTexture[3], pos, 0)[0];\n #endif\n return rawSample;\n #else\n return texelFetch(volumeTexture[0], pos, 0);\n #endif\n}\n\nvec4 getTextureValue(vec3 pos) {\n vec4 tmp = rawSampleTexture(pos);\n\n // Force nearest\n #if defined(vtkComponent0ForceNearest) || \\\n defined(vtkComponent1ForceNearest) || \\\n defined(vtkComponent2ForceNearest) || \\\n defined(vtkComponent3ForceNearest)\n vec3 nearestPos = (floor(pos * vec3(volume.dimensions)) + 0.5) *\n volume.inverseDimensions;\n vec4 nearestValue = rawSampleTexture(nearestPos);\n #ifdef vtkComponent0ForceNearest\n tmp[0] = nearestValue[0];\n #endif\n #ifdef vtkComponent1ForceNearest\n tmp[1] = nearestValue[1];\n #endif\n #ifdef vtkComponent2ForceNearest\n tmp[2] = nearestValue[2];\n #endif\n #ifdef vtkComponent3ForceNearest\n tmp[3] = nearestValue[3];\n #endif\n #endif\n\n // Set alpha when using dependent components\n #ifndef EnabledIndependentComponents\n #if vtkNumberOfComponents == 1\n tmp.a = tmp.r;\n #endif\n #if vtkNumberOfComponents == 2\n tmp.a = tmp.g;\n #endif\n #if vtkNumberOfComponents == 3\n tmp.a = length(tmp.rgb);\n #endif\n #endif\n\n return tmp;\n}\n\n// `height` is usually `volume.transferFunctionsSampleHeight[component]`\n// when using independent component and `0.5` otherwise. Don't move the if\n// statement in these function, as the callers usually already knows if it is\n// using independent component or not\nfloat getOpacityFromTexture(float scalar, int component, float height) {\n float scaledScalar = scalar * volume.opacityTextureScale[component] +\n volume.opacityTextureShift[component];\n return texture2D(opacityTexture, vec2(scaledScalar, height)).r;\n}\nvec3 getColorFromTexture(float scalar, int component, float height) {\n float scaledScalar = scalar * volume.colorTextureScale[component] +\n volume.colorTextureShift[component];\n return texture2D(colorTexture, vec2(scaledScalar, height)).rgb;\n}\n\n//=======================================================================\n// transformation between VC and IS space\n\n// convert vector position from idx to vc\nvec3 posIStoVC(vec3 posIS) {\n return volume.vecISToVCMatrix * posIS + volume.originVC;\n}\n\n// convert vector position from vc to idx\nvec3 posVCtoIS(vec3 posVC) {\n return volume.vecVCToISMatrix * (posVC - volume.originVC);\n}\n\n// Rotate vector to view coordinate\nvec3 vecISToVC(vec3 dirIS) {\n return volume.vecISToVCMatrix * dirIS;\n}\n\n// Rotate vector to idx coordinate\nvec3 vecVCToIS(vec3 dirVC) {\n return volume.vecVCToISMatrix * dirVC;\n}\n\n//=======================================================================\n// Given a normal compute the gradient opacity factors\nfloat computeGradientOpacityFactor(float normalMag, int component) {\n float goscale = volume.gradientOpacityScale[component];\n float goshift = volume.gradientOpacityShift[component];\n float gomin = volume.gradientOpacityMin[component];\n float gomax = volume.gradientOpacityMax[component];\n return clamp(normalMag * goscale + goshift, gomin, gomax);\n}\n\n#ifdef vtkClippingPlanesOn\n bool isPointClipped(vec3 posVC) {\n for (int i = 0; i < clip_numPlanes; ++i) {\n if (dot(vec3(vClipPlaneOrigins[i] - posVC), vClipPlaneNormals[i]) > 0.0) {\n return true;\n }\n }\n return false;\n }\n#endif\n\n//=======================================================================\n// compute the normal and gradient magnitude for a position, uses forward\n// difference\n\n// The output normal is in VC\nvec4 computeDensityNormal(vec3 opacityUCoords[2], float opacityTextureHeight,\n float gradientOpacity, int component) {\n // Pass the scalars through the opacity functions\n vec4 opacityG;\n opacityG.x += getOpacityFromTexture(opacityUCoords[0].x, component,\n opacityTextureHeight);\n opacityG.y += getOpacityFromTexture(opacityUCoords[0].y, component,\n opacityTextureHeight);\n opacityG.z += getOpacityFromTexture(opacityUCoords[0].z, component,\n opacityTextureHeight);\n opacityG.x -= getOpacityFromTexture(opacityUCoords[1].x, component,\n opacityTextureHeight);\n opacityG.y -= getOpacityFromTexture(opacityUCoords[1].y, component,\n opacityTextureHeight);\n opacityG.z -= getOpacityFromTexture(opacityUCoords[1].z, component,\n opacityTextureHeight);\n\n // Divide by spacing and convert to VC\n opacityG.xyz *= gradientOpacity * volume.inverseSpacing;\n opacityG.w = length(opacityG.xyz);\n if (opacityG.w == 0.0) {\n return vec4(0.0);\n }\n\n // Normalize\n opacityG.xyz = normalize(vecISToVC(opacityG.xyz));\n\n return opacityG;\n}\n\n// The output normal is in VC\nvec4 computeNormalForDensity(vec3 posIS, out vec3 scalarInterp[2],\n const int opacityComponent) {\n vec3 offsetedPosIS;\n for (int axis = 0; axis < 3; ++axis) {\n // Positive direction\n offsetedPosIS = posIS;\n offsetedPosIS[axis] += volume.inverseDimensions[axis];\n scalarInterp[0][axis] =\n getTextureValue(offsetedPosIS)[opacityComponent];\n #ifdef vtkClippingPlanesOn\n if (isPointClipped(posIStoVC(offsetedPosIS))) {\n scalarInterp[0][axis] = 0.0;\n }\n #endif\n\n // Negative direction\n offsetedPosIS = posIS;\n offsetedPosIS[axis] -= volume.inverseDimensions[axis];\n scalarInterp[1][axis] =\n getTextureValue(offsetedPosIS)[opacityComponent];\n #ifdef vtkClippingPlanesOn\n if (isPointClipped(posIStoVC(offsetedPosIS))) {\n scalarInterp[1][axis] = 0.0;\n }\n #endif\n }\n\n vec4 result;\n result.xyz = (scalarInterp[0] - scalarInterp[1]) * volume.inverseSpacing;\n result.w = length(result.xyz);\n if (result.w == 0.0) {\n return vec4(0.0);\n }\n result.xyz = normalize(vecISToVC(result.xyz));\n return result;\n}\n\nvec4 fragCoordToPCPos(vec4 fragCoord) {\n return vec4((fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0,\n (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0,\n (fragCoord.z - 0.5) * 2.0, 1.0);\n}\n\nvec4 pcPosToWorldCoord(vec4 pcPos) {\n return volume.PCWCMatrix * pcPos;\n}\n\nvec3 fragCoordToIndexSpace(vec4 fragCoord) {\n vec4 pcPos = fragCoordToPCPos(fragCoord);\n vec4 worldCoord = pcPosToWorldCoord(pcPos);\n vec4 vertex = (worldCoord / worldCoord.w);\n\n vec3 index = (volume.worldToIndex * vertex).xyz;\n\n // half voxel fix for labelmapOutline\n return (index + vec3(0.5)) * volume.inverseDimensions;\n}\n\nvec3 fragCoordToWorld(vec4 fragCoord) {\n vec4 pcPos = fragCoordToPCPos(fragCoord);\n vec4 worldCoord = pcPosToWorldCoord(pcPos);\n return worldCoord.xyz;\n}\n\n//=======================================================================\n// Compute the normals and gradient magnitudes for a position for independent\n// components The output normals are in VC\nmat4 computeMat4Normal(vec3 posIS, vec4 tValue) {\n vec3 xvec = vec3(volume.inverseDimensions.x, 0.0, 0.0);\n vec3 yvec = vec3(0.0, volume.inverseDimensions.y, 0.0);\n vec3 zvec = vec3(0.0, 0.0, volume.inverseDimensions.z);\n\n vec4 distX = getTextureValue(posIS + xvec) - getTextureValue(posIS - xvec);\n vec4 distY = getTextureValue(posIS + yvec) - getTextureValue(posIS - yvec);\n vec4 distZ = getTextureValue(posIS + zvec) - getTextureValue(posIS - zvec);\n\n // divide by spacing\n distX *= 0.5 * volume.inverseSpacing.x;\n distY *= 0.5 * volume.inverseSpacing.y;\n distZ *= 0.5 * volume.inverseSpacing.z;\n\n mat4 result;\n\n // optionally compute the 1st component\n #if vtkNumberOfComponents > 0 && !defined(vtkComponent0Proportional)\n {\n const int component = 0;\n vec3 normal = vec3(distX[component], distY[component], distZ[component]);\n float normalLength = length(normal);\n if (normalLength > 0.0) {\n normal = normalize(vecISToVC(normal));\n }\n result[component] = vec4(normal, normalLength);\n }\n #endif\n\n // optionally compute the 2nd component\n #if vtkNumberOfComponents > 1 && !defined(vtkComponent1Proportional)\n {\n const int component = 1;\n vec3 normal = vec3(distX[component], distY[component], distZ[component]);\n float normalLength = length(normal);\n if (normalLength > 0.0) {\n normal = normalize(vecISToVC(normal));\n }\n result[component] = vec4(normal, normalLength);\n }\n #endif\n\n // optionally compute the 3rd component\n #if vtkNumberOfComponents > 2 && !defined(vtkComponent2Proportional)\n {\n const int component = 2;\n vec3 normal = vec3(distX[component], distY[component], distZ[component]);\n float normalLength = length(normal);\n if (normalLength > 0.0) {\n normal = normalize(vecISToVC(normal));\n }\n result[component] = vec4(normal, normalLength);\n }\n #endif\n\n // optionally compute the 4th component\n #if vtkNumberOfComponents > 3 && !defined(vtkComponent3Proportional)\n {\n const int component = 3;\n vec3 normal = vec3(distX[component], distY[component], distZ[component]);\n float normalLength = length(normal);\n if (normalLength > 0.0) {\n normal = normalize(vecISToVC(normal));\n }\n result[component] = vec4(normal, normalLength);\n }\n #endif\n\n return result;\n}\n\n//=======================================================================\n// global shadow - secondary ray\n\n// henyey greenstein phase function\nfloat phaseFunction(float cos_angle) {\n // divide by 2.0 instead of 4pi to increase intensity\n float anisotropy = volume.anisotropy;\n if (abs(anisotropy) <= EPSILON) {\n // isotropic scatter returns 0.5 instead of 1/4pi to increase intensity\n return 0.5;\n }\n float anisotropy2 = volume.anisotropySquared;\n return ((1.0 - anisotropy2) /\n pow(1.0 + anisotropy2 - 2.0 * anisotropy * cos_angle, 1.5)) /\n 2.0;\n}\n\n// Compute the two intersection distances of the ray with the volume in VC\n// The entry point is `rayOriginVC + distanceMin * rayDirVC` and the exit point\n// is `rayOriginVC + distanceMax * rayDirVC` If distanceMin < distanceMax, the\n// volume is not intersected The ray origin is inside the box when distanceMin <\n// 0.0 < distanceMax\nvec2 rayIntersectVolumeDistances(vec3 rayOriginVC, vec3 rayDirVC) {\n // Compute origin and direction in IS\n vec3 rayOriginIS = posVCtoIS(rayOriginVC);\n vec3 rayDirIS = vecVCToIS(rayDirVC);\n // Don't check for infinity as the min/max combination afterward will always\n // find an intersection before infinity\n vec3 invDir = 1.0 / rayDirIS;\n\n // We have: bound = origin + t * dir\n // So: t = (1/dir) * (bound - origin)\n vec3 distancesTo0 = invDir * (vec3(0.0) - rayOriginIS);\n vec3 distancesTo1 = invDir * (vec3(1.0) - rayOriginIS);\n // Min and max distances to plane intersection per plane\n vec3 dMinPerAxis = min(distancesTo0, distancesTo1);\n vec3 dMaxPerAxis = max(distancesTo0, distancesTo1);\n // Overall first and last intersection\n float distanceMin = max(dMinPerAxis.x, max(dMinPerAxis.y, dMinPerAxis.z));\n float distanceMax = min(dMaxPerAxis.x, min(dMaxPerAxis.y, dMaxPerAxis.z));\n return vec2(distanceMin, distanceMax);\n}\n\n//=======================================================================\n// local ambient occlusion\n#if vtkMaxLaoKernelSize > 0\n\n // Return a random point on the unit sphere\n vec3 sampleDirectionUniform(int rayIndex) {\n // Each ray of each fragment should be different, two sources of randomness\n // are used. Only depends on ray index\n vec2 rayRandomness = kernelSample[rayIndex];\n // Only depends on fragment\n float fragmentRandomness = fragmentSeed;\n // Merge both source of randomness in a single uniform random variable using\n // the formula (x+y < 1 ? x+y : x+y-1). The simpler formula (x+y)/2 doesn't\n // result in a uniform distribution\n vec2 mergedRandom = rayRandomness + vec2(fragmentRandomness);\n mergedRandom -= vec2(greaterThanEqual(mergedRandom, vec2(1.0)));\n\n // Insipred by:\n // https://karthikkaranth.me/blog/generating-random-points-in-a-sphere/#better-choice-of-spherical-coordinates\n float u = mergedRandom[0];\n float v = mergedRandom[1];\n float theta = u * 2.0 * PI;\n float phi = acos(2.0 * v - 1.0);\n float sinTheta = sin(theta);\n float cosTheta = cos(theta);\n float sinPhi = sin(phi);\n float cosPhi = cos(phi);\n return vec3(sinPhi * cosTheta, sinPhi * sinTheta, cosPhi);\n }\n\n float computeLAO(vec3 posVC, vec4 normalVC, float originalOpacity) {\n // apply LAO only at selected locations, otherwise return full brightness\n if (normalVC.w <= 0.0 || originalOpacity <= 0.05) {\n return 1.0;\n }\n\n #ifdef EnabledGradientOpacity\n float gradientOpacityFactor = computeGradientOpacityFactor(normalVC.w, 0);\n #endif\n\n float visibilitySum = 0.0;\n float weightSum = 0.0;\n for (int i = 0; i < volume.kernelSize; i++) {\n // Only sample on an hemisphere around the normalVC.xyz axis, so\n // normalDotRay should be negative\n vec3 rayDirectionVC = sampleDirectionUniform(i);\n float normalDotRay = dot(normalVC.xyz, rayDirectionVC);\n if (normalDotRay > 0.0) {\n // Flip rayDirectionVC when it is in the wrong hemisphere\n rayDirectionVC = -rayDirectionVC;\n normalDotRay = -normalDotRay;\n }\n\n vec3 currPosIS = posVCtoIS(posVC);\n float visibility = 1.0;\n vec3 randomDirStepIS = vecVCToIS(rayDirectionVC * sampleDistance);\n for (int j = 0; j < volume.kernelRadius; j++) {\n currPosIS += randomDirStepIS;\n // If out of the volume, we are done\n if (any(lessThan(currPosIS, vec3(0.0))) ||\n any(greaterThan(currPosIS, vec3(1.0)))) {\n break;\n }\n float opacity = getOpacityFromTexture(getTextureValue(currPosIS).r, 0, 0.5);\n #ifdef EnabledGradientOpacity\n opacity *= gradientOpacityFactor;\n #endif\n visibility *= 1.0 - opacity;\n // If visibility is less than EPSILON, consider it to be 0\n if (visibility < EPSILON) {\n visibility = 0.0;\n break;\n }\n }\n float rayWeight = -normalDotRay;\n visibilitySum += visibility * rayWeight;\n weightSum += rayWeight;\n }\n\n // If no sample, LAO factor is one\n if (weightSum == 0.0) {\n return 1.0;\n }\n\n // LAO factor is the average visibility:\n // - visibility low => ambient low\n // - visibility high => ambient high\n float lao = visibilitySum / weightSum;\n\n // Reduce variance by clamping\n return clamp(lao, 0.3, 1.0);\n }\n#endif\n\n//=======================================================================\n// Volume shadows\n#if vtkNumberOfLights > 0\n\n // Non-memoised version\n float computeVolumeShadowWithoutCache(vec3 posVC, vec3 lightDirNormVC) {\n // modify sample distance with a random number between 1.5 and 3.0\n float rayStepLength =\n volumeShadowSampleDistance * mix(1.5, 3.0, fragmentSeed);\n\n // in case the first sample near surface has a very tiled light ray, we need\n // to offset start position\n vec3 initialPosVC = posVC + rayStepLength * lightDirNormVC;\n\n #ifdef vtkClippingPlanesOn\n float clippingPlanesMaxDistance = infinity;\n for (int i = 0; i < clip_numPlanes; ++i) {\n // Find distance of intersection with the plane\n // Points are clipped when:\n // dot(planeOrigin - (rayOrigin + distance * rayDirection), planeNormal) > 0\n // This is equivalent to:\n // dot(planeOrigin - rayOrigin, planeNormal) - distance * dot(rayDirection,\n // planeNormal) > 0.0\n // We precompute the dot products, so we clip ray points when:\n // dotOrigin - distance * dotDirection > 0.0\n float dotOrigin =\n dot(vClipPlaneOrigins[i] - initialPosVC, vClipPlaneNormals[i]);\n if (dotOrigin > 0.0) {\n // The initialPosVC is clipped by this plane\n return 1.0;\n }\n float dotDirection = dot(lightDirNormVC, vClipPlaneNormals[i]);\n if (dotDirection < 0.0) {\n // We only hit the plane if dotDirection is negative, as (distance is\n // positive)\n float intersectionDistance =\n dotOrigin / dotDirection; // negative divided by negative => positive\n clippingPlanesMaxDistance =\n min(clippingPlanesMaxDistance, intersectionDistance);\n }\n }\n #endif\n\n vec2 intersectionDistances =\n rayIntersectVolumeDistances(initialPosVC, lightDirNormVC);\n\n if (intersectionDistances[1] <= intersectionDistances[0] ||\n intersectionDistances[1] <= 0.0) {\n // Volume not hit or behind the ray\n return 1.0;\n }\n\n // When globalIlluminationReach is 0, no sample at all\n // When globalIlluminationReach is 1, the ray will go through the whole\n // volume\n float maxTravelDistance = mix(0.0, volume.diagonalLength,\n volume.globalIlluminationReach);\n float startDistance = max(intersectionDistances[0], 0.0);\n float endDistance = min(intersectionDistances[1], startDistance + maxTravelDistance);\n #ifdef vtkClippingPlanesOn\n endDistance = min(endDistance, clippingPlanesMaxDistance);\n #endif\n if (endDistance - startDistance < 0.0) {\n return 1.0;\n }\n\n // These two variables are used to compute posIS, without having to call\n // VCtoIS at each step\n vec3 initialPosIS = posVCtoIS(initialPosVC);\n // The light dir is scaled and rotated, but not translated, as it is a\n // vector (w = 0)\n vec3 scaledLightDirIS = vecVCToIS(lightDirNormVC);\n\n float shadow = 1.0;\n for (float currentDistance = startDistance; currentDistance <= endDistance;\n currentDistance += rayStepLength) {\n vec3 posIS = initialPosIS + currentDistance * scaledLightDirIS;\n vec4 scalar = getTextureValue(posIS);\n float opacity = getOpacityFromTexture(scalar.r, 0, 0.5);\n #if defined(EnabledGradientOpacity) && !defined(EnabledIndependentComponents)\n vec3 scalarInterp[2];\n vec4 normal = computeNormalForDensity(posIS, scalarInterp, 3);\n float opacityFactor = computeGradientOpacityFactor(normal.w, 0);\n opacity *= opacityFactor;\n #endif\n shadow *= 1.0 - opacity;\n\n // Early termination if shadow coeff is near 0.0\n if (shadow < EPSILON) {\n return 0.0;\n }\n }\n return shadow;\n }\n\n // Some cache for volume shadows\n struct {\n vec3 posVC;\n float shadow;\n } cachedShadows[vtkNumberOfLights];\n\n // Memoised version\n float computeVolumeShadow(vec3 posVC, vec3 lightDirNormVC, int lightIdx) {\n if (posVC == cachedShadows[lightIdx].posVC) {\n return cachedShadows[lightIdx].shadow;\n }\n float shadow = computeVolumeShadowWithoutCache(posVC, lightDirNormVC);\n cachedShadows[lightIdx].posVC = posVC;\n cachedShadows[lightIdx].shadow = shadow;\n return shadow;\n }\n\n#endif\n\n//=======================================================================\n// surface light contribution\n#if vtkNumberOfLights > 0\n vec3 applyLighting(vec3 tColor, vec4 normalVC) {\n vec3 diffuse = vec3(0.0, 0.0, 0.0);\n vec3 specular = vec3(0.0, 0.0, 0.0);\n for (int lightIdx = 0; lightIdx < vtkNumberOfLights; lightIdx++) {\n float df = dot(normalVC.xyz, lights[lightIdx].directionVC);\n if (df > 0.0) {\n diffuse += df * lights[lightIdx].color;\n float sf = dot(normalVC.xyz, -lights[lightIdx].halfAngleVC);\n if (sf > 0.0) {\n specular += pow(sf, volume.specularPower) * lights[lightIdx].color;\n }\n }\n }\n return tColor * (diffuse * volume.diffuse + volume.ambient) +\n specular * volume.specular;\n }\n\n vec3 applySurfaceShadowLighting(vec3 tColor, float alpha, vec3 posVC,\n vec4 normalVC) {\n // everything in VC\n vec3 diffuse = vec3(0.0);\n vec3 specular = vec3(0.0);\n for (int ligthIdx = 0; ligthIdx < vtkNumberOfLights; ligthIdx++) {\n vec3 vertLightDirection;\n float attenuation;\n if (lights[ligthIdx].isPositional == 1) {\n vertLightDirection = posVC - lights[ligthIdx].positionVC;\n float lightDistance = length(vertLightDirection);\n // Normalize with precomputed length\n vertLightDirection = vertLightDirection / lightDistance;\n // Base attenuation\n vec3 attenuationPolynom = lights[ligthIdx].attenuation;\n attenuation =\n 1.0 / (attenuationPolynom[0] +\n lightDistance * (attenuationPolynom[1] +\n lightDistance * attenuationPolynom[2]));\n // Cone attenuation\n float coneDot = dot(vertLightDirection, lights[ligthIdx].directionVC);\n // Per OpenGL standard cone angle is 90 or less for a spot light\n if (lights[ligthIdx].coneAngle <= 90.0) {\n if (coneDot >= cos(radians(lights[ligthIdx].coneAngle))) {\n // Inside the cone\n attenuation *= pow(coneDot, lights[ligthIdx].exponent);\n } else {\n // Outside the cone\n attenuation = 0.0;\n }\n }\n } else {\n vertLightDirection = lights[ligthIdx].directionVC;\n attenuation = 1.0;\n }\n\n float ndotL = dot(normalVC.xyz, vertLightDirection);\n if (ndotL < 0.0 && twoSidedLighting == 1) {\n ndotL = -ndotL;\n }\n if (ndotL > 0.0) {\n // Diffuse\n diffuse += ndotL * attenuation * lights[ligthIdx].color;\n // Specular\n float vdotR =\n dot(-rayDirVC, normalize(vertLightDirection - 2.0 * ndotL * normalVC.xyz));\n if (vdotR > 0.0) {\n specular += pow(vdotR, volume.specularPower) * attenuation *\n lights[ligthIdx].color;\n }\n }\n }\n #if vtkMaxLaoKernelSize > 0\n float laoFactor = computeLAO(posVC, normalVC, alpha);\n #else\n const float laoFactor = 1.0;\n #endif\n return tColor * (diffuse * volume.diffuse +\n volume.ambient * laoFactor) +\n specular * volume.specular;\n }\n\n vec3 applyVolumeShadowLighting(vec3 tColor, vec3 posVC) {\n // Here we have no effect of cones and no attenuation\n vec3 diffuse = vec3(0.0);\n for (int lightIdx = 0; lightIdx < vtkNumberOfLights; lightIdx++) {\n vec3 lightDirVC = lights[lightIdx].isPositional == 1\n ? normalize(lights[lightIdx].positionVC - posVC)\n : -lights[lightIdx].directionVC;\n float shadowCoeff = computeVolumeShadow(posVC, lightDirVC, lightIdx);\n float phaseAttenuation = phaseFunction(dot(rayDirVC, lightDirVC));\n diffuse += phaseAttenuation * shadowCoeff * lights[lightIdx].color;\n }\n return tColor * (diffuse * volume.diffuse + volume.ambient);\n }\n#endif\n\n// LAO of surface shadows and volume shadows only work with dependent components\nvec3 applyAllLightning(vec3 tColor, float alpha, vec3 posVC,\n vec4 surfaceNormalVC) {\n #if vtkNumberOfLights > 0\n // 0 <= volCoeff < EPSILON => only surface shadows\n // EPSILON <= volCoeff < 1 - EPSILON => mix of surface and volume shadows\n // 1 - EPSILON <= volCoeff => only volume shadows\n float volCoeff = volume.volumetricScatteringBlending *\n (1.0 - alpha / 2.0) *\n (1.0 - atan(surfaceNormalVC.w) * INV4PI);\n\n // Compute surface lighting if needed\n vec3 surfaceShadedColor = tColor;\n #ifdef EnableSurfaceLighting\n if (volCoeff < 1.0 - EPSILON) {\n surfaceShadedColor =\n applySurfaceShadowLighting(tColor, alpha, posVC, surfaceNormalVC);\n }\n #endif\n\n // Compute volume lighting if needed\n vec3 volumeShadedColor = tColor;\n #ifdef EnableVolumeLighting\n if (volCoeff >= EPSILON) {\n volumeShadedColor = applyVolumeShadowLighting(tColor, posVC);\n }\n #endif\n\n // Return the right mix\n if (volCoeff < EPSILON) {\n // Surface shadows\n return surfaceShadedColor;\n }\n if (volCoeff >= 1.0 - EPSILON) {\n // Volume shadows\n return volumeShadedColor;\n }\n // Mix of surface and volume shadows\n return mix(surfaceShadedColor, volumeShadedColor, volCoeff);\n #endif\n return tColor;\n}\n\nvec4 getColorForLabelOutline() {\n vec3 centerPosIS =\n fragCoordToIndexSpace(gl_FragCoord); // pos in texture space\n vec4 centerValue = getTextureValue(centerPosIS);\n bool pixelOnBorder = false;\n vec4 tColor = vec4(getColorFromTexture(centerValue.r, 0, 0.5),\n getOpacityFromTexture(centerValue.r, 0, 0.5));\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 =\n texture2D(labelOutlineThicknessTexture, vec2(textureCoordinate, 0.5)).r;\n int actualThickness = int(textureValue * 255.0);\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\n // invisible. 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 =\n vec4(gl_FragCoord.x + float(i), 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 = volume.outlineOpacity;\n }\n\n return tColor;\n}\n\nvec4 getColorForAdditivePreset(vec4 tValue, vec3 posVC, vec3 posIS) {\n // compute normals\n mat4 normalMat = computeMat4Normal(posIS, tValue);\n vec4 normalLights[2];\n normalLights[0] = normalMat[0];\n normalLights[1] = normalMat[1];\n #if vtkNumberOfLights > 0\n if (volume.computeNormalFromOpacity == 1) {\n for (int component = 0; component < 2; ++component) {\n vec3 scalarInterp[2];\n float height = volume.transferFunctionsSampleHeight[component];\n computeNormalForDensity(posIS, scalarInterp, component);\n normalLights[component] =\n computeDensityNormal(scalarInterp, height, 1.0, component);\n }\n }\n #endif\n\n // compute opacities\n float opacities[2];\n opacities[0] = getOpacityFromTexture(\n tValue[0], 0, volume.transferFunctionsSampleHeight[0]);\n opacities[1] = getOpacityFromTexture(\n tValue[1], 1, volume.transferFunctionsSampleHeight[1]);\n #ifdef EnabledGradientOpacity\n for (int component = 0; component < 2; ++component) {\n opacities[component] *=\n computeGradientOpacityFactor(normalMat[component].a, component);\n }\n #endif\n float opacitySum = opacities[0] + opacities[1];\n if (opacitySum <= 0.0) {\n return vec4(0.0);\n }\n\n // mix the colors and opacities\n vec3 colors[2];\n for (int component = 0; component < 2; ++component) {\n float sampleHeight = volume.transferFunctionsSampleHeight[component];\n vec3 color = getColorFromTexture(tValue[component], component, sampleHeight);\n color = applyAllLightning(color, opacities[component], posVC,\n normalLights[component]);\n colors[component] = color;\n }\n vec3 mixedColor =\n (opacities[0] * colors[0] + opacities[1] * colors[1]) / opacitySum;\n return vec4(mixedColor, min(1.0, opacitySum));\n}\n\nvec4 getColorForColorizePreset(vec4 tValue, vec3 posVC, vec3 posIS) {\n // compute normals\n mat4 normalMat = computeMat4Normal(posIS, tValue);\n vec4 normalLight = normalMat[0];\n #if vtkNumberOfLights > 0\n if (volume.computeNormalFromOpacity == 1) {\n vec3 scalarInterp[2];\n float height = volume.transferFunctionsSampleHeight[0];\n computeNormalForDensity(posIS, scalarInterp, 0);\n normalLight = computeDensityNormal(scalarInterp, height, 1.0, 0);\n }\n #endif\n\n // compute opacities\n float opacity = getOpacityFromTexture(\n tValue[0], 0, volume.transferFunctionsSampleHeight[0]);\n #ifdef EnabledGradientOpacity\n opacity *= computeGradientOpacityFactor(normalMat[0].a, 0);\n #endif\n\n // colorizing component\n vec3 colorizingColor = getColorFromTexture(\n tValue[0], 1, volume.transferFunctionsSampleHeight[1]);\n float colorizingOpacity = getOpacityFromTexture(\n tValue[1], 1, volume.transferFunctionsSampleHeight[1]);\n\n // mix the colors and opacities\n vec3 color =\n getColorFromTexture(tValue[0], 0,\n volume.transferFunctionsSampleHeight[0]) *\n mix(vec3(1.0), colorizingColor, colorizingOpacity);\n color = applyAllLightning(color, opacity, posVC, normalLight);\n return vec4(color, opacity);\n}\n\nvec4 getColorForDefaultIndependentPreset(vec4 tValue, vec3 posIS) {\n\n // compute the normal vectors as needed\n #if defined(EnabledGradientOpacity) || vtkNumberOfLights > 0\n mat4 normalMat = computeMat4Normal(posIS, tValue);\n #endif\n\n // process color and opacity for each component\n // initial value of alpha is determined by wether the first component is\n // proportional or not\n #if defined(vtkComponent0Proportional)\n // when it is proportional, it starts at 1 (neutral for multiplications)\n float alpha = 1.0;\n #else\n // when it is not proportional, it starts at 0 (neutral for additions)\n float alpha = 0.0;\n #endif\n\n vec3 mixedColor = vec3(0.0);\n #if vtkNumberOfComponents > 0\n {\n const int component = 0;\n vec3 color = getColorFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n float opacity = getOpacityFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n #if !defined(vtkComponent0Proportional)\n float alphaContribution = volume.independentComponentMix[component] * opacity;\n #ifdef EnabledGradientOpacity\n alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component);\n #endif\n alpha += alphaContribution;\n #if vtkNumberOfLights > 0\n color = applyLighting(color, normalMat[component]);\n #endif\n #else\n color *= opacity;\n alpha *= mix(opacity, 1.0,\n (1.0 - volume.independentComponentMix[component]));\n #endif\n mixedColor += volume.independentComponentMix[component] * color;\n }\n #endif\n #if vtkNumberOfComponents > 1\n {\n const int component = 1;\n vec3 color = getColorFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n float opacity = getOpacityFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n #if !defined(vtkComponent1Proportional)\n float alphaContribution = volume.independentComponentMix[component] * opacity;\n #ifdef EnabledGradientOpacity\n alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component);\n #endif\n alpha += alphaContribution;\n #if vtkNumberOfLights > 0\n color = applyLighting(color, normalMat[component]);\n #endif\n #else\n color *= opacity;\n alpha *= mix(opacity, 1.0,\n (1.0 - volume.independentComponentMix[component]));\n #endif\n mixedColor += volume.independentComponentMix[component] * color;\n }\n #endif\n #if vtkNumberOfComponents > 2\n {\n const int component = 2;\n vec3 color = getColorFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n float opacity = getOpacityFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n #if !defined(vtkComponent2Proportional)\n float alphaContribution = volume.independentComponentMix[component] * opacity;\n #ifdef EnabledGradientOpacity\n alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component);\n #endif\n alpha += alphaContribution;\n #if vtkNumberOfLights > 0\n color = applyLighting(color, normalMat[component]);\n #endif\n #else\n color *= opacity;\n alpha *= mix(opacity, 1.0,\n (1.0 - volume.independentComponentMix[component]));\n #endif\n mixedColor += volume.independentComponentMix[component] * color;\n }\n #endif\n #if vtkNumberOfComponents > 3\n {\n const int component = 3;\n vec3 color = getColorFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n float opacity = getOpacityFromTexture(\n tValue[component], component,\n volume.transferFunctionsSampleHeight[component]);\n #if !defined(vtkComponent3Proportional)\n float alphaContribution = volume.independentComponentMix[component] * opacity;\n #ifdef EnabledGradientOpacity\n alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component);\n #endif\n alpha += alphaContribution;\n #if vtkNumberOfLights > 0\n color = applyLighting(color, normalMat[component]);\n #endif\n #else\n color *= opacity;\n alpha *= mix(opacity, 1.0,\n (1.0 - volume.independentComponentMix[component]));\n #endif\n mixedColor += volume.independentComponentMix[component] * color;\n }\n #endif\n\n return vec4(mixedColor, alpha);\n}\n\nvec4 getColorForDependentComponents(vec4 tValue, vec3 posVC, vec3 posIS) {\n #if defined(EnabledGradientOpacity) || vtkNumberOfLights > 0\n // use component 3 of the opacity texture as getTextureValue() sets alpha to\n // the opacity value\n vec3 scalarInterp[2];\n vec4 normal0 = computeNormalForDensity(posIS, scalarInterp, 3);\n float gradientOpacity = computeGradientOpacityFactor(normal0.a, 0);\n #endif\n\n // get color and opacity\n #if vtkNumberOfComponents == 1\n vec3 tColor = getColorFromTexture(tValue.r, 0, 0.5);\n float alpha = getOpacityFromTexture(tValue.r, 0, 0.5);\n #endif\n #if vtkNumberOfComponents == 2\n vec3 tColor = vec3(tValue.r * volume.colorTextureScale[0] +\n volume.colorTextureShift[0]);\n float alpha = getOpacityFromTexture(tValue.a, 1, 0.5);\n #endif\n #if vtkNumberOfComponents == 3\n vec3 tColor = tValue.rgb * volume.colorTextureScale.rgb +\n volume.colorTextureShift.rgb;\n float alpha = getOpacityFromTexture(tValue.a, 0, 0.5);\n #endif\n #if vtkNumberOfComponents == 4\n vec3 tColor = tValue.rgb * volume.colorTextureScale.rgb +\n volume.colorTextureShift.rgb;\n float alpha = getOpacityFromTexture(tValue.a, 3, 0.5);\n #endif\n\n // Apply gradient opacity\n #if defined(EnabledGradientOpacity)\n alpha *= gradientOpacity;\n #endif\n\n #if vtkNumberOfComponents == 1\n if (alpha < EPSILON) {\n return vec4(0.0);\n }\n #endif\n\n // lighting\n #if vtkNumberOfLights > 0\n vec4 normalLight;\n if (volume.computeNormalFromOpacity == 1) {\n if (normal0[3] != 0.0) {\n normalLight =\n computeDensityNormal(scalarInterp, 0.5, gradientOpacity, 0);\n if (normalLight[3] == 0.0) {\n normalLight = normal0;\n }\n }\n } else {\n normalLight = normal0;\n }\n tColor = applyAllLightning(tColor, alpha, posVC, normalLight);\n #endif\n\n return vec4(tColor, alpha);\n}\n\nvec4 getColorForValue(vec4 tValue, vec3 posVC, vec3 posIS) {\n #ifdef EnableColorForValueFunctionId0\n return getColorForDependentComponents(tValue, posVC, posIS);\n #endif\n\n #ifdef EnableColorForValueFunctionId1\n return getColorForAdditivePreset(tValue, posVC, posIS);\n #endif\n\n #ifdef EnableColorForValueFunctionId2\n return getColorForColorizePreset(tValue, posVC, posIS);\n #endif\n\n #ifdef EnableColorForValueFunctionId3\n /*\n * Mix the color information from all the independent components to get a\n * single rgba output. See other shader functions like\n * `getColorForAdditivePreset` to learn how to create a custom color mix.\n * The custom color mix should return a value, but if it doesn't, it will\n * fallback on the default shading\n */\n //VTK::CustomColorMix\n #endif\n\n #if defined(EnableColorForValueFunctionId4) || defined(EnableColorForValueFunctionId3)\n return getColorForDefaultIndependentPreset(tValue, posIS);\n #endif\n\n #ifdef EnableColorForValueFunctionId5\n return getColorForLabelOutline();\n #endif\n}\n\nbool valueWithinScalarRange(vec4 val) {\n #if vtkNumberOfComponents > 1 && !defined(EnabledIndependentComponents)\n return false;\n #endif\n vec4 rangeMin = volume.ipScalarRangeMin;\n vec4 rangeMax = volume.ipScalarRangeMax;\n for (int component = 0; component < vtkNumberOfComponents; ++component) {\n if (val[component] < rangeMin[component] ||\n rangeMax[component] < val[component]) {\n return false;\n }\n }\n return true;\n}\n\n#if vtkBlendMode == LABELMAP_EDGE_PROJECTION_BLEND\n bool checkOnEdgeForNeighbor(int xFragmentOffset, int yFragmentOffset,\n int segmentIndex, vec3 stepIS) {\n vec3 volumeDimensions = vec3(volume.dimensions);\n vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(xFragmentOffset),\n gl_FragCoord.y + float(yFragmentOffset),\n gl_FragCoord.z, gl_FragCoord.w);\n vec3 originalNeighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);\n\n vec3 neighborPosIS = originalNeighborPosIS;\n for (int k = 0; k < vtkMaximumNumberOfSamples / 2; ++k) {\n ivec3 texCoord = ivec3(neighborPosIS * volumeDimensions);\n vec4 texValue = rawFetchTexture(texCoord);\n if (int(texValue.g) == segmentIndex) {\n // not on edge\n return false;\n }\n neighborPosIS += stepIS;\n }\n\n neighborPosIS = originalNeighborPosIS;\n for (int k = 0; k < vtkMaximumNumberOfSamples / 2; ++k) {\n ivec3 texCoord = ivec3(neighborPosIS * volumeDimensions);\n vec4 texValue = rawFetchTexture(texCoord);\n if (int(texValue.g) == segmentIndex) {\n // not on edge\n return false;\n }\n neighborPosIS -= stepIS;\n }\n\n // onedge\n float sampleHeight = volume.transferFunctionsSampleHeight[1];\n vec3 tColorSegment =\n getColorFromTexture(float(segmentIndex), 1, sampleHeight);\n float pwfValueSegment =\n getOpacityFromTexture(float(segmentIndex), 1, sampleHeight);\n gl_FragData[0] = vec4(tColorSegment, pwfValueSegment);\n return true;\n }\n#endif\n\nvec4 getColorAtPos(vec3 posVC) {\n vec3 posIS = posVCtoIS(posVC);\n vec4 texValue = getTextureValue(posIS);\n return getColorForValue(texValue, posVC, posIS);\n}\n\n//=======================================================================\n// Apply the specified blend mode operation along the ray's path.\n//\nvoid applyBlend(vec3 rayOriginVC, vec3 rayDirVC, float minDistance,\n float maxDistance) {\n // start slightly inside and apply some jitter\n vec3 stepVC = rayDirVC * sampleDistance;\n float raySteps = (maxDistance - minDistance) / sampleDistance;\n\n // Avoid 0.0 jitter\n float jitter = 0.01 + 0.99 * fragmentSeed;\n\n #if vtkBlendMode == COMPOSITE_BLEND\n // now map through opacity and color\n vec3 firstPosVC = rayOriginVC + minDistance * rayDirVC;\n vec4 firstColor = getColorAtPos(firstPosVC);\n\n // handle very thin volumes\n if (raySteps <= 1.0) {\n firstColor.a = 1.0 - pow(1.0 - firstColor.a, raySteps);\n gl_FragData[0] = firstColor;\n return;\n }\n\n // first color only counts for `jitter` factor of the step\n firstColor.a = 1.0 - pow(1.0 - firstColor.a, jitter);\n vec4 color = vec4(firstColor.rgb * firstColor.a, firstColor.a);\n vec3 posVC = firstPosVC + jitter * stepVC;\n float stepsTraveled = jitter;\n\n for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) {\n break;\n }\n vec4 tColor = getColorAtPos(posVC);\n\n color = color + vec4(tColor.rgb * tColor.a, tColor.a) * (1.0 - color.a);\n stepsTraveled++;\n posVC += stepVC;\n if (color.a > 0.99) {\n color.a = 1.0;\n break;\n }\n }\n\n if (color.a < 0.99 && (raySteps - stepsTraveled) > 0.0) {\n vec3 endPosVC = rayOriginVC + maxDistance * rayDirVC;\n vec4 tColor = getColorAtPos(endPosVC);\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\n #if vtkBlendMode == MAXIMUM_INTENSITY_BLEND || \\\n vtkBlendMode == 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 == MAXIMUM_INTENSITY_BLEND\n #define OP max\n #else\n #define OP min\n #endif\n\n vec3 posVC = rayOriginVC + minDistance * rayDirVC;\n float stepsTraveled = 0.0;\n\n // Find a value to initialize the selected variables\n vec4 selectedValue;\n vec3 selectedPosVC;\n vec3 selectedPosIS;\n {\n vec3 posIS = posVCtoIS(posVC);\n selectedValue = getTextureValue(posIS);\n selectedPosVC = posVC;\n selectedPosIS = posIS;\n }\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 gl_FragData[0] = getColorForValue(selectedValue, selectedPosVC, selectedPosIS);\n return;\n }\n\n posVC += jitter * stepVC;\n stepsTraveled += jitter;\n\n // Sample along the ray until vtkMaximumNumberOfSamples,\n // ending slightly inside the total distance\n for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) {\n break;\n }\n\n // Get selected values\n vec3 posIS = posVCtoIS(posVC);\n vec4 previousSelectedValue = selectedValue;\n vec4 currentValue = getTextureValue(posIS);\n selectedValue = OP(selectedValue, currentValue);\n if (previousSelectedValue != selectedValue) {\n selectedPosVC = posVC;\n selectedPosIS = posIS;\n }\n\n // Otherwise, continue along the ray\n stepsTraveled++;\n posVC += stepVC;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posVC = rayOriginVC + maxDistance * rayDirVC;\n {\n vec3 posIS = posVCtoIS(posVC);\n vec4 previousSelectedValue = selectedValue;\n vec4 currentValue = getTextureValue(posIS);\n selectedValue = OP(selectedValue, currentValue);\n if (previousSelectedValue != selectedValue) {\n selectedPosVC = posVC;\n selectedPosIS = posIS;\n }\n }\n\n gl_FragData[0] = getColorForValue(selectedValue, selectedPosVC, selectedPosIS);\n #endif\n\n #if vtkBlendMode == ADDITIVE_INTENSITY_BLEND || \\\n vtkBlendMode == AVERAGE_INTENSITY_BLEND\n vec4 sum = vec4(0.);\n #if vtkBlendMode == AVERAGE_INTENSITY_BLEND\n float totalWeight = 0.0;\n #endif\n vec3 posVC = rayOriginVC + minDistance * rayDirVC;\n float stepsTraveled = 0.0;\n\n vec3 posIS = posVCtoIS(posVC);\n vec4 value = getTextureValue(posIS);\n\n if (raySteps <= 1.0) {\n gl_FragData[0] = getColorForValue(value * raySteps, posVC, posIS);\n return;\n }\n\n if (valueWithinScalarRange(value)) {\n sum += value * jitter;\n #if vtkBlendMode == AVERAGE_INTENSITY_BLEND\n totalWeight += jitter;\n #endif\n }\n posVC += jitter * stepVC;\n stepsTraveled += jitter;\n\n // Sample along the ray until vtkMaximumNumberOfSamples,\n // ending slightly inside the total distance\n for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) {\n break;\n }\n\n posIS = posVCtoIS(posVC);\n value = getTextureValue(posIS);\n // One can control the scalar range by setting the AverageIPScalarRange to\n // disregard scalar values, not in the range of interest, from the average\n // computation. 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(value)) {\n sum += value;\n #if vtkBlendMode == AVERAGE_INTENSITY_BLEND\n totalWeight++;\n #endif\n }\n\n stepsTraveled++;\n posVC += stepVC;\n }\n\n // Perform the last step along the ray using the\n // residual distance\n posVC = rayOriginVC + maxDistance * rayDirVC;\n posIS = posVCtoIS(posVC);\n value = getTextureValue(posIS);\n if (valueWithinScalarRange(value)) {\n sum += value;\n #if vtkBlendMode == AVERAGE_INTENSITY_BLEND\n totalWeight += raySteps - stepsTraveled;\n #endif\n }\n\n #if vtkBlendMode == AVERAGE_INTENSITY_BLEND\n sum /= vec4(totalWeight, totalWeight, totalWeight, 1.0);\n #endif\n\n gl_FragData[0] = getColorForValue(sum, posVC, posIS);\n #endif\n\n #if vtkBlendMode == RADON_TRANSFORM_BLEND\n float normalizedRayIntensity = 1.0;\n vec3 posVC = rayOriginVC + minDistance * rayDirVC;\n float stepsTraveled = 0.0;\n\n // handle very thin volumes\n if (raySteps <= 1.0) {\n vec3 posIS = posVCtoIS(posVC);\n vec4 tValue = getTextureValue(posIS);\n normalizedRayIntensity -= raySteps * sampleDistance *\n getOpacityFromTexture(tValue.r, 0, 0.5);\n gl_FragData[0] =\n vec4(getColorFromTexture(normalizedRayIntensity, 0, 0.5), 1.0);\n return;\n }\n\n posVC += jitter * stepVC;\n stepsTraveled += jitter;\n\n for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) {\n if (stepsTraveled + 1.0 >= raySteps) {\n break;\n }\n\n vec3 posIS = posVCtoIS(posVC);\n vec4 value = getTextureValue(posIS);\n // Convert scalar value to normalizedRayIntensity coefficient and\n // accumulate normalizedRayIntensity\n normalizedRayIntensity -=\n sampleDistance * getOpacityFromTexture(value.r, 0, 0.5);\n\n posVC += stepVC;\n stepsTraveled++;\n }\n\n // map normalizedRayIntensity to color\n gl_FragData[0] =\n vec4(getColorFromTexture(normalizedRayIntensity, 0, 0.5), 1.0);\n #endif\n\n #if vtkBlendMode == LABELMAP_EDGE_PROJECTION_BLEND\n // Only works with a single volume\n vec3 posVC = rayOriginVC + minDistance * rayDirVC;\n float stepsTraveled = 0.0;\n vec3 posIS = posVCtoIS(posVC);\n vec4 tValue = getTextureValue(posIS);\n if (raySteps <= 1.0) {\n gl_FragData[0] = getColorForValue(tValue, posVC, posIS);\n return;\n }\n\n vec3 stepIS = vecVCToIS(stepVC);\n vec4 value = tValue;\n posIS += jitter * stepIS;\n stepsTraveled += jitter;\n vec3 maxPosIS = posIS; // Store the position of the max value\n int segmentIndex = int(value.g);\n bool originalPosHasSeenNonZero = false;\n\n if (segmentIndex != 0) {\n // Tried using the segment index in an boolean array but reading\n // from the array by dynamic indexing was horrondously slow\n // so use bit masking instead and assign 1 to the bit corresponding to the\n // segment index and later check if the bit is set via bit operations\n setLabelOutlineBit(segmentIndex);\n }\n\n // Sample along the ray until vtkMaximumNumberOfSamples,\n // ending slightly inside the total distance\n for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) {\n // If we have reached the last step, break\n if (stepsTraveled + 1.0 >= raySteps) {\n break;\n }\n\n // compute the scalar\n tValue = getTextureValue(posIS);\n segmentIndex = int(tValue.g);\n\n if (segmentIndex != 0) {\n originalPosHasSeenNonZero = true;\n setLabelOutlineBit(segmentIndex);\n }\n\n if (tValue.r > value.r) {\n value = tValue; // Update the max value\n maxPosIS = posIS; // Update the position where max occurred\n }\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 = posVCtoIS(rayOriginVC + maxDistance * rayDirVC);\n tValue = getTextureValue(posIS);\n\n if (tValue.r > value.r) {\n value = tValue; // Update the max value\n maxPosIS = posIS; // Update the position where max occurred\n }\n\n // If we have not seen any non-zero segments, we can return early\n // and grab color from the actual center value first component (image)\n if (!originalPosHasSeenNonZero) {\n vec3 maxPosVC = posIStoVC(maxPosIS);\n gl_FragData[0] = getColorForValue(value, maxPosVC, maxPosIS);\n return;\n }\n\n vec3 neighborRayStepsIS = stepIS;\n float neighborRaySteps = raySteps;\n bool shouldLookInAllNeighbors = false;\n\n vec3 volumeSpacings = volume.spacing;\n float minVoxelSpacing =\n min(volumeSpacings[0], min(volumeSpacings[1], volumeSpacings[2]));\n vec4 base =\n vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);\n\n vec4 baseXPlus = vec4(gl_FragCoord.x + 1.0, gl_FragCoord.y, gl_FragCoord.z,\n gl_FragCoord.w);\n vec4 baseYPlus = vec4(gl_FragCoord.x, gl_FragCoord.y + 1.0, gl_FragCoord.z,\n gl_FragCoord.w);\n\n vec3 baseWorld = fragCoordToWorld(base);\n vec3 baseXPlusWorld = fragCoordToWorld(baseXPlus);\n vec3 baseYPlusWorld = fragCoordToWorld(baseYPlus);\n\n float XPlusDiff = length(baseXPlusWorld - baseWorld);\n float YPlusDiff = length(baseYPlusWorld - baseWorld);\n\n float minFragSpacingWorld = min(XPlusDiff, YPlusDiff);\n\n for (int s = 1; s < MAX_SEGMENT_INDEX; s++) {\n // bail out quickly if the segment index has not\n // been seen by the center segment\n if (!isLabelOutlineBitSet(s)) {\n continue;\n }\n\n // Use texture sampling for outlineThickness so that we can have\n // per segment thickness\n float textureCoordinate = float(s - 1) / 1024.0;\n float textureValue =\n texture2D(labelOutlineThicknessTexture, vec2(textureCoordinate, 0.5)).r;\n\n int actualThickness = int(textureValue * 255.0);\n\n // check the extreme points in the neighborhood since there is a better\n // chance of finding the edge there, so that we can bail out\n // faster if we find the edge\n bool onEdge = checkOnEdgeForNeighbor(-actualThickness, -actualThickness, s,\n stepIS) ||\n checkOnEdgeForNeighbor(actualThickness, actualThickness, s,\n stepIS) ||\n checkOnEdgeForNeighbor(actualThickness, -actualThickness, s,\n stepIS) ||\n checkOnEdgeForNeighbor(-actualThickness, +actualThickness, s,\n stepIS);\n\n if (onEdge) {\n return;\n }\n\n // since the next step is computationally expensive, we need to perform\n // some optimizations to avoid it if possible. One of the optimizations\n // is to check the whether the minimum of the voxel spacing is greater than\n // the 2 * the thickness of the outline segment. If that is the case\n // then we can safely skip the next step since we can be sure that the\n // the previous 4 checks on the extreme points would caught the entirety\n // of the all the fragments inside. i.e., this happens when we zoom out,\n if (minVoxelSpacing >\n (2.0 * float(actualThickness) - 1.0) * minFragSpacingWorld) {\n continue;\n }\n\n // Loop through the rest, skipping the processed extremes and the center\n for (int i = -actualThickness; i <= actualThickness; i++) {\n for (int j = -actualThickness; j <= actualThickness; j++) {\n if (i == 0 && j == 0)\n continue; // Skip the center\n if (abs(i) == actualThickness && abs(j) == actualThickness)\n continue; // Skip corners\n if (checkOnEdgeForNeighbor(i, j, s, stepIS)) {\n return;\n }\n }\n }\n }\n\n float sampleHeight = volume.transferFunctionsSampleHeight[0];\n vec3 tColor0 = getColorFromTexture(value.r, 0, sampleHeight);\n float pwfValue0 = getOpacityFromTexture(value.r, 0, sampleHeight);\n gl_FragData[0] = vec4(tColor0, pwfValue0);\n #endif\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 rayOriginVC, vec3 rayDirVC) {\n vec2 dists = rayIntersectVolumeDistances(rayOriginVC, rayDirVC);\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 / rayDirVC.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\nfloat getFragmentSeed() {\n // This first noise has a diagonal pattern\n float firstNoise =\n fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453);\n // This second noise is made out of blocks of CPU generated noise\n float secondNoise = texture2D(jtexture, gl_FragCoord.xy / 32.0).r;\n // Combine the two sources of noise in a way that the distribution is uniform\n // in [0,1[\n float noiseSum = firstNoise + secondNoise;\n return noiseSum < 1.0 ? noiseSum : noiseSum - 1.0;\n}\n\nvoid main() {\n fragmentSeed = getFragmentSeed();\n\n if (cameraParallel == 1) {\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 rayOriginVC = vertexVCVSOutput;\n vec2 rayStartEndDistancesVC = computeRayDistances(rayOriginVC, rayDirVC);\n if (rayStartEndDistancesVC[1] <= rayStartEndDistancesVC[0] ||\n rayStartEndDistancesVC[1] <= 0.0) {\n // Volume not hit or behind the ray\n discard;\n }\n\n // Perform the blending operation along the ray\n applyBlend(rayOriginVC, rayDirVC, rayStartEndDistancesVC[0], rayStartEndDistancesVC[1]);\n}\n";
|
|
2
2
|
|
|
3
3
|
export { vtkVolumeFS as v };
|
|
@@ -629,12 +629,12 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
629
629
|
} else {
|
|
630
630
|
if (roughnessTexture?.getImageLoaded()) {
|
|
631
631
|
if (checkDims(roughnessTexture)) {
|
|
632
|
-
usedTextures.push('_roughnessMap = textureSample(RoughnessTexture, RoughnessTextureSampler, input.tcoordVS)
|
|
632
|
+
usedTextures.push('_roughnessMap = textureSample(RoughnessTexture, RoughnessTextureSampler, input.tcoordVS);');
|
|
633
633
|
}
|
|
634
634
|
}
|
|
635
635
|
if (metallicTexture?.getImageLoaded()) {
|
|
636
636
|
if (checkDims(metallicTexture)) {
|
|
637
|
-
usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS)
|
|
637
|
+
usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS);');
|
|
638
638
|
}
|
|
639
639
|
}
|
|
640
640
|
if (ambientOcclusionTexture?.getImageLoaded()) {
|
|
@@ -847,11 +847,11 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
847
847
|
}
|
|
848
848
|
if (idata) {
|
|
849
849
|
model.colorTexture.setInputData(idata);
|
|
850
|
-
newTextures.push(['
|
|
850
|
+
newTextures.push(['Diffuse', model.colorTexture]);
|
|
851
851
|
}
|
|
852
852
|
const actor = model.WebGPUActor.getRenderable();
|
|
853
853
|
const renderer = model.WebGPURenderer.getRenderable();
|
|
854
|
-
const textures = [['
|
|
854
|
+
const textures = [['Diffuse', actor.getProperty().getDiffuseTexture?.()], ['Diffuse', actor.getTextures()[0]], ['Diffuse', model.colorTexture], ['ORM', actor.getProperty().getORMTexture?.()], ['RM', actor.getProperty().getRMTexture?.()], ['Roughness', actor.getProperty().getRoughnessTexture?.()], ['Metallic', actor.getProperty().getMetallicTexture?.()], ['Normal', actor.getProperty().getNormalTexture?.()], ['AmbientOcclusion', actor.getProperty().getAmbientOcclusionTexture?.()], ['Emission', actor.getProperty().getEmissionTexture?.()], ['Environment', renderer.getEnvironmentTexture?.()]];
|
|
855
855
|
textures.forEach(_ref => {
|
|
856
856
|
let [name, tex] = _ref;
|
|
857
857
|
if (!tex) return;
|
|
@@ -866,7 +866,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
866
866
|
// Add textures to manager only if not present
|
|
867
867
|
newTextures.forEach(_ref2 => {
|
|
868
868
|
let [textureName, srcTexture] = _ref2;
|
|
869
|
-
const newTex = model.device.getTextureManager().getTextureForVTKTexture(srcTexture
|
|
869
|
+
const newTex = model.device.getTextureManager().getTextureForVTKTexture(srcTexture);
|
|
870
870
|
if (!newTex.getReady()) return;
|
|
871
871
|
let found = false;
|
|
872
872
|
for (let t = 0; t < model.textures.length; ++t) {
|
|
@@ -878,7 +878,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
878
878
|
}
|
|
879
879
|
if (!found) {
|
|
880
880
|
usedTextures[model.textures.length] = true;
|
|
881
|
-
const tview = newTex.createView(textureName);
|
|
881
|
+
const tview = newTex.createView(`${textureName}Texture`);
|
|
882
882
|
model.textures.push(newTex);
|
|
883
883
|
model.textureViews.push(tview);
|
|
884
884
|
|
|
@@ -888,24 +888,24 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
|
|
|
888
888
|
if (srcTexture.getEdgeClamp() && srcTexture.getRepeat()) addressMode = 'mirror-repeat';else if (srcTexture.getEdgeClamp()) addressMode = 'clamp-to-edge';else if (srcTexture.getRepeat()) addressMode = 'repeat';
|
|
889
889
|
|
|
890
890
|
// Handle environment texture separately
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
891
|
+
if (textureName !== 'Environment') {
|
|
892
|
+
tview.addSampler(model.device, {
|
|
893
|
+
addressModeU: addressMode,
|
|
894
|
+
addressModeV: addressMode,
|
|
895
|
+
addressModeW: addressMode,
|
|
896
|
+
minFilter: interpolate,
|
|
897
|
+
magFilter: interpolate
|
|
898
|
+
});
|
|
899
|
+
} else {
|
|
900
|
+
tview.addSampler(model.device, {
|
|
900
901
|
addressModeU: 'repeat',
|
|
901
902
|
addressModeV: 'clamp-to-edge',
|
|
902
903
|
addressModeW: 'repeat',
|
|
903
904
|
minFilter: interpolate,
|
|
904
905
|
magFilter: interpolate,
|
|
905
906
|
mipmapFilter: 'linear'
|
|
906
|
-
};
|
|
907
|
+
});
|
|
907
908
|
}
|
|
908
|
-
tview.addSampler(model.device, options);
|
|
909
909
|
}
|
|
910
910
|
});
|
|
911
911
|
|
|
@@ -38,8 +38,7 @@ function vtkWebGPUPolyDataMapper(publicAPI, model) {
|
|
|
38
38
|
// and they handle the rendering of that cell array
|
|
39
39
|
const cellMappers = [];
|
|
40
40
|
let cellOffset = 0;
|
|
41
|
-
|
|
42
|
-
for (let i = PrimitiveTypes.Points; i <= PrimitiveTypes.TriangleStrips; i++) {
|
|
41
|
+
for (let i = PrimitiveTypes.Points; i <= PrimitiveTypes.Triangles; i++) {
|
|
43
42
|
if (prims[i].getNumberOfValues() > 0) {
|
|
44
43
|
if (!model.primitives[i]) {
|
|
45
44
|
model.primitives[i] = publicAPI.createCellArrayMapper();
|
|
@@ -56,41 +55,22 @@ function vtkWebGPUPolyDataMapper(publicAPI, model) {
|
|
|
56
55
|
model.primitives[i] = null;
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
|
-
|
|
60
|
-
// Handle edge visibility for both triangles and triangle strips
|
|
61
58
|
if (model.WebGPUActor.getRenderable().getProperty().getEdgeVisibility()) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
model.primitives[PrimitiveTypes.TriangleEdges] = null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Handle triangle strip edges
|
|
80
|
-
if (prims[PrimitiveTypes.TriangleStrips].getNumberOfValues() > 0) {
|
|
81
|
-
const i = PrimitiveTypes.TriangleStripEdges;
|
|
82
|
-
if (!model.primitives[i]) {
|
|
83
|
-
model.primitives[i] = publicAPI.createCellArrayMapper();
|
|
59
|
+
for (let i = PrimitiveTypes.TriangleEdges; i <= PrimitiveTypes.TriangleStripEdges; i++) {
|
|
60
|
+
if (prims[i - 2].getNumberOfValues() > 0) {
|
|
61
|
+
if (!model.primitives[i]) {
|
|
62
|
+
model.primitives[i] = publicAPI.createCellArrayMapper();
|
|
63
|
+
}
|
|
64
|
+
const cellMapper = model.primitives[i];
|
|
65
|
+
cellMapper.setCellArray(prims[i - 2]);
|
|
66
|
+
cellMapper.setCurrentInput(poly);
|
|
67
|
+
cellMapper.setCellOffset(model.primitives[i - 2].getCellOffset());
|
|
68
|
+
cellMapper.setPrimitiveType(i);
|
|
69
|
+
cellMapper.setRenderable(model.renderable);
|
|
70
|
+
cellMappers.push(cellMapper);
|
|
71
|
+
} else {
|
|
72
|
+
model.primitives[i] = null;
|
|
84
73
|
}
|
|
85
|
-
const cellMapper = model.primitives[i];
|
|
86
|
-
cellMapper.setCellArray(prims[PrimitiveTypes.TriangleStrips]);
|
|
87
|
-
cellMapper.setCurrentInput(poly);
|
|
88
|
-
cellMapper.setCellOffset(model.primitives[PrimitiveTypes.TriangleStrips].getCellOffset());
|
|
89
|
-
cellMapper.setPrimitiveType(i);
|
|
90
|
-
cellMapper.setRenderable(model.renderable);
|
|
91
|
-
cellMappers.push(cellMapper);
|
|
92
|
-
} else {
|
|
93
|
-
model.primitives[PrimitiveTypes.TriangleStripEdges] = null;
|
|
94
74
|
}
|
|
95
75
|
}
|
|
96
76
|
publicAPI.prepareNodes();
|
|
@@ -315,7 +315,7 @@ function vtkWebGPURenderer(publicAPI, model) {
|
|
|
315
315
|
ubo.addEntry('FSQMatrix', 'mat4x4<f32>');
|
|
316
316
|
ubo.addEntry('BackgroundColor', 'vec4<f32>');
|
|
317
317
|
model.clearFSQ.setUBO(ubo);
|
|
318
|
-
const environmentTextureHash = device.getTextureManager().getTextureForVTKTexture(model.backgroundTex
|
|
318
|
+
const environmentTextureHash = device.getTextureManager().getTextureForVTKTexture(model.backgroundTex);
|
|
319
319
|
if (environmentTextureHash.getReady()) {
|
|
320
320
|
const tview = environmentTextureHash.createView(`EnvironmentTexture`);
|
|
321
321
|
model.clearFSQ.setTextureViews([tview]);
|
|
@@ -84,27 +84,17 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
84
84
|
req.height = req.imageBitmap.height;
|
|
85
85
|
req.depth = 1;
|
|
86
86
|
req.format = 'rgba8unorm';
|
|
87
|
-
req.flip =
|
|
87
|
+
req.flip = false;
|
|
88
88
|
_copyImageToTexture(req.imageBitmap);
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
|
-
if (req.jsImageData) {
|
|
91
|
+
if (req.jsImageData && !req.nativeArray) {
|
|
92
92
|
req.width = req.jsImageData.width;
|
|
93
93
|
req.height = req.jsImageData.height;
|
|
94
94
|
req.depth = 1;
|
|
95
95
|
req.format = 'rgba8unorm';
|
|
96
96
|
req.flip = true;
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (req.image) {
|
|
101
|
-
req.width = req.image.width;
|
|
102
|
-
req.height = req.image.height;
|
|
103
|
-
req.depth = 1;
|
|
104
|
-
req.format = 'rgba8unorm';
|
|
105
|
-
req.flip = true;
|
|
106
|
-
_copyImageToTexture(req.image);
|
|
107
|
-
return;
|
|
97
|
+
req.nativeArray = req.jsImageData.data;
|
|
108
98
|
}
|
|
109
99
|
const tDetails = vtkWebGPUTypes.getDetailsFromTextureFormat(model.format);
|
|
110
100
|
let bufferBytesPerRow = model.width * tDetails.stride;
|
|
@@ -164,6 +154,15 @@ function vtkWebGPUTexture(publicAPI, model) {
|
|
|
164
154
|
if (req.nativeArray) {
|
|
165
155
|
nativeArray = req.nativeArray;
|
|
166
156
|
}
|
|
157
|
+
if (req.image) {
|
|
158
|
+
const canvas = new OffscreenCanvas(req.image.width, req.image.height);
|
|
159
|
+
const ctx = canvas.getContext('2d');
|
|
160
|
+
ctx.translate(0, canvas.height);
|
|
161
|
+
ctx.scale(1, -1);
|
|
162
|
+
ctx.drawImage(req.image, 0, 0, req.image.width, req.image.height, 0, 0, canvas.width, canvas.height);
|
|
163
|
+
const imageData = ctx.getImageData(0, 0, req.image.width, req.image.height);
|
|
164
|
+
nativeArray = imageData.data;
|
|
165
|
+
}
|
|
167
166
|
const is3D = publicAPI.getDimensionality() === 3;
|
|
168
167
|
const alignedTextureData = alignTextureData(nativeArray, model.height, is3D ? model.depth : 1);
|
|
169
168
|
bufferBytesPerRow = alignedTextureData[1];
|
|
@@ -69,10 +69,9 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
69
69
|
req.height = req.image.height;
|
|
70
70
|
req.depth = 1;
|
|
71
71
|
req.format = 'rgba8unorm';
|
|
72
|
-
req.flip = true;
|
|
73
72
|
/* eslint-disable no-undef */
|
|
74
73
|
/* eslint-disable no-bitwise */
|
|
75
|
-
req.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING
|
|
74
|
+
req.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING;
|
|
76
75
|
/* eslint-enable no-undef */
|
|
77
76
|
/* eslint-enable no-bitwise */
|
|
78
77
|
}
|
|
@@ -87,7 +86,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
87
86
|
req.nativeArray = req.jsImageData.data;
|
|
88
87
|
/* eslint-disable no-undef */
|
|
89
88
|
/* eslint-disable no-bitwise */
|
|
90
|
-
req.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING
|
|
89
|
+
req.usage = GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING;
|
|
91
90
|
/* eslint-enable no-undef */
|
|
92
91
|
/* eslint-enable no-bitwise */
|
|
93
92
|
}
|
|
@@ -100,7 +99,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
100
99
|
req.flip = true;
|
|
101
100
|
/* eslint-disable no-undef */
|
|
102
101
|
/* eslint-disable no-bitwise */
|
|
103
|
-
req.usage = GPUTextureUsage.
|
|
102
|
+
req.usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT;
|
|
104
103
|
/* eslint-enable no-undef */
|
|
105
104
|
/* eslint-enable no-bitwise */
|
|
106
105
|
}
|
|
@@ -113,7 +112,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
113
112
|
req.flip = true;
|
|
114
113
|
/* eslint-disable no-undef */
|
|
115
114
|
/* eslint-disable no-bitwise */
|
|
116
|
-
req.usage = GPUTextureUsage.
|
|
115
|
+
req.usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT;
|
|
117
116
|
/* eslint-enable no-undef */
|
|
118
117
|
/* eslint-enable no-bitwise */
|
|
119
118
|
}
|
|
@@ -121,9 +120,7 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
121
120
|
|
|
122
121
|
// create a texture (used by getTexture)
|
|
123
122
|
function _createTexture(req) {
|
|
124
|
-
const newTex = vtkWebGPUTexture.newInstance(
|
|
125
|
-
label: req.label
|
|
126
|
-
});
|
|
123
|
+
const newTex = vtkWebGPUTexture.newInstance();
|
|
127
124
|
newTex.create(model.device, {
|
|
128
125
|
width: req.width,
|
|
129
126
|
height: req.height,
|
|
@@ -160,11 +157,9 @@ function vtkWebGPUTextureManager(publicAPI, model) {
|
|
|
160
157
|
treq.hash = treq.time + treq.format + treq.mipLevel;
|
|
161
158
|
return model.device.getTextureManager().getTexture(treq);
|
|
162
159
|
};
|
|
163
|
-
publicAPI.getTextureForVTKTexture =
|
|
164
|
-
let label = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
|
|
160
|
+
publicAPI.getTextureForVTKTexture = srcTexture => {
|
|
165
161
|
const treq = {
|
|
166
|
-
time: srcTexture.getMTime()
|
|
167
|
-
label
|
|
162
|
+
time: srcTexture.getMTime()
|
|
168
163
|
};
|
|
169
164
|
if (srcTexture.getInputData()) {
|
|
170
165
|
treq.imageData = srcTexture.getInputData();
|
|
@@ -640,10 +640,10 @@ function vtkWebGPUVolumePassFSQ(publicAPI, model) {
|
|
|
640
640
|
|
|
641
641
|
// handle filteringMode
|
|
642
642
|
const tScale = model.textureViews[vidx + 4].getTexture().getScale();
|
|
643
|
-
const ipScalarRange =
|
|
643
|
+
const ipScalarRange = actor.getProperty().getIpScalarRange();
|
|
644
644
|
ipScalarRangeArray[vidx * 4] = ipScalarRange[0] / tScale;
|
|
645
645
|
ipScalarRangeArray[vidx * 4 + 1] = ipScalarRange[1] / tScale;
|
|
646
|
-
ipScalarRangeArray[vidx * 4 + 2] =
|
|
646
|
+
ipScalarRangeArray[vidx * 4 + 2] = actor.getProperty().getFilterMode();
|
|
647
647
|
}
|
|
648
648
|
model.SSBO.addEntry('SCTCMatrix', 'mat4x4<f32>');
|
|
649
649
|
model.SSBO.addEntry('planeNormals', 'mat4x4<f32>');
|