uniform sampler3D volumeTexture;      //Texture containing gradient and density
uniform sampler3D magnitudeTexture; //Texture containing gradient magnitude
uniform sampler3D laplacianTexture; //Texture containing laplacian filter
uniform sampler2D colorTexture; //Texture containing color transfer function
varying float distFromCam; //Fragments distance from camera, determined at
//slice vertices
uniform float volDistFromCam; //Cameras from volume extrema
uniform float volDiag; //Length of volume's diagonal
uniform float distCont, distExp; //Contribution of distance color and scaling exponent
uniform vec3 distCol; //Distance color
uniform float normalCont; //Contribution of transfer function color
uniform float toneCont; //Contribution of tone color
uniform vec3 warmCol, coolCol; //Warm and cool tone colors
uniform float oriOpacCont; //Contribution of transfer function opacity
uniform float boundCont, boundExp; //Contribution of boundary enhancement calculated opacity
//and scaling exponent
uniform float invMaxGrad; //The inverse of the maximum gradient magnitude
uniform float invMaxLap; //The inverse of the maximum laplacian value
uniform float ambient; //Ambient light contribution
uniform float distClip; //fragments closer than this value will be discarded
uniform float silhCont, silhExp; //Opacity contribution of silhouette function and
//scaling factor
uniform float edgeThresh, edgeExp; //Values determing which "edge" voxels should be highlighted
uniform float lapThresh, lapExp; //Values determing which "crease" voxels should be highlighted
uniform vec3 toonCol; //The toon shading color

//Variables toggling certain enhancements
uniform bool doBoundary, doSilhouette, doDistance, doTone, doNormal, doEdge, doLap, doToon, doLighting; void main()
//The first step is to look up this fragments texture info in the various //texture arrays we have setup. //This texture contains the normalized, scaled gradient in the rgb channels //and the voxel's density in the alpha channel vec4 texValue = texture3D(volumeTexture, gl_TexCoord[0].stp); //This texture contains a mapping from the volumes density to a color //and a opacity. So we use the voxel's denisty as a texture coordinate. vec4 texColor = texture2D(colorTexture, vec2(texValue.a,0)); //Texture containing voxel's gradient magnitude vec4 gradMag = texture3D(magnitudeTexture, gl_TexCoord[0].stp);

//This texture contains the filtered laplacian value at this
vec4 lapValue = texture3D(laplacianTexture, gl_TexCoord[0].stp); //We have to scale the gradient between -1 and 1 texValue.rgb = texValue.rgb*2.0 - 1.0; //Now we can project the gradient into eye space. If the gradient //is degenerate, we do not want to try to normalize it (this causes //garbage to be put in the gradient) bool degenerate = length(texValue.rgb) < .1; if(!degenerate) texValue.rgb = normalize(gl_NormalMatrix * normalize(texValue.rgb)); //Calculate how far the voxel is into the volume relative to the //viewer float distance = (distFromCam-volDistFromCam)/volDiag; //If the alpha is zeror or the distance is less than //the clip value, discard this fragment if(texColor.a == 0 || distance < distClip) discard; //Create a vector where the final color will be stored vec4 finalColor = vec4(0.0,0.0,0.0,0.0);

//Calculate the diffuse contribution of the light
float dotLight = dot(normalize(vec3(-gl_LightSource[0].position)), texValue.rgb); //Create copy of dot product for tone shading float toneLight = dotLight; dotLight += ambient; //Add in the ambient light
dotLight = clamp(dotLight, 0.0, 1.0); if(!doLighting) { dotLight = 1.0; //If lighting is off, make the fragment fully lit
toneLight = 1.0;
//Calculate the angle between the viewer and the gradient in eye space //The viewer always looks down 0,0,-1 in eye space float dotView = dot(vec3(0,0,-1), texValue.rgb); //If we are contributing the transfer function color, add it in weighted //by the diffuse and normal contributions if(doNormal) {
finalColor.rgb = normalCont*dotLight*texColor.rgb;

//If we are doing tone shading, add in the correct mix of cool and warm
if(doTone) { //Calculate the correct color according to the Gooch shading model. //toneLight ranges from -1 to 1 finalColor.rgb += toneCont*(((1.0+toneLight)*.5)*warmCol + (1.0-((1.0+toneLight)*.5))*coolCol);
//Initialize the final opacity to the transfer function's opacity //contribution finalColor.a = oriOpacCont*texColor.a;

//If we want to enhance the boundaries, we can add in opacity
//scaled by the normalized gradient magnitude raised to an
//exponent. So magnitudes of 0 will add no opaqueness, magnitudes
//of 1 will add the maximum amount of opaqueness.
finalColor.a += boundCont*pow(invMaxGrad*gradMag.r,boundExp); //If we want to enhance silhoutte edges, we can make sure the gradient //is not degenerate and change the opacity based on the voxel's //perpendicularity relative to the viewer. if(doSilhouette) { if(!degenerate)
finalColor.a += silhCont*pow(1.0-abs(dotView),silhExp);
//If we want to do toon shading we... if(doToon){
//...create a set of 4 colors that go from bright
//to the specified toon color
vec3 col = toonCol - vec3(1.0, 1.0, 1.0); vec4 color1 = vec4(toonCol - .75*col, finalColor.a); vec4 color2 = vec4(toonCol - .5*col, finalColor.a); vec4 color3 = vec4(toonCol - .25*col, finalColor.a); vec4 color4 = vec4(toonCol, finalColor.a); //and then assign different ranges of the diffuse //coefficient to one of the 4 colors if (dotLight > 0.95) finalColor = color1; else if (dotLight > 0.5) finalColor = color2; else if (dotLight > 0.25) finalColor = color3; else finalColor = color4;
//If distance coloring is toggled, we mix the color so far //with the specified distance color depending on how far //this fragment is into the volume (a value between 0 and 1) //raised to an exponent to scale the effect. if(doDistance) { float cont = distCont*(pow(distance, distExp)); finalColor.rgb = (1-cont)*finalColor.rgb + cont*distCol;

//If edge highlighting is toggled we darken the voxel based on its perpendicularity
//to the viewing direction.
if(doEdge) { //We make very perpendicular voxels black, and lesser perpendicular voxels //decreasingly dark until the value is below the threshold, when no //enhancement is performed. float edgeVal = pow(1.0-abs(dotView),edgeExp); if(edgeVal >= edgeThresh && !degenerate) finalColor.rgb = mix(finalColor.rgb, vec3(0.0,0.0,0.0), pow((edgeVal-edgeThresh)/(1.0-edgeThresh),4));
//Attempt to highlight creases using a second order derivative of the volume. //Laplacian values are scaled between 0 and 1 if(doLap) { float lapVal = pow(abs(lapValue.r)*invMaxLap,lapExp); if(lapVal >= lapThresh || lapVal <= 1.0 - lapThresh) finalColor.rgb = 0.0;
//Apply the final color the fragment. gl_FragColor = finalColor;