const float infinity = 3.402823466e38;
in vec3 vertexVCVSOutput;
#define COMPOSITE_BLEND 0 #define MAXIMUM_INTENSITY_BLEND 1 #define MINIMUM_INTENSITY_BLEND 2 #define AVERAGE_INTENSITY_BLEND 3 #define ADDITIVE_INTENSITY_BLEND 4 #define RADON_TRANSFORM_BLEND 5 #define LABELMAP_EDGE_PROJECTION_BLEND 6
#define vtkNumberOfLights #define vtkMaxLaoKernelSize #define vtkNumberOfComponents #define vtkBlendMode #define vtkMaximumNumberOfSamples
uniform int twoSidedLighting;
#if vtkMaxLaoKernelSize > 0 vec2 kernelSample[vtkMaxLaoKernelSize]; #endif
#ifdef EnabledMultiTexturePerVolume #define vtkNumberOfVolumeTextures vtkNumberOfComponents #else #define vtkNumberOfVolumeTextures 1 #endif uniform highp sampler3D volumeTexture[vtkNumberOfVolumeTextures]; uniform sampler2D colorTexture; uniform sampler2D opacityTexture; uniform sampler2D jtexture; uniform sampler2D labelOutlineThicknessTexture;
struct Volume {
vec3 originVC; vec3 spacing; vec3 inverseSpacing; ivec3 dimensions; vec3 inverseDimensions; mat3 vecISToVCMatrix; mat3 vecVCToISMatrix; mat4 PCWCMatrix; mat4 worldToIndex; float diagonalLength;
vec4 colorTextureScale; vec4 colorTextureShift; vec4 opacityTextureScale; vec4 opacityTextureShift;
vec4 transferFunctionsSampleHeight;
vec4 independentComponentMix;
vec4 ipScalarRangeMin; vec4 ipScalarRangeMax;
float ambient; float diffuse; float specular; float specularPower; int computeNormalFromOpacity;
vec4 gradientOpacityScale; vec4 gradientOpacityShift; vec4 gradientOpacityMin; vec4 gradientOpacityMax;
float volumetricScatteringBlending; float globalIlluminationReach; float anisotropy; float anisotropySquared;
int kernelSize; int kernelRadius;
float outlineOpacity; }; uniform Volume volume;
struct Light { vec3 color; vec3 positionVC; vec3 directionVC; vec3 halfAngleVC; vec3 attenuation; float exponent; float coneAngle; int isPositional; }; #if vtkNumberOfLights > 0 uniform Light lights[vtkNumberOfLights]; #endif
uniform float vpWidth; uniform float vpHeight; uniform float vpOffsetX; uniform float vpOffsetY;
const int MAX_SEGMENT_INDEX = 256; #define MAX_SEGMENTS 256 #define UINT_SIZE 32
#define BITMASK_SIZE ((MAX_SEGMENTS + UINT_SIZE - 1) / UINT_SIZE) uint labelOutlineBitmasks[BITMASK_SIZE];
void setLabelOutlineBit(int segmentIndex) { int arrayIndex = segmentIndex / UINT_SIZE; int bitIndex = segmentIndex % UINT_SIZE; labelOutlineBitmasks[arrayIndex] |= 1u << bitIndex; }
bool isLabelOutlineBitSet(int segmentIndex) { int arrayIndex = segmentIndex / UINT_SIZE; int bitIndex = segmentIndex % UINT_SIZE; return ((labelOutlineBitmasks[arrayIndex] & (1u << bitIndex)) != 0u); }
uniform float camThick; uniform float camNear; uniform float camFar; uniform int cameraParallel;
float fragmentSeed;
uniform float sampleDistance; uniform float volumeShadowSampleDistance;
vec3 rayDirVC;
#define INV4PI 0.0796 #define EPSILON 0.001 #define PI 3.1415 #define PI2 9.8696
vec4 rawSampleTexture(vec3 pos) { #ifdef EnabledMultiTexturePerVolume vec4 rawSample; rawSample[0] = texture(volumeTexture[0], pos)[0]; #if vtkNumberOfComponents > 1 rawSample[1] = texture(volumeTexture[1], pos)[0]; #endif #if vtkNumberOfComponents > 2 rawSample[2] = texture(volumeTexture[2], pos)[0]; #endif #if vtkNumberOfComponents > 3 rawSample[3] = texture(volumeTexture[3], pos)[0]; #endif return rawSample; #else return texture(volumeTexture[0], pos); #endif }
vec4 rawFetchTexture(ivec3 pos) { #ifdef EnabledMultiTexturePerVolume vec4 rawSample; #if vtkNumberOfComponents > 0 rawSample[0] = texelFetch(volumeTexture[0], pos, 0)[0]; #endif #if vtkNumberOfComponents > 1 rawSample[1] = texelFetch(volumeTexture[1], pos, 0)[0]; #endif #if vtkNumberOfComponents > 2 rawSample[2] = texelFetch(volumeTexture[2], pos, 0)[0]; #endif #if vtkNumberOfComponents > 3 rawSample[3] = texelFetch(volumeTexture[3], pos, 0)[0]; #endif return rawSample; #else return texelFetch(volumeTexture[0], pos, 0); #endif }
vec4 getTextureValue(vec3 pos) { vec4 tmp = rawSampleTexture(pos);
#if defined(vtkComponent0ForceNearest) || \ defined(vtkComponent1ForceNearest) || \ defined(vtkComponent2ForceNearest) || \ defined(vtkComponent3ForceNearest) vec3 nearestPos = (floor(pos * vec3(volume.dimensions)) + 0.5) * volume.inverseDimensions; vec4 nearestValue = rawSampleTexture(nearestPos); #ifdef vtkComponent0ForceNearest tmp[0] = nearestValue[0]; #endif #ifdef vtkComponent1ForceNearest tmp[1] = nearestValue[1]; #endif #ifdef vtkComponent2ForceNearest tmp[2] = nearestValue[2]; #endif #ifdef vtkComponent3ForceNearest tmp[3] = nearestValue[3]; #endif #endif
#ifndef EnabledIndependentComponents #if vtkNumberOfComponents == 1 tmp.a = tmp.r; #endif #if vtkNumberOfComponents == 2 tmp.a = tmp.g; #endif #if vtkNumberOfComponents == 3 tmp.a = length(tmp.rgb); #endif #endif
return tmp; }
float getOpacityFromTexture(float scalar, int component, float height) { float scaledScalar = scalar * volume.opacityTextureScale[component] + volume.opacityTextureShift[component]; return texture2D(opacityTexture, vec2(scaledScalar, height)).r; } vec3 getColorFromTexture(float scalar, int component, float height) { float scaledScalar = scalar * volume.colorTextureScale[component] + volume.colorTextureShift[component]; return texture2D(colorTexture, vec2(scaledScalar, height)).rgb; }
vec3 posIStoVC(vec3 posIS) { return volume.vecISToVCMatrix * posIS + volume.originVC; }
vec3 posVCtoIS(vec3 posVC) { return volume.vecVCToISMatrix * (posVC - volume.originVC); }
vec3 vecISToVC(vec3 dirIS) { return volume.vecISToVCMatrix * dirIS; }
vec3 vecVCToIS(vec3 dirVC) { return volume.vecVCToISMatrix * dirVC; }
float computeGradientOpacityFactor(float normalMag, int component) { float goscale = volume.gradientOpacityScale[component]; float goshift = volume.gradientOpacityShift[component]; float gomin = volume.gradientOpacityMin[component]; float gomax = volume.gradientOpacityMax[component]; return clamp(normalMag * goscale + goshift, gomin, gomax); }
#ifdef vtkClippingPlanesOn bool isPointClipped(vec3 posVC) { for (int i = 0; i < clip_numPlanes; ++i) { if (dot(vec3(vClipPlaneOrigins[i] - posVC), vClipPlaneNormals[i]) > 0.0) { return true; } } return false; } #endif
vec4 computeDensityNormal(vec3 opacityUCoords[2], float opacityTextureHeight, float gradientOpacity, int component) { vec4 opacityG; opacityG.x += getOpacityFromTexture(opacityUCoords[0].x, component, opacityTextureHeight); opacityG.y += getOpacityFromTexture(opacityUCoords[0].y, component, opacityTextureHeight); opacityG.z += getOpacityFromTexture(opacityUCoords[0].z, component, opacityTextureHeight); opacityG.x -= getOpacityFromTexture(opacityUCoords[1].x, component, opacityTextureHeight); opacityG.y -= getOpacityFromTexture(opacityUCoords[1].y, component, opacityTextureHeight); opacityG.z -= getOpacityFromTexture(opacityUCoords[1].z, component, opacityTextureHeight);
opacityG.xyz *= gradientOpacity * volume.inverseSpacing; opacityG.w = length(opacityG.xyz); if (opacityG.w == 0.0) { return vec4(0.0); }
opacityG.xyz = normalize(vecISToVC(opacityG.xyz));
return opacityG; }
vec4 computeNormalForDensity(vec3 posIS, out vec3 scalarInterp[2], const int opacityComponent) { vec3 offsetedPosIS; for (int axis = 0; axis < 3; ++axis) { offsetedPosIS = posIS; offsetedPosIS[axis] += volume.inverseDimensions[axis]; scalarInterp[0][axis] = getTextureValue(offsetedPosIS)[opacityComponent]; #ifdef vtkClippingPlanesOn if (isPointClipped(posIStoVC(offsetedPosIS))) { scalarInterp[0][axis] = 0.0; } #endif
offsetedPosIS = posIS; offsetedPosIS[axis] -= volume.inverseDimensions[axis]; scalarInterp[1][axis] = getTextureValue(offsetedPosIS)[opacityComponent]; #ifdef vtkClippingPlanesOn if (isPointClipped(posIStoVC(offsetedPosIS))) { scalarInterp[1][axis] = 0.0; } #endif }
vec4 result; result.xyz = (scalarInterp[0] - scalarInterp[1]) * volume.inverseSpacing; result.w = length(result.xyz); if (result.w == 0.0) { return vec4(0.0); } result.xyz = normalize(vecISToVC(result.xyz)); return result; }
vec4 fragCoordToPCPos(vec4 fragCoord) { return vec4((fragCoord.x / vpWidth - vpOffsetX - 0.5) * 2.0, (fragCoord.y / vpHeight - vpOffsetY - 0.5) * 2.0, (fragCoord.z - 0.5) * 2.0, 1.0); }
vec4 pcPosToWorldCoord(vec4 pcPos) { return volume.PCWCMatrix * pcPos; }
vec3 fragCoordToIndexSpace(vec4 fragCoord) { vec4 pcPos = fragCoordToPCPos(fragCoord); vec4 worldCoord = pcPosToWorldCoord(pcPos); vec4 vertex = (worldCoord / worldCoord.w);
vec3 index = (volume.worldToIndex * vertex).xyz;
return (index + vec3(0.5)) * volume.inverseDimensions; }
vec3 fragCoordToWorld(vec4 fragCoord) { vec4 pcPos = fragCoordToPCPos(fragCoord); vec4 worldCoord = pcPosToWorldCoord(pcPos); return worldCoord.xyz; }
mat4 computeMat4Normal(vec3 posIS, vec4 tValue) { vec3 xvec = vec3(volume.inverseDimensions.x, 0.0, 0.0); vec3 yvec = vec3(0.0, volume.inverseDimensions.y, 0.0); vec3 zvec = vec3(0.0, 0.0, volume.inverseDimensions.z);
vec4 distX = getTextureValue(posIS + xvec) - getTextureValue(posIS - xvec); vec4 distY = getTextureValue(posIS + yvec) - getTextureValue(posIS - yvec); vec4 distZ = getTextureValue(posIS + zvec) - getTextureValue(posIS - zvec);
distX *= 0.5 * volume.inverseSpacing.x; distY *= 0.5 * volume.inverseSpacing.y; distZ *= 0.5 * volume.inverseSpacing.z;
mat4 result;
#if vtkNumberOfComponents > 0 && !defined(vtkComponent0Proportional) { const int component = 0; vec3 normal = vec3(distX[component], distY[component], distZ[component]); float normalLength = length(normal); if (normalLength > 0.0) { normal = normalize(vecISToVC(normal)); } result[component] = vec4(normal, normalLength); } #endif
#if vtkNumberOfComponents > 1 && !defined(vtkComponent1Proportional) { const int component = 1; vec3 normal = vec3(distX[component], distY[component], distZ[component]); float normalLength = length(normal); if (normalLength > 0.0) { normal = normalize(vecISToVC(normal)); } result[component] = vec4(normal, normalLength); } #endif
#if vtkNumberOfComponents > 2 && !defined(vtkComponent2Proportional) { const int component = 2; vec3 normal = vec3(distX[component], distY[component], distZ[component]); float normalLength = length(normal); if (normalLength > 0.0) { normal = normalize(vecISToVC(normal)); } result[component] = vec4(normal, normalLength); } #endif
#if vtkNumberOfComponents > 3 && !defined(vtkComponent3Proportional) { const int component = 3; vec3 normal = vec3(distX[component], distY[component], distZ[component]); float normalLength = length(normal); if (normalLength > 0.0) { normal = normalize(vecISToVC(normal)); } result[component] = vec4(normal, normalLength); } #endif
return result; }
float phaseFunction(float cos_angle) { float anisotropy = volume.anisotropy; if (abs(anisotropy) <= EPSILON) { return 0.5; } float anisotropy2 = volume.anisotropySquared; return ((1.0 - anisotropy2) / pow(1.0 + anisotropy2 - 2.0 * anisotropy * cos_angle, 1.5)) / 2.0; }
vec2 rayIntersectVolumeDistances(vec3 rayOriginVC, vec3 rayDirVC) { vec3 rayOriginIS = posVCtoIS(rayOriginVC); vec3 rayDirIS = vecVCToIS(rayDirVC); vec3 invDir = 1.0 / rayDirIS;
vec3 distancesTo0 = invDir * (vec3(0.0) - rayOriginIS); vec3 distancesTo1 = invDir * (vec3(1.0) - rayOriginIS); vec3 dMinPerAxis = min(distancesTo0, distancesTo1); vec3 dMaxPerAxis = max(distancesTo0, distancesTo1); float distanceMin = max(dMinPerAxis.x, max(dMinPerAxis.y, dMinPerAxis.z)); float distanceMax = min(dMaxPerAxis.x, min(dMaxPerAxis.y, dMaxPerAxis.z)); return vec2(distanceMin, distanceMax); }
#if vtkMaxLaoKernelSize > 0
vec3 sampleDirectionUniform(int rayIndex) { vec2 rayRandomness = kernelSample[rayIndex]; float fragmentRandomness = fragmentSeed; vec2 mergedRandom = rayRandomness + vec2(fragmentRandomness); mergedRandom -= vec2(greaterThanEqual(mergedRandom, vec2(1.0)));
float u = mergedRandom[0]; float v = mergedRandom[1]; float theta = u * 2.0 * PI; float phi = acos(2.0 * v - 1.0); float sinTheta = sin(theta); float cosTheta = cos(theta); float sinPhi = sin(phi); float cosPhi = cos(phi); return vec3(sinPhi * cosTheta, sinPhi * sinTheta, cosPhi); }
float computeLAO(vec3 posVC, vec4 normalVC, float originalOpacity) { if (normalVC.w <= 0.0 || originalOpacity <= 0.05) { return 1.0; }
#ifdef EnabledGradientOpacity float gradientOpacityFactor = computeGradientOpacityFactor(normalVC.w, 0); #endif
float visibilitySum = 0.0; float weightSum = 0.0; for (int i = 0; i < volume.kernelSize; i++) { vec3 rayDirectionVC = sampleDirectionUniform(i); float normalDotRay = dot(normalVC.xyz, rayDirectionVC); if (normalDotRay > 0.0) { rayDirectionVC = -rayDirectionVC; normalDotRay = -normalDotRay; }
vec3 currPosIS = posVCtoIS(posVC); float visibility = 1.0; vec3 randomDirStepIS = vecVCToIS(rayDirectionVC * sampleDistance); for (int j = 0; j < volume.kernelRadius; j++) { currPosIS += randomDirStepIS; if (any(lessThan(currPosIS, vec3(0.0))) || any(greaterThan(currPosIS, vec3(1.0)))) { break; } float opacity = getOpacityFromTexture(getTextureValue(currPosIS).r, 0, 0.5); #ifdef EnabledGradientOpacity opacity *= gradientOpacityFactor; #endif visibility *= 1.0 - opacity; if (visibility < EPSILON) { visibility = 0.0; break; } } float rayWeight = -normalDotRay; visibilitySum += visibility * rayWeight; weightSum += rayWeight; }
if (weightSum == 0.0) { return 1.0; }
float lao = visibilitySum / weightSum;
return clamp(lao, 0.3, 1.0); } #endif
#if vtkNumberOfLights > 0
float computeVolumeShadowWithoutCache(vec3 posVC, vec3 lightDirNormVC) { float rayStepLength = volumeShadowSampleDistance * mix(1.5, 3.0, fragmentSeed);
vec3 initialPosVC = posVC + rayStepLength * lightDirNormVC;
#ifdef vtkClippingPlanesOn float clippingPlanesMaxDistance = infinity; for (int i = 0; i < clip_numPlanes; ++i) { float dotOrigin = dot(vClipPlaneOrigins[i] - initialPosVC, vClipPlaneNormals[i]); if (dotOrigin > 0.0) { return 1.0; } float dotDirection = dot(lightDirNormVC, vClipPlaneNormals[i]); if (dotDirection < 0.0) { float intersectionDistance = dotOrigin / dotDirection; clippingPlanesMaxDistance = min(clippingPlanesMaxDistance, intersectionDistance); } } #endif
vec2 intersectionDistances = rayIntersectVolumeDistances(initialPosVC, lightDirNormVC);
if (intersectionDistances[1] <= intersectionDistances[0] || intersectionDistances[1] <= 0.0) { return 1.0; }
float maxTravelDistance = mix(0.0, volume.diagonalLength, volume.globalIlluminationReach); float startDistance = max(intersectionDistances[0], 0.0); float endDistance = min(intersectionDistances[1], startDistance + maxTravelDistance); #ifdef vtkClippingPlanesOn endDistance = min(endDistance, clippingPlanesMaxDistance); #endif if (endDistance - startDistance < 0.0) { return 1.0; }
vec3 initialPosIS = posVCtoIS(initialPosVC); vec3 scaledLightDirIS = vecVCToIS(lightDirNormVC);
float shadow = 1.0; for (float currentDistance = startDistance; currentDistance <= endDistance; currentDistance += rayStepLength) { vec3 posIS = initialPosIS + currentDistance * scaledLightDirIS; vec4 scalar = getTextureValue(posIS); float opacity = getOpacityFromTexture(scalar.r, 0, 0.5); #if defined(EnabledGradientOpacity) && !defined(EnabledIndependentComponents) vec3 scalarInterp[2]; vec4 normal = computeNormalForDensity(posIS, scalarInterp, 3); float opacityFactor = computeGradientOpacityFactor(normal.w, 0); opacity *= opacityFactor; #endif shadow *= 1.0 - opacity;
if (shadow < EPSILON) { return 0.0; } } return shadow; }
struct { vec3 posVC; float shadow; } cachedShadows[vtkNumberOfLights];
float computeVolumeShadow(vec3 posVC, vec3 lightDirNormVC, int lightIdx) { if (posVC == cachedShadows[lightIdx].posVC) { return cachedShadows[lightIdx].shadow; } float shadow = computeVolumeShadowWithoutCache(posVC, lightDirNormVC); cachedShadows[lightIdx].posVC = posVC; cachedShadows[lightIdx].shadow = shadow; return shadow; }
#endif
#if vtkNumberOfLights > 0 vec3 applyLighting(vec3 tColor, vec4 normalVC) { vec3 diffuse = vec3(0.0, 0.0, 0.0); vec3 specular = vec3(0.0, 0.0, 0.0); for (int lightIdx = 0; lightIdx < vtkNumberOfLights; lightIdx++) { float df = dot(normalVC.xyz, lights[lightIdx].directionVC); if (df > 0.0) { diffuse += df * lights[lightIdx].color; float sf = dot(normalVC.xyz, -lights[lightIdx].halfAngleVC); if (sf > 0.0) { specular += pow(sf, volume.specularPower) * lights[lightIdx].color; } } } return tColor * (diffuse * volume.diffuse + volume.ambient) + specular * volume.specular; }
vec3 applySurfaceShadowLighting(vec3 tColor, float alpha, vec3 posVC, vec4 normalVC) { vec3 diffuse = vec3(0.0); vec3 specular = vec3(0.0); for (int ligthIdx = 0; ligthIdx < vtkNumberOfLights; ligthIdx++) { vec3 vertLightDirection; float attenuation; if (lights[ligthIdx].isPositional == 1) { vertLightDirection = posVC - lights[ligthIdx].positionVC; float lightDistance = length(vertLightDirection); vertLightDirection = vertLightDirection / lightDistance; vec3 attenuationPolynom = lights[ligthIdx].attenuation; attenuation = 1.0 / (attenuationPolynom[0] + lightDistance * (attenuationPolynom[1] + lightDistance * attenuationPolynom[2])); float coneDot = dot(vertLightDirection, lights[ligthIdx].directionVC); if (lights[ligthIdx].coneAngle <= 90.0) { if (coneDot >= cos(radians(lights[ligthIdx].coneAngle))) { attenuation *= pow(coneDot, lights[ligthIdx].exponent); } else { attenuation = 0.0; } } } else { vertLightDirection = lights[ligthIdx].directionVC; attenuation = 1.0; }
float ndotL = dot(normalVC.xyz, vertLightDirection); if (ndotL < 0.0 && twoSidedLighting == 1) { ndotL = -ndotL; } if (ndotL > 0.0) { diffuse += ndotL * attenuation * lights[ligthIdx].color; float vdotR = dot(-rayDirVC, normalize(vertLightDirection - 2.0 * ndotL * normalVC.xyz)); if (vdotR > 0.0) { specular += pow(vdotR, volume.specularPower) * attenuation * lights[ligthIdx].color; } } } #if vtkMaxLaoKernelSize > 0 float laoFactor = computeLAO(posVC, normalVC, alpha); #else const float laoFactor = 1.0; #endif return tColor * (diffuse * volume.diffuse + volume.ambient * laoFactor) + specular * volume.specular; }
vec3 applyVolumeShadowLighting(vec3 tColor, vec3 posVC) { vec3 diffuse = vec3(0.0); for (int lightIdx = 0; lightIdx < vtkNumberOfLights; lightIdx++) { vec3 lightDirVC = lights[lightIdx].isPositional == 1 ? normalize(lights[lightIdx].positionVC - posVC) : -lights[lightIdx].directionVC; float shadowCoeff = computeVolumeShadow(posVC, lightDirVC, lightIdx); float phaseAttenuation = phaseFunction(dot(rayDirVC, lightDirVC)); diffuse += phaseAttenuation * shadowCoeff * lights[lightIdx].color; } return tColor * (diffuse * volume.diffuse + volume.ambient); } #endif
vec3 applyAllLightning(vec3 tColor, float alpha, vec3 posVC, vec4 surfaceNormalVC) { #if vtkNumberOfLights > 0 float volCoeff = volume.volumetricScatteringBlending * (1.0 - alpha / 2.0) * (1.0 - atan(surfaceNormalVC.w) * INV4PI);
vec3 surfaceShadedColor = tColor; #ifdef EnableSurfaceLighting if (volCoeff < 1.0 - EPSILON) { surfaceShadedColor = applySurfaceShadowLighting(tColor, alpha, posVC, surfaceNormalVC); } #endif
vec3 volumeShadedColor = tColor; #ifdef EnableVolumeLighting if (volCoeff >= EPSILON) { volumeShadedColor = applyVolumeShadowLighting(tColor, posVC); } #endif
if (volCoeff < EPSILON) { return surfaceShadedColor; } if (volCoeff >= 1.0 - EPSILON) { return volumeShadedColor; } return mix(surfaceShadedColor, volumeShadedColor, volCoeff); #endif return tColor; }
vec4 getColorForLabelOutline() { vec3 centerPosIS = fragCoordToIndexSpace(gl_FragCoord); vec4 centerValue = getTextureValue(centerPosIS); bool pixelOnBorder = false; vec4 tColor = vec4(getColorFromTexture(centerValue.r, 0, 0.5), getOpacityFromTexture(centerValue.r, 0, 0.5));
int segmentIndex = int(centerValue.r * 255.0);
float textureCoordinate = float(segmentIndex - 1) / 1024.0; float textureValue = texture2D(labelOutlineThicknessTexture, vec2(textureCoordinate, 0.5)).r; int actualThickness = int(textureValue * 255.0);
if (segmentIndex == 0) { return vec4(0, 0, 0, 0); }
for (int i = -actualThickness; i <= actualThickness; i++) { for (int j = -actualThickness; j <= actualThickness; j++) { if (i == 0 || j == 0) { continue; }
vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(i), gl_FragCoord.y + float(j), gl_FragCoord.z, gl_FragCoord.w);
vec3 neighborPosIS = fragCoordToIndexSpace(neighborPixelCoord); vec4 value = getTextureValue(neighborPosIS);
if (any(notEqual(value, centerValue))) { pixelOnBorder = true; break; } }
if (pixelOnBorder == true) { break; } }
if (pixelOnBorder == true) { tColor.a = volume.outlineOpacity; }
return tColor; }
vec4 getColorForAdditivePreset(vec4 tValue, vec3 posVC, vec3 posIS) { mat4 normalMat = computeMat4Normal(posIS, tValue); vec4 normalLights[2]; normalLights[0] = normalMat[0]; normalLights[1] = normalMat[1]; #if vtkNumberOfLights > 0 if (volume.computeNormalFromOpacity == 1) { for (int component = 0; component < 2; ++component) { vec3 scalarInterp[2]; float height = volume.transferFunctionsSampleHeight[component]; computeNormalForDensity(posIS, scalarInterp, component); normalLights[component] = computeDensityNormal(scalarInterp, height, 1.0, component); } } #endif
float opacities[2]; opacities[0] = getOpacityFromTexture( tValue[0], 0, volume.transferFunctionsSampleHeight[0]); opacities[1] = getOpacityFromTexture( tValue[1], 1, volume.transferFunctionsSampleHeight[1]); #ifdef EnabledGradientOpacity for (int component = 0; component < 2; ++component) { opacities[component] *= computeGradientOpacityFactor(normalMat[component].a, component); } #endif float opacitySum = opacities[0] + opacities[1]; if (opacitySum <= 0.0) { return vec4(0.0); }
vec3 colors[2]; for (int component = 0; component < 2; ++component) { float sampleHeight = volume.transferFunctionsSampleHeight[component]; vec3 color = getColorFromTexture(tValue[component], component, sampleHeight); color = applyAllLightning(color, opacities[component], posVC, normalLights[component]); colors[component] = color; } vec3 mixedColor = (opacities[0] * colors[0] + opacities[1] * colors[1]) / opacitySum; return vec4(mixedColor, min(1.0, opacitySum)); }
vec4 getColorForColorizePreset(vec4 tValue, vec3 posVC, vec3 posIS) { mat4 normalMat = computeMat4Normal(posIS, tValue); vec4 normalLight = normalMat[0]; #if vtkNumberOfLights > 0 if (volume.computeNormalFromOpacity == 1) { vec3 scalarInterp[2]; float height = volume.transferFunctionsSampleHeight[0]; computeNormalForDensity(posIS, scalarInterp, 0); normalLight = computeDensityNormal(scalarInterp, height, 1.0, 0); } #endif
float opacity = getOpacityFromTexture( tValue[0], 0, volume.transferFunctionsSampleHeight[0]); #ifdef EnabledGradientOpacity opacity *= computeGradientOpacityFactor(normalMat[0].a, 0); #endif
vec3 colorizingColor = getColorFromTexture( tValue[0], 1, volume.transferFunctionsSampleHeight[1]); float colorizingOpacity = getOpacityFromTexture( tValue[1], 1, volume.transferFunctionsSampleHeight[1]);
vec3 color = getColorFromTexture(tValue[0], 0, volume.transferFunctionsSampleHeight[0]) * mix(vec3(1.0), colorizingColor, colorizingOpacity); color = applyAllLightning(color, opacity, posVC, normalLight); return vec4(color, opacity); }
vec4 getColorForDefaultIndependentPreset(vec4 tValue, vec3 posIS) {
#if defined(EnabledGradientOpacity) || vtkNumberOfLights > 0 mat4 normalMat = computeMat4Normal(posIS, tValue); #endif
#if defined(vtkComponent0Proportional) float alpha = 1.0; #else float alpha = 0.0; #endif
vec3 mixedColor = vec3(0.0); #if vtkNumberOfComponents > 0 { const int component = 0; vec3 color = getColorFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); float opacity = getOpacityFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); #if !defined(vtkComponent0Proportional) float alphaContribution = volume.independentComponentMix[component] * opacity; #ifdef EnabledGradientOpacity alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component); #endif alpha += alphaContribution; #if vtkNumberOfLights > 0 color = applyLighting(color, normalMat[component]); #endif #else color *= opacity; alpha *= mix(opacity, 1.0, (1.0 - volume.independentComponentMix[component])); #endif mixedColor += volume.independentComponentMix[component] * color; } #endif #if vtkNumberOfComponents > 1 { const int component = 1; vec3 color = getColorFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); float opacity = getOpacityFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); #if !defined(vtkComponent1Proportional) float alphaContribution = volume.independentComponentMix[component] * opacity; #ifdef EnabledGradientOpacity alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component); #endif alpha += alphaContribution; #if vtkNumberOfLights > 0 color = applyLighting(color, normalMat[component]); #endif #else color *= opacity; alpha *= mix(opacity, 1.0, (1.0 - volume.independentComponentMix[component])); #endif mixedColor += volume.independentComponentMix[component] * color; } #endif #if vtkNumberOfComponents > 2 { const int component = 2; vec3 color = getColorFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); float opacity = getOpacityFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); #if !defined(vtkComponent2Proportional) float alphaContribution = volume.independentComponentMix[component] * opacity; #ifdef EnabledGradientOpacity alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component); #endif alpha += alphaContribution; #if vtkNumberOfLights > 0 color = applyLighting(color, normalMat[component]); #endif #else color *= opacity; alpha *= mix(opacity, 1.0, (1.0 - volume.independentComponentMix[component])); #endif mixedColor += volume.independentComponentMix[component] * color; } #endif #if vtkNumberOfComponents > 3 { const int component = 3; vec3 color = getColorFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); float opacity = getOpacityFromTexture( tValue[component], component, volume.transferFunctionsSampleHeight[component]); #if !defined(vtkComponent3Proportional) float alphaContribution = volume.independentComponentMix[component] * opacity; #ifdef EnabledGradientOpacity alphaContribution *= computeGradientOpacityFactor(normalMat[component].a, component); #endif alpha += alphaContribution; #if vtkNumberOfLights > 0 color = applyLighting(color, normalMat[component]); #endif #else color *= opacity; alpha *= mix(opacity, 1.0, (1.0 - volume.independentComponentMix[component])); #endif mixedColor += volume.independentComponentMix[component] * color; } #endif
return vec4(mixedColor, alpha); }
vec4 getColorForDependentComponents(vec4 tValue, vec3 posVC, vec3 posIS) { #if defined(EnabledGradientOpacity) || vtkNumberOfLights > 0 vec3 scalarInterp[2]; vec4 normal0 = computeNormalForDensity(posIS, scalarInterp, 3); float gradientOpacity = computeGradientOpacityFactor(normal0.a, 0); #endif
#if vtkNumberOfComponents == 1 vec3 tColor = getColorFromTexture(tValue.r, 0, 0.5); float alpha = getOpacityFromTexture(tValue.r, 0, 0.5); #endif #if vtkNumberOfComponents == 2 vec3 tColor = vec3(tValue.r * volume.colorTextureScale[0] + volume.colorTextureShift[0]); float alpha = getOpacityFromTexture(tValue.a, 1, 0.5); #endif #if vtkNumberOfComponents == 3 vec3 tColor = tValue.rgb * volume.colorTextureScale.rgb + volume.colorTextureShift.rgb; float alpha = getOpacityFromTexture(tValue.a, 0, 0.5); #endif #if vtkNumberOfComponents == 4 vec3 tColor = tValue.rgb * volume.colorTextureScale.rgb + volume.colorTextureShift.rgb; float alpha = getOpacityFromTexture(tValue.a, 3, 0.5); #endif
#if defined(EnabledGradientOpacity) alpha *= gradientOpacity; #endif
#if vtkNumberOfComponents == 1 if (alpha < EPSILON) { return vec4(0.0); } #endif
#if vtkNumberOfLights > 0 vec4 normalLight; if (volume.computeNormalFromOpacity == 1) { if (normal0[3] != 0.0) { normalLight = computeDensityNormal(scalarInterp, 0.5, gradientOpacity, 0); if (normalLight[3] == 0.0) { normalLight = normal0; } } } else { normalLight = normal0; } tColor = applyAllLightning(tColor, alpha, posVC, normalLight); #endif
return vec4(tColor, alpha); }
vec4 getColorForValue(vec4 tValue, vec3 posVC, vec3 posIS) { #ifdef EnableColorForValueFunctionId0 return getColorForDependentComponents(tValue, posVC, posIS); #endif
#ifdef EnableColorForValueFunctionId1 return getColorForAdditivePreset(tValue, posVC, posIS); #endif
#ifdef EnableColorForValueFunctionId2 return getColorForColorizePreset(tValue, posVC, posIS); #endif
#ifdef EnableColorForValueFunctionId3
#endif
#if defined(EnableColorForValueFunctionId4) || defined(EnableColorForValueFunctionId3) return getColorForDefaultIndependentPreset(tValue, posIS); #endif
#ifdef EnableColorForValueFunctionId5 return getColorForLabelOutline(); #endif }
bool valueWithinScalarRange(vec4 val) { #if vtkNumberOfComponents > 1 && !defined(EnabledIndependentComponents) return false; #endif vec4 rangeMin = volume.ipScalarRangeMin; vec4 rangeMax = volume.ipScalarRangeMax; for (int component = 0; component < vtkNumberOfComponents; ++component) { if (val[component] < rangeMin[component] || rangeMax[component] < val[component]) { return false; } } return true; }
#if vtkBlendMode == LABELMAP_EDGE_PROJECTION_BLEND bool checkOnEdgeForNeighbor(int xFragmentOffset, int yFragmentOffset, int segmentIndex, vec3 stepIS) { vec3 volumeDimensions = vec3(volume.dimensions); vec4 neighborPixelCoord = vec4(gl_FragCoord.x + float(xFragmentOffset), gl_FragCoord.y + float(yFragmentOffset), gl_FragCoord.z, gl_FragCoord.w); vec3 originalNeighborPosIS = fragCoordToIndexSpace(neighborPixelCoord);
vec3 neighborPosIS = originalNeighborPosIS; for (int k = 0; k < vtkMaximumNumberOfSamples / 2; ++k) { ivec3 texCoord = ivec3(neighborPosIS * volumeDimensions); vec4 texValue = rawFetchTexture(texCoord); if (int(texValue.g) == segmentIndex) { return false; } neighborPosIS += stepIS; }
neighborPosIS = originalNeighborPosIS; for (int k = 0; k < vtkMaximumNumberOfSamples / 2; ++k) { ivec3 texCoord = ivec3(neighborPosIS * volumeDimensions); vec4 texValue = rawFetchTexture(texCoord); if (int(texValue.g) == segmentIndex) { return false; } neighborPosIS -= stepIS; }
float sampleHeight = volume.transferFunctionsSampleHeight[1]; vec3 tColorSegment = getColorFromTexture(float(segmentIndex), 1, sampleHeight); float pwfValueSegment = getOpacityFromTexture(float(segmentIndex), 1, sampleHeight); gl_FragData[0] = vec4(tColorSegment, pwfValueSegment); return true; } #endif
vec4 getColorAtPos(vec3 posVC) { vec3 posIS = posVCtoIS(posVC); vec4 texValue = getTextureValue(posIS); return getColorForValue(texValue, posVC, posIS); }
void applyBlend(vec3 rayOriginVC, vec3 rayDirVC, float minDistance, float maxDistance) { vec3 stepVC = rayDirVC * sampleDistance; float raySteps = (maxDistance - minDistance) / sampleDistance;
float jitter = 0.01 + 0.99 * fragmentSeed;
#if vtkBlendMode == COMPOSITE_BLEND vec3 firstPosVC = rayOriginVC + minDistance * rayDirVC; vec4 firstColor = getColorAtPos(firstPosVC);
if (raySteps <= 1.0) { firstColor.a = 1.0 - pow(1.0 - firstColor.a, raySteps); gl_FragData[0] = firstColor; return; }
firstColor.a = 1.0 - pow(1.0 - firstColor.a, jitter); vec4 color = vec4(firstColor.rgb * firstColor.a, firstColor.a); vec3 posVC = firstPosVC + jitter * stepVC; float stepsTraveled = jitter;
for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) { if (stepsTraveled + 1.0 >= raySteps) { break; } vec4 tColor = getColorAtPos(posVC);
color = color + vec4(tColor.rgb * tColor.a, tColor.a) * (1.0 - color.a); stepsTraveled++; posVC += stepVC; if (color.a > 0.99) { color.a = 1.0; break; } }
if (color.a < 0.99 && (raySteps - stepsTraveled) > 0.0) { vec3 endPosVC = rayOriginVC + maxDistance * rayDirVC; vec4 tColor = getColorAtPos(endPosVC); tColor.a = 1.0 - pow(1.0 - tColor.a, raySteps - stepsTraveled);
float mix = (1.0 - color.a); color = color + vec4(tColor.rgb * tColor.a, tColor.a) * mix; }
gl_FragData[0] = vec4(color.rgb / color.a, color.a); #endif
#if vtkBlendMode == MAXIMUM_INTENSITY_BLEND || \ vtkBlendMode == MINIMUM_INTENSITY_BLEND
#if vtkBlendMode == MAXIMUM_INTENSITY_BLEND #define OP max #else #define OP min #endif
vec3 posVC = rayOriginVC + minDistance * rayDirVC; float stepsTraveled = 0.0;
vec4 selectedValue; vec3 selectedPosVC; vec3 selectedPosIS; { vec3 posIS = posVCtoIS(posVC); selectedValue = getTextureValue(posIS); selectedPosVC = posVC; selectedPosIS = posIS; }
if (raySteps <= 1.0) { gl_FragData[0] = getColorForValue(selectedValue, selectedPosVC, selectedPosIS); return; }
posVC += jitter * stepVC; stepsTraveled += jitter;
for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) { if (stepsTraveled + 1.0 >= raySteps) { break; }
vec3 posIS = posVCtoIS(posVC); vec4 previousSelectedValue = selectedValue; vec4 currentValue = getTextureValue(posIS); selectedValue = OP(selectedValue, currentValue); if (previousSelectedValue != selectedValue) { selectedPosVC = posVC; selectedPosIS = posIS; }
stepsTraveled++; posVC += stepVC; }
posVC = rayOriginVC + maxDistance * rayDirVC; { vec3 posIS = posVCtoIS(posVC); vec4 previousSelectedValue = selectedValue; vec4 currentValue = getTextureValue(posIS); selectedValue = OP(selectedValue, currentValue); if (previousSelectedValue != selectedValue) { selectedPosVC = posVC; selectedPosIS = posIS; } }
gl_FragData[0] = getColorForValue(selectedValue, selectedPosVC, selectedPosIS); #endif
#if vtkBlendMode == ADDITIVE_INTENSITY_BLEND || \ vtkBlendMode == AVERAGE_INTENSITY_BLEND vec4 sum = vec4(0.); #if vtkBlendMode == AVERAGE_INTENSITY_BLEND float totalWeight = 0.0; #endif vec3 posVC = rayOriginVC + minDistance * rayDirVC; float stepsTraveled = 0.0;
vec3 posIS = posVCtoIS(posVC); vec4 value = getTextureValue(posIS);
if (raySteps <= 1.0) { gl_FragData[0] = getColorForValue(value * raySteps, posVC, posIS); return; }
if (valueWithinScalarRange(value)) { sum += value * jitter; #if vtkBlendMode == AVERAGE_INTENSITY_BLEND totalWeight += jitter; #endif } posVC += jitter * stepVC; stepsTraveled += jitter;
for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) { if (stepsTraveled + 1.0 >= raySteps) { break; }
posIS = posVCtoIS(posVC); value = getTextureValue(posIS); if (valueWithinScalarRange(value)) { sum += value; #if vtkBlendMode == AVERAGE_INTENSITY_BLEND totalWeight++; #endif }
stepsTraveled++; posVC += stepVC; }
posVC = rayOriginVC + maxDistance * rayDirVC; posIS = posVCtoIS(posVC); value = getTextureValue(posIS); if (valueWithinScalarRange(value)) { sum += value; #if vtkBlendMode == AVERAGE_INTENSITY_BLEND totalWeight += raySteps - stepsTraveled; #endif }
#if vtkBlendMode == AVERAGE_INTENSITY_BLEND sum /= vec4(totalWeight, totalWeight, totalWeight, 1.0); #endif
gl_FragData[0] = getColorForValue(sum, posVC, posIS); #endif
#if vtkBlendMode == RADON_TRANSFORM_BLEND float normalizedRayIntensity = 1.0; vec3 posVC = rayOriginVC + minDistance * rayDirVC; float stepsTraveled = 0.0;
if (raySteps <= 1.0) { vec3 posIS = posVCtoIS(posVC); vec4 tValue = getTextureValue(posIS); normalizedRayIntensity -= raySteps * sampleDistance * getOpacityFromTexture(tValue.r, 0, 0.5); gl_FragData[0] = vec4(getColorFromTexture(normalizedRayIntensity, 0, 0.5), 1.0); return; }
posVC += jitter * stepVC; stepsTraveled += jitter;
for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) { if (stepsTraveled + 1.0 >= raySteps) { break; }
vec3 posIS = posVCtoIS(posVC); vec4 value = getTextureValue(posIS); normalizedRayIntensity -= sampleDistance * getOpacityFromTexture(value.r, 0, 0.5);
posVC += stepVC; stepsTraveled++; }
gl_FragData[0] = vec4(getColorFromTexture(normalizedRayIntensity, 0, 0.5), 1.0); #endif
#if vtkBlendMode == LABELMAP_EDGE_PROJECTION_BLEND vec3 posVC = rayOriginVC + minDistance * rayDirVC; float stepsTraveled = 0.0; vec3 posIS = posVCtoIS(posVC); vec4 tValue = getTextureValue(posIS); if (raySteps <= 1.0) { gl_FragData[0] = getColorForValue(tValue, posVC, posIS); return; }
vec3 stepIS = vecVCToIS(stepVC); vec4 value = tValue; posIS += jitter * stepIS; stepsTraveled += jitter; vec3 maxPosIS = posIS; int segmentIndex = int(value.g); bool originalPosHasSeenNonZero = false;
if (segmentIndex != 0) { setLabelOutlineBit(segmentIndex); }
for (int i = 0; i < vtkMaximumNumberOfSamples; ++i) { if (stepsTraveled + 1.0 >= raySteps) { break; }
tValue = getTextureValue(posIS); segmentIndex = int(tValue.g);
if (segmentIndex != 0) { originalPosHasSeenNonZero = true; setLabelOutlineBit(segmentIndex); }
if (tValue.r > value.r) { value = tValue; maxPosIS = posIS; }
stepsTraveled++; posIS += stepIS; }
posIS = posVCtoIS(rayOriginVC + maxDistance * rayDirVC); tValue = getTextureValue(posIS);
if (tValue.r > value.r) { value = tValue; maxPosIS = posIS; }
if (!originalPosHasSeenNonZero) { vec3 maxPosVC = posIStoVC(maxPosIS); gl_FragData[0] = getColorForValue(value, maxPosVC, maxPosIS); return; }
vec3 neighborRayStepsIS = stepIS; float neighborRaySteps = raySteps; bool shouldLookInAllNeighbors = false;
vec3 volumeSpacings = volume.spacing; float minVoxelSpacing = min(volumeSpacings[0], min(volumeSpacings[1], volumeSpacings[2])); vec4 base = vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w);
vec4 baseXPlus = vec4(gl_FragCoord.x + 1.0, gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w); vec4 baseYPlus = vec4(gl_FragCoord.x, gl_FragCoord.y + 1.0, gl_FragCoord.z, gl_FragCoord.w);
vec3 baseWorld = fragCoordToWorld(base); vec3 baseXPlusWorld = fragCoordToWorld(baseXPlus); vec3 baseYPlusWorld = fragCoordToWorld(baseYPlus);
float XPlusDiff = length(baseXPlusWorld - baseWorld); float YPlusDiff = length(baseYPlusWorld - baseWorld);
float minFragSpacingWorld = min(XPlusDiff, YPlusDiff);
for (int s = 1; s < MAX_SEGMENT_INDEX; s++) { if (!isLabelOutlineBitSet(s)) { continue; }
float textureCoordinate = float(s - 1) / 1024.0; float textureValue = texture2D(labelOutlineThicknessTexture, vec2(textureCoordinate, 0.5)).r;
int actualThickness = int(textureValue * 255.0);
bool onEdge = checkOnEdgeForNeighbor(-actualThickness, -actualThickness, s, stepIS) || checkOnEdgeForNeighbor(actualThickness, actualThickness, s, stepIS) || checkOnEdgeForNeighbor(actualThickness, -actualThickness, s, stepIS) || checkOnEdgeForNeighbor(-actualThickness, +actualThickness, s, stepIS);
if (onEdge) { return; }
if (minVoxelSpacing > (2.0 * float(actualThickness) - 1.0) * minFragSpacingWorld) { continue; }
for (int i = -actualThickness; i <= actualThickness; i++) { for (int j = -actualThickness; j <= actualThickness; j++) { if (i == 0 && j == 0) continue; if (abs(i) == actualThickness && abs(j) == actualThickness) continue; if (checkOnEdgeForNeighbor(i, j, s, stepIS)) { return; } } } }
float sampleHeight = volume.transferFunctionsSampleHeight[0]; vec3 tColor0 = getColorFromTexture(value.r, 0, sampleHeight); float pwfValue0 = getOpacityFromTexture(value.r, 0, sampleHeight); gl_FragData[0] = vec4(tColor0, pwfValue0); #endif }
vec2 computeRayDistances(vec3 rayOriginVC, vec3 rayDirVC) { vec2 dists = rayIntersectVolumeDistances(rayOriginVC, rayDirVC);
dists.x = max(0.0, dists.x);
float farDist = -camThick / rayDirVC.z; dists.y = min(farDist, dists.y);
return dists; }
float getFragmentSeed() { float firstNoise = fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453); float secondNoise = texture2D(jtexture, gl_FragCoord.xy / 32.0).r; float noiseSum = firstNoise + secondNoise; return noiseSum < 1.0 ? noiseSum : noiseSum - 1.0; }
void main() { fragmentSeed = getFragmentSeed();
if (cameraParallel == 1) { rayDirVC = vec3(0.0, 0.0, -1.0); } else { rayDirVC = normalize(vertexVCVSOutput); }
vec3 rayOriginVC = vertexVCVSOutput; vec2 rayStartEndDistancesVC = computeRayDistances(rayOriginVC, rayDirVC); if (rayStartEndDistancesVC[1] <= rayStartEndDistancesVC[0] || rayStartEndDistancesVC[1] <= 0.0) { discard; }
applyBlend(rayOriginVC, rayDirVC, rayStartEndDistancesVC[0], rayStartEndDistancesVC[1]); }
|