UNPKG

vtk.js-custom-jhonny

Version:

Visualization Toolkit for the Web

4 lines 63.4 kB
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";

export { vtkVolumeFS as v };