//TEXTURE

uniform sampler2D txColorFront;
uniform sampler2D txColorBack;
uniform sampler3D txVolume;
uniform sampler1D txGradTransfer;
uniform sampler1D txDensityTransfer;
uniform sampler2D txNoise;

//QUALITY PERFORMANCE

uniform float stepSize;	
uniform int   maxSamples;
uniform vec3  gradDelta;
uniform float minClip;
uniform bool  applyNoise;

//LIGHTING

uniform vec3 lightPos;
uniform vec3 lightColor;
uniform bool bLocalViewer;
uniform bool bHeadLight;
uniform bool bEnableLight;

//SHADING

uniform float ambience;
uniform float specularity;

//SHADOWS

uniform bool  shadowEnabled;
uniform int   shadowSamples;
uniform float shadowTreshold; 

//TEXTURE ACCESS 

varying vec2 fragpos;

//LIGHTDIR

varying vec3 lightDirNL;

//GLOBAL FRAGVARS 

vec3  rayStart;
vec3  rayEnd;
vec3  rayDir;
float rayLength;
float rayStep;
vec3  samplePos;
vec3  lightDir;

//GLOBAL SAMPLEVARS

float density;
vec3  grad;
float gradLength;
vec4  sampleCol;
float diffuse;
float specular;
vec3  litColor;
float shadowMultiply;

//GLOBAL FRAGRESULT

vec4 resultCol;

 /*********************** DVR FRAGMENT SHADER *****************\
|*                                                             *|
|*  APPROACH : BACK FRONT COLOR TEXTURES                       *|
|*  VERSION  : 1.2                                             *|
|*  (c)      : Robert Hausmair                                 *|
|*                                                             *|
 \*************************************************************/

//FUNCTION DELARATIONS

void Grad1();
void Grad2();
void ModulateColor();
void ModulateAll();
void ModulateNone();
void DoLighting();
void DoShadow();

void main(){

	/*-----------------------------*/
	/*---INIT FOR EVERY FRAGMENT---*/
	/*-----------------------------*/

	rayStart  = texture2D(txColorFront,fragpos).xyz;
	rayEnd    = texture2D(txColorBack,fragpos).xyz;
	rayDir    = normalize(rayEnd - rayStart);
	rayLength = length(rayEnd - rayStart);
	rayStep   = 0.0;
	resultCol = vec4(0.0,0.0,0.0,0.0);
	
	// STOCHASTIC RAY SHIFTING

	if(applyNoise)
	{
		rayStart  += (texture2D(txNoise,fragpos).r)  * stepSize * 5 *  rayDir;
	}

	/*-----------------------------------------*/ 
	/*---RAYMARCHING AND SAMPLE ACCUMULATION---*/
	/*-----------------------------------------*/ 

	for(int i = 0 ; i < maxSamples ; i++)
	{
		samplePos = rayStart + (rayDir * rayStep);
		density   = texture3D(txVolume,samplePos).a;

		// SKIP MINOR CONTRIBUTION BELOW TRESHOLD
		
		if(density > minClip)
		{
			//COMPUTE GRADIENT USING CENTRAL DIFFERENCE
	
			Grad1();
			//Grad2;
			gradLength = length(grad);
			
			//CLASSIFICATION BASED ON DENSITY AND GRADIENT MAGNITUDE

			//ModulateNone();
			//ModulateColor();
			ModulateAll();
			
			//LOCAL ILLUMINATION MODEL

			if(bEnableLight)
			{
				//COMPUTE LIGHT VECTOR

				if(bLocalViewer)
				{
					if(bHeadLight)
					{
						lightDir = rayDir;
					}
					else
					{
						lightDir = normalize(samplePos - lightPos);
					}
				}
				else
				{
					lightDir = normalize(lightDirNL);
				}

				// CALL LIGHTING FUNCTION

				if(gradLength > 0.01)
				{
					DoLighting();
				}
				else
				{
					diffuse = specular = 2.0 / 3.1415926535;
				}

				// CALL SHADOW FUNCTION

				if(shadowEnabled)
				{
					shadowMultiply = 1.0;
					DoShadow();

					litColor = (( (diffuse + ambience) * sampleCol.xyz * lightColor) + (specular * specularity * shadowMultiply) ) * shadowMultiply;
				}
				else
				{
					litColor = ( (diffuse + ambience) * sampleCol.xyz * lightColor) + (specular * specularity);
				}
			}
			else
			{
				litColor = sampleCol.rgb;
			}
	
			//OPACITY CORRECTION
			
			sampleCol.a = 1.0 - pow(1.0-sampleCol.a,stepSize/0.001);	

			//FRONT TO BACK COMPOSITING

			resultCol.rgb += (1.0 - resultCol.a) * sampleCol.a * litColor;
			resultCol.a   += (1.0 - resultCol.a) * sampleCol.a;
			
		}
			
		/*--------------------*/ 
		/*---STOP CONDITION---*/	
		/*--------------------*/ 	
		
		if( (rayStep += stepSize) > rayLength || resultCol.a > 0.98){
			i = maxSamples;
		}		
		
	}

	/*---DEPTH BUFFER WRITE---*/
	
	if(resultCol.a > 0.0){
		gl_FragDepth = gl_FragCoord.z;
	}else{
		gl_FragDepth = 1.0;
	}

	/*---OUTPUT---*/

	gl_FragColor = resultCol;
}