Color refraction_shader::Shade( const Scene &scene, const HitInfo &hit ) const{    
	  
	if(gen >= max_depth)
		return scene.envmap->Shade(hit.ray);

	if( mat->emission != black )
		if (_ray.type == indirect_ray) //turnign off the light
			return black;
		else
			return mat->emission; //this si direct sample, so return illum of the light
	
	if(mat->reflectivity != black && _ray.type == indirect_ray)
		return black;
		
	Vec3   O = _ray.origin;
	Vec3   P = hit.point;
	Vec3   N = hit.normal;
	Vec3   E = Unit( O - P );
	Vec3   R = Unit( ( 2.0 * ( E * N ) ) * N - E );
	if( E * N < 0.0 ) N = -N; //flip the normal to make it point into the space of the ray origin

	if(mat->translucency != black ){
		Ray rr = obj->getRefracetedRay(_ray,P,N);
		if(rr.direction!=Vec3(0,0,0)) 
			return scene.Trace(rr)*mat->translucency 
			+ scene.Trace(obj->getReflectedRay(_ray,P,N))*(white-mat->translucency);
		else return black;//this is TIR even after 4 level of internal reflections
	}
	
	unsigned k = 1,n = 1;
		
	if(gen == 1 ){ // rays of tree depth = 1
		n = numDirectRays;
		k = numIndirectRays;
	}

	Color color = mat->diffuse * mat->ambient;

	Sample *dSamples = new Sample[n*n];
	unsigned numSamples;
	HitInfo dHit;		
	Ray dRay,iRay;		
	unsigned Y = scene.NumLights() ;

	for( unsigned i = 0; i < Y; i++ ){
		const Object *light = scene.GetLight(i);
		numSamples = light->GetSamples (hit.point, hit.normal, dSamples, n);
			//#pragma omp parallel for 
		for(unsigned j=0;j<numSamples ;j++){
			dRay.origin = hit.point + Unit(N)*10E-4;
			dRay.direction = Unit( dSamples[j].P - hit.point );
			dRay.generation = gen + 1; //next level in tree
			dHit.ray = dRay;
			dHit.distance = Infinity;
			dRay.type = _ray.type;
			if(scene.Cast(dRay,dHit))
				if( dHit.object == light){
					color += max( 0, dRay.direction*Unit(N) )*
							mat->diffuse*
							light->material->emission*dSamples[j].w/Pi;
					if( mat->Phong_exp !=0 && mat->specular != black) 
							//this si handling specularity
						color += (white-mat->reflectivity)* 
							pow( max( dRay.direction*R,0), mat->Phong_exp) * 
							mat->specular*light->material->emission*dSamples[j].w;
					}
				else if(dHit.object->material->translucency != black){
					//hit a tranclusive surface, so cast the transmitted ray
					Ray rr = dHit.object->getRefracetedRay(dRay,dHit.point, dHit.normal);
					HitInfo rrHit;
					rrHit.distance = Infinity;
					rrHit.ray = rr;
					if(scene.Cast(rr,rrHit))
						if(rrHit.object == light)
							color += max( 0, dRay.direction*Unit(N) )*
							mat->diffuse*
							light->material->emission*dSamples[j].w/Pi;
					if( mat->Phong_exp !=0 && mat->specular != black)
						color += (white-mat->reflectivity)*
							pow( max( dRay.direction*R,0), mat->Phong_exp) * 
							mat->specular*light->material->emission*dSamples[j].w;
					
					}
				}
		 
	}	
	
	if(mat->reflectivity == black){
		//sample hmisphere (stratified)
		if(max_depth > 2){
			Sample*	iSamples = new Sample[k*k];
			numSamples = SampleProjectedHemisphere (hit.point, hit.normal, iSamples, k);
			//#pragma omp parallel for
			for(unsigned j = 0; j < numSamples ;j++){
				iRay.origin = hit.point + Unit(N)*10E-4; 
				iRay.from = obj;
				iRay.direction = Unit( hit.point - iSamples[j].P );
				iRay.generation = gen+ 1;
				iRay.type = indirect_ray;
				color += (mat->diffuse*iSamples[j].w*scene.Trace(iRay))/TwoPi;
			}
			delete [] iSamples;
			iSamples = NULL;
		}
	}
	else{
		
		Ray rr;
		rr.origin = P+ N*10E-4;
		rr.direction = R;
		rr.from = obj;
		rr.ignore = NULL;
		rr.generation = gen+1;
		color = color*(white - mat->reflectivity);
		color += mat->reflectivity*scene.Trace(rr);
	}

	

	delete [] dSamples;
	dSamples = NULL;
	return color; 
	

}
Color basic_shader::Shade( const Scene &scene, const HitInfo &hit ) const
    {
    Ray ray;
	Ray reflectionRay;
	Ray refractedRay;
	Ray secondaryRefractedRay;

    HitInfo otherhit;
	HitInfo refractionHit;
    static const double epsilon = 1.0E-6;
    if( Emitter( hit.object ) ) return hit.object->material->emission;

    Material *mat   = hit.object->material;
    Color  diffuse  = mat->diffuse;
    Color  specular = mat->specular;
    Color  color    = mat->ambient * diffuse;
    Vec3   O = hit.ray.origin;
    Vec3   P = hit.point;
    Vec3   N = hit.normal;
    Vec3   E = Unit( O - P );
    Vec3   R = Unit( ( 2.0 * ( E * N ) ) * N - E );
    Color  r = mat->reflectivity;
    double e = mat->Phong_exp;
    double k = mat->ref_index;
	Color t = mat->translucency;

	if(hit.object->Inside(P)){
		P = P + epsilon*N;
	}

    if( E * N < 0.0 ) N = -N;  // Flip the normal if necessary.

	//get the attentuation
	
	double attenuation;
	double lightDistance;
	Vec3 lightVector;
	double diffuseFactor;
	double specularFactor;
	Color diffuseColor = Color();
	Color specularColor = Color();
	Color finalColor;
	Color colorWithLighting;
	Color reflectedColor;
	Color refractedColor;
	Vec3 currentR;
	double shadowFactor = 0;
	bool objectWasHit = false;

	int numGridVals = 30;
	float numSamples = 5;
	int totalNumGridVals = numGridVals*numGridVals*numSamples;
	int totalArrayValues = totalNumGridVals*2;
	float totalGridVals = numGridVals;
	float currentXvalue;
	float currentYvalue;
	float interval = 2.0f/totalGridVals;
	float currentDist;
	bool posZinHemisphere,negZinHemisphere;
	int currentInd1,currentInd2;
	float randomX,randomY;
	float currentPosZvalue,currentNegZvalue;
	//Vec3 currentNormal = Vec3(0.0f,0.0f,1.0f);
	Vec3 currentNormal = N;
	float dotProdPosZ,dotProdNegZ,dotProdXYpart;
	Vec3 positiveZvector;
	Vec3 negativeZvector;
	int numLightsHit = 0;
	
	/*
	Surface Area of sphere from each patch P is approximately area(P)*(1/z') where z' is taken at the center
	This comes from the fact that the surface area is integral_P (1/z)
	*/
	float minZvalue=sqrt(interval)*sqrt(2-interval);;
	
	//used to calculate surface area
	float centerXvalue,centerYvalue,centerZvalue,approxSurfaceArea;

	//temp variable. default emission of the light blocks
	Color defaultEmission = Color(1.0,1.0,1.0);
	double emissionFactor = 30.0;

	Color posZpatchValue = Color();
	Color negZpatchValue = Color();
	Color posZpatchSpecValue = Color();
	Color negZpatchSpecValue = Color();
	double radius,patchFormFactor;
	Vec3 otherNormal;
	float currentEnergy = 0;

	for(int xInd = 0; xInd < numGridVals; xInd++){
		for(int yInd = 0; yInd < numGridVals; yInd++){

			centerXvalue = -1 + interval*xInd + interval*0.5;
			centerYvalue = -1 + interval*yInd + interval*0.5;
			currentDist = centerXvalue*centerXvalue + centerYvalue*centerYvalue;
			centerZvalue = sqrt(1-currentDist);

			
			if(centerZvalue < minZvalue){
				centerZvalue = minZvalue;
			}

			approxSurfaceArea = interval*interval*(1.0f/centerZvalue);	
			

			posZpatchValue = Color();
			negZpatchValue = Color();

			for(int sampleInd = 0; sampleInd < numSamples; sampleInd++){

				randomX = (double)rand() / RAND_MAX;
				randomY = (double)rand() / RAND_MAX;

				currentXvalue = -1+interval*xInd + interval*randomX;
				currentYvalue = -1+interval*yInd + interval*randomY;
				
				currentDist = currentXvalue*currentXvalue + currentYvalue*currentYvalue;

				posZinHemisphere = false;
				negZinHemisphere = false;

				currentInd1 = (xInd*numGridVals + yInd)*numSamples + sampleInd;
				currentInd2 = currentInd1 + totalNumGridVals;

				//makes sure it is inside the unit disk
				if(currentDist <= 1){

					currentPosZvalue = sqrt(1-currentDist);
					currentNegZvalue = -currentPosZvalue;

					dotProdXYpart = currentNormal.x*currentXvalue + currentNormal.y*currentYvalue;
					dotProdPosZ = currentNormal.z*currentPosZvalue + dotProdXYpart;
					dotProdNegZ = currentNormal.z*currentNegZvalue + dotProdXYpart;

					posZinHemisphere = (dotProdPosZ >=0);
					negZinHemisphere = (dotProdNegZ >=0);

				}


				if(posZinHemisphere){

					positiveZvector = Vec3(currentXvalue,currentYvalue,currentPosZvalue);

					//light ray to case to determine occulsion
					ray.origin = P;
					ray.direction = positiveZvector;
					HitInfo objectHit;
					objectHit.distance = Infinity;

					shadowFactor = 1;
					if(scene.Cast(ray,objectHit) ){
						if(objectHit.object != NULL){
							Vec3 LightPos = objectHit.point;
							
							if(LightPos.z > 12.0){
								lightVector = LightPos - P;
								numLightsHit = numLightsHit + 1;
								radius = Length(lightVector);
								otherNormal = Unit(objectHit.normal);
								lightVector = Unit(lightVector);

								//this is the current patches approximation of F_ij
								//patchFormFactor = ((abs(lightVector*currentNormal))*(abs(-1*lightVector*otherNormal)))/(Pi*radius*radius);
								patchFormFactor = ((lightVector*currentNormal)*(-1*lightVector*otherNormal))/(Pi*radius*radius);
								patchFormFactor = max(0,patchFormFactor);
								//printf("radius: %f\n",radius);
								currentEnergy = currentEnergy + emissionFactor*patchFormFactor;
								diffuseColor = diffuseColor + GetDiffuseColor(lightVector,N,defaultEmission,diffuse);
							}
						}
					}

				}

				if(negZinHemisphere){
					negativeZvector = Vec3(currentXvalue,currentYvalue,currentNegZvalue);
					//printf("(x,y,z)=(%f,%f,%f)\n",gridXvalues[currentInd2],gridYvalues[currentInd2],gridZvalues[currentInd2]);

					//light ray to case to determine occulsion
					ray.origin = P;
					ray.direction = negativeZvector;
					HitInfo objectHit;
					objectHit.distance = Infinity;

					shadowFactor = 1;
					if(scene.Cast(ray,objectHit) ){
						if(objectHit.object != NULL){
							Vec3 LightPos = objectHit.point;
							
							if(LightPos.z > 12.0){
								lightVector = LightPos - P;
								numLightsHit = numLightsHit + 1;
								radius = Length(lightVector);
								
								otherNormal = Unit(objectHit.normal);
								lightVector = Unit(lightVector);

								//this is the current patches approximation of F_ij
								//patchFormFactor = ((abs(lightVector*currentNormal))*(abs(-1*lightVector*otherNormal)))/(Pi*radius*radius);
								patchFormFactor = ((lightVector*currentNormal)*(-1*lightVector*otherNormal))/(Pi*radius*radius);
								patchFormFactor = max(0,patchFormFactor);
								currentEnergy = currentEnergy + emissionFactor*patchFormFactor;
								diffuseColor = diffuseColor + GetDiffuseColor(lightVector,N,defaultEmission,diffuse);
								//negZpatchValue = negZpatchValue + GetDiffuseColor(lightVector,N,defaultEmission,diffuse);
							}
						}
					}
				}

			}

			//diffuseColor = diffuseColor + (posZpatchValue/numSamples)*approxSurfaceArea;
			//diffuseColor = diffuseColor + (negZpatchValue/numSamples)*approxSurfaceArea;
			//printf("Approx Surface Area:%f\n",approxSurfaceArea);

			//diffuseColor = diffuseColor + posZpatchValue;
			//diffuseColor = diffuseColor + negZpatchValue;

			if(diffuseColor.blue > 0.5 || diffuseColor.green > 0.5 || diffuseColor.red > 0.5){
				//printf("Current Diffuse Color: (%f,%f,%f)\n",diffuseColor.blue,diffuseColor.green,diffuseColor.red);
			}
			
			
		}
	}
	
	//makes sure to include the light vectors in the calculations
	for( unsigned i = 0; i < scene.NumLights(); i++ )
        {
        const Object *light = scene.GetLight(i);
        Color emission = light->material->emission;
        AABB box = GetBox( *light );
        Vec3 LightPos( Center( box ) ); 

		//gets the light Vector
		lightVector = LightPos - P;
		lightDistance = Length(lightVector);
		Vec3 unitLightVector = Unit(lightVector);
		
		//gets the attenuation factor
		attenuation = 1/(attenuation_a + attenuation_b*lightDistance + attenuation_c*lightDistance*lightDistance);

		float dotProd = currentNormal.x*unitLightVector.x + currentNormal.y*unitLightVector.y + currentNormal.z*unitLightVector.z;

		//light vector in unit hemipshere
		if(dotProd >= 0){

			//light ray to case to determine occulsion
			ray.origin = P;
			ray.direction = unitLightVector;
			HitInfo objectHit;
			objectHit.distance = Infinity;

			if(scene.Cast(ray,objectHit) ){
				if(objectHit.object != NULL){
					Vec3 LightPos = objectHit.point;
							
					if(LightPos.z > 12.0){
						numLightsHit = numLightsHit + 1;
						lightVector = LightPos - P;
						//diffuseColor = diffuseColor + GetDiffuseColor(lightVector,N,defaultEmission,diffuse);

						radius = Length(lightVector);
								
						otherNormal = Unit(objectHit.normal);
						lightVector = Unit(lightVector);

						//this is the current patches approximation of F_ij
						//patchFormFactor = ((abs(lightVector*currentNormal))*(abs(-1*lightVector*otherNormal)))/(Pi*radius*radius);
						patchFormFactor = ((lightVector*currentNormal)*(-1*lightVector*otherNormal))/(Pi*radius*radius);
						patchFormFactor = max(0,patchFormFactor);
						currentEnergy = currentEnergy + emissionFactor*patchFormFactor;
						diffuseColor = diffuseColor + GetDiffuseColor(lightVector,N,defaultEmission,diffuse);
					}
				}
			}

		}

		

		
    }

	diffuseColor = currentEnergy*defaultEmission;

	//diffuseColor = diffuseColor/12.0;

	float numLights = numLightsHit;
	//diffuseColor = diffuseColor/(numLights*Pi);
	if(numLightsHit > 1){
		//printf("Number of Lights Hit:%d\n",numLightsHit);
		//printf("Original Color: (%f,%f,%f)\n",diffuse.blue,diffuse.green,diffuse.red);
		//printf("Diffuse Color: (%f,%f,%f)\n\n",diffuseColor.blue,diffuseColor.green,diffuseColor.red);

	}

	//colorWithLighting = color + diffuseColor*diffuse + specularColor*specular;
	//colorWithLighting = diffuseColor*diffuse;
	colorWithLighting = diffuseColor*diffuse + diffuse*0.4;

	//set variables for reflection
	reflectionRay.origin = P;
	reflectionRay.direction = R;
	reflectionRay.generation = hit.ray.generation + 1;
	reflectedColor = Color();

	//set variables for refraction

	refractedRay.origin = P-epsilon*N;

	Vec3 refractionDir = RefractionDirection(1.0,k,E,N);
	refractedRay.direction = refractionDir; //for refraction
	refractedRay.generation = hit.ray.generation + 1;
	refractedColor = Color();
 	refractionHit.distance = Infinity;

	//only do refraction if the transluency is greater than zero
	//	this is an optimization so unnecessary refractions are not calculated
	if( (t.red + t.green + t.blue) > epsilon){
		if(scene.Cast(refractedRay,refractionHit)){

			bool insideMaterial = false;
			Vec3 currentNormal;
			Vec3 previousRefractedDirection;

			do{
				currentNormal = Unit(refractionHit.normal);
				previousRefractedDirection = refractedRay.direction;
				refractedRay.direction = RefractionDirection(k,1.0,-1*refractedRay.direction,-1*currentNormal);

				if(Length(refractedRay.direction) < epsilon){
					insideMaterial = true;
					refractionHit.distance = Infinity;
					refractedRay.origin = refractionHit.point - epsilon*currentNormal;
					refractedRay.direction = Unit( ( 2.0 * ( previousRefractedDirection * currentNormal ) ) * (-1*currentNormal) + previousRefractedDirection );


					if(!scene.Cast(refractedRay,refractionHit)){
						insideMaterial = false;
					}
				}else{

					refractedRay.origin = refractionHit.point + epsilon*currentNormal;
					insideMaterial = false;

				}

				

			}while(insideMaterial);

			refractedRay.generation = hit.ray.generation + 1;

			//now do refraction
			refractedColor = scene.Trace(refractedRay);

		}
	}


	//do the reflection
	//only do reflection if the reflectance is greater than zero
	//	this is an optimization so unnecessary reflections are not calculated
	if( (r.red + r.green + r.blue) > epsilon){
		reflectedColor = scene.Trace(reflectionRay);
	}
	
	//printf("diffuseColor: (%f,%f,%f)\n",colorWithLighting.red,colorWithLighting.green,colorWithLighting.blue);

	//now combine calculated color with reflected color
	//finalColor = (-1*t + Color(1.0,1.0,1.0))*(colorWithLighting + r*reflectedColor) + t*refractedColor;

	return colorWithLighting;
	//return finalColor; 
    }