//pref
lightAmount|float|0.0|0.3|1
ambient|float|0.0|0.0|1.0
diffuse|float|0.0|0.5|1.0
specular|float|0.0|0.9|1.0
shininess|float|0.01|10.0|30
boundExp|float|0.0|0.0|3
Phong surface lighting (specular, diffuse, ambient). Generally, keep ambient at zero and adjust lightAmount to fill unilluminated regions.|note

//vert
void main() {
	gl_TexCoord[1] = gl_MultiTexCoord1;
	gl_Position = ftransform();
}
//frag
uniform int loops;
uniform float stepSize, sliceSize, viewWidth, viewHeight;
uniform sampler3D intensityVol; //3DTexture containing brightness
uniform sampler3D gradientVol; //3DTexture containing gradient direction and magnitude
uniform sampler2D backFace;
uniform vec3 clearColor,lightPosition, clipPlane;
uniform float clipPlaneDepth;
uniform float ambient,diffuse,specular,shininess, boundExp, lightAmount;

const vec3 lightColor = vec3(1.0,1.0,1.0);
const vec3 materialColor = vec3(1.0,1.0,1.0);

vec3 shading(vec3 N,vec3 V,vec3 L)
{
	vec3 final_color = vec3(ambient,ambient,ambient);
	float lambertTerm = dot(N,L);
	if (lambertTerm > 0.0) {
		final_color += lightColor * materialColor * lambertTerm * diffuse;			
		vec3 E = normalize(V);
		vec3 R = reflect(-L, N);
		float specularTerm = pow( max(dot(R, E), 0.0),shininess );
		final_color += lightColor * specularTerm * specular;	
	}
	return final_color;
}

void main() {
	// get normalized pixel coordinate in view port (e.g. [0,1]x[0,1])
	vec2 pixelCoord = gl_FragCoord.st;
	pixelCoord.x /= viewWidth;
	pixelCoord.y /= viewHeight;	
	// starting position of the ray is stored in the texture coordinate
	vec3 start = gl_TexCoord[1].xyz;
	vec3 backPosition = texture2D(backFace,pixelCoord).xyz;
	vec3 dir = backPosition - start;
	float len = length(dir);
	dir = normalize(dir);
	//next see if clip plane intersects ray
	if (clipPlaneDepth > -0.5) {
		gl_FragColor.rgb = vec3(1.0,0.0,0.0);
		//next, see if clip plane faces viewer
		bool frontface = (dot(dir , clipPlane) > 0.0);
		//next, distance from ray origin to clip plane
		float dis = dot(dir,clipPlane);
		if (dis != 0.0  )  dis = (-clipPlaneDepth - dot(clipPlane, start.xyz-0.5)) / dis;
		//we set "len = 0.0"  instead of "discard" or "return": return fails on 2006MacBookPro10.4ATI1900, discard fails on MacPro10.5NV8800 will not discard
		if ((frontface) && (dis >= len)) len = 0.0;
		if ((!frontface) && (dis <= 0.0)) len = 0.0;
		if ((dis > 0.0) && (dis < len)) {
			if (frontface) {
				start = start + dir * dis;
			} else {
				backPosition =  start + dir * (dis); 
			}
			dir = backPosition - start;
			len = length(dir);
			dir = normalize(dir);		
		}
	}	

	vec3 deltaDir = dir * stepSize;
	vec4 colorSample,gradientSample,colAcc = vec4(0.0,0.0,0.0,0.0);
	float lengthAcc = 0.0;
	//We need to calculate the ray's starting position. We add a random
	//fraction of the stepsize to the original starting point to dither the output
	vec3 samplePos = start.xyz + deltaDir* (fract(sin(gl_FragCoord.x * 12.9898 + gl_FragCoord.y * 78.233) * 43758.5453));
	vec4 prevNorm = vec4(0.0,0.0,0.0,0.0);
	float stepSizex2 = stepSize * 2.0;
	for(int i = 0; i < loops; i++) {
		colorSample = texture3D(intensityVol,samplePos);
		colorSample.a = 1.0-pow((1.0 - colorSample.a), stepSize/sliceSize);//opacity correction
		if ((colorSample.a > 0.01) && (lengthAcc > stepSizex2)  ) {	
			gradientSample= texture3D(gradientVol,samplePos); //interpolate gradient direction and magnitude
			gradientSample.rgb = normalize(gradientSample.rgb*2.0 - 1.0); //direction saved as 0..1, rescale to -1..1
			//re-use previous normal if it has larger magnitude
			if (gradientSample.a < prevNorm.a) 
				gradientSample.rgb = prevNorm.rgb;
			prevNorm = gradientSample;
			if (boundExp > 0.0)
				colorSample.a = colorSample.a * pow(gradientSample.a,boundExp);	
			if (lightAmount > 0.0) {
		  		vec3 N = normalize(vec3(gradientSample.r,gradientSample.g,gradientSample.b));
				vec3 phong = shading(N,-dir,lightPosition);
				if (lightAmount < 1.0) {
					phong = (lightAmount) * (phong-0.3);
					colorSample.rgb += phong;
				} else {
					colorSample.rgb = phong;
				}
			
		  	}
		}

		colorSample.rgb *= colorSample.a; 
		//accumulate color
		colAcc= (1.0 - colAcc.a) * colorSample + colAcc;
		samplePos += deltaDir;
		lengthAcc += stepSize;
		// terminate if opacity > 1 or the ray is outside the volume
		if ( lengthAcc >= len || colAcc.a > 0.95 )
			break;
	}
	colAcc.a = colAcc.a/0.95;
	if ( colAcc.a < 1.0 )
		colAcc.rgb = mix(clearColor,colAcc.rgb,colAcc.a);
	if (len == 0.0) colAcc.rgb = clearColor;
	gl_FragColor = colAcc;
}