 #if MAX_DIRECTIONAL_LIGHTS > 0
   for(int i = 0; i < MAX_DIRECTIONAL_LIGHTS; i++)
   {
-		float q = max(dot(uDirectionalLights[i].direction, n), 0.0);
+		vec3 s = uDirectionalLights[i].direction;
+
+		#ifdef LAMBERT
+			float q = lambert(s, n);
+		#endif
+
+		#ifdef OREN_NAYAR
+			float q = orenNayar(v, s, n);
+		#endif
 
 		if (q > 0.0) {
-			vec3 r = reflect(-uDirectionalLights[i].direction, n);
-			float w = pow(max(dot(r, v), 0.0), uShininess);
+			#ifdef BLINN_PHONG
+				float w = blinnPhong(v, s, n);
+			#endif
+
+			#ifdef COOK_TORRANCE
+				float w = cookTorrance(v, s, n);
+			#endif
+
+			#ifdef PHONG
+				float w = phong(v, s, n);
+			#endif
 
     	diffuse += uDirectionalLights[i].diffuse * uDiffuse * q;
 			specular += uDirectionalLights[i].specular * uSpecular * w;

#endif

#ifdef DIFFUSE_MAP
	diffuse *= vec3(texture2D(uDiffuseSampler, vUv));
#endif

#ifdef SPECULAR_MAP
	specular *= vec3(texture2D(uSpecularSampler, vUv))
#endif

#ifdef ENVIRONMENT_MAP
#ifdef REFLECTION
	diffuse = mix(diffuse, textureCube(uEnvironmentSampler, reflect(-v, n)).rgb, 0.5);
#endif

#ifdef REFRACTION
	diffuse = mix(diffuse, textureCube(uEnvironmentSampler, refract(-v, n, uRefractFactor)).rgb, 0.5);
#endif
#endif

  gl_FragColor = vec4(emissive + diffuse + specular, 1.0);


#ifdef ENVIRONMENT_MAP
	uniform samplerCube uEnvironmentSampler;
#endif

#ifdef REFRACTION
	uniform float uRefractFactor;
#endif

#if defined(COOK_TORRANCE) && defined(OREN_NAYAR)
	uniform float uAlbedo;
	uniform float uFresnel;
	uniform float uRoughness;
#elif defined(COOK_TORRANCE)
	uniform float uFresnel;
	uniform float uRoughness;
#elif defined(OREN_NAYAR)
	uniform float uAlbedo;
	uniform float uRoughness;
#endif

#ifdef LAMBERT
float lambert(vec3 s, vec3 n)
{
	return max(dot(s, n), 0.0);
}
#endif

#ifdef OREN_NAYAR
float orenNayar(vec3 v, vec3 s, vec3 n)
{
	float PI = acos(-1.0);

	float SdotV = dot(s, v);
	float SdotN = dot(s, n);
	float NdotV = dot(n, v);

	float S = SdotV - SdotN * NdotV;
	float T = mix(1.0, max(SdotN, NdotV), step(0.0, S));
	float sigma2 = uRoughness * uRoughness;

	float A = 1.0 + sigma2 * (uAlbedo / (sigma2 + 0.13) + 0.5 / (sigma2 + 0.33));
  float B = 0.45 * sigma2 / (sigma2 + 0.09);

  return uAlbedo * max(0.0, SdotN) * (A + B * S / T) / PI;
}
#endif

#ifdef PHONG
float phong(vec3 v, vec3 s, vec3 n)
{
	vec3 r = reflect(-s, n);
	return pow(max(dot(r, v), 0.0), uShininess);
}
#endif

#ifdef BLINN_PHONG
float blinnPhong(vec3 v, vec3 s, vec3 n)
{
	vec3 h = normalize(s + v);
	return pow(max(dot(h, n), 0.0), uShininess);
}
#endif

#ifdef COOK_TORRANCE
float beckmannDistribution(float NdotH, float roughness)
{
	float PI = acos(-1.0);
	float cos2Beta = NdotH * NdotH;
	float tan2Beta = (cos2Beta - 1.0) / cos2Beta;
	float div =  PI * roughness * roughness * cos2Beta * cos2Beta;
	return exp(tan2Beta / (roughness * roughness)) / div;
}

float cookTorrance(vec3 v, vec3 s, vec3 n)
{
	vec3 h = normalize(s + v);

	float VdotN = max(dot(v, n), 0.0);
	float SdotN = max(dot(s, n), 0.0);
	float VdotH = max(dot(v, h), 0.0);
	float SdotH = max(dot(s, h), 0.0);
	float NdotH = max(dot(n, h), 0.0);

	float G1 = (2.0 * NdotH * VdotN) / VdotH;
  float G2 = (2.0 * NdotH * SdotN) / SdotH;
  float G = min(1.0, min(G1, G2));

  float D = beckmannDistribution(NdotH, uRoughness);
  float F = pow(1.0 - VdotN, uFresnel);
	float PI = acos(-1.0);

  return  G * F * D / max(PI * VdotN, 0.0);
}
#endif

#ifdef NORMAL_MAP
uniform sampler2D uNormalSampler;

vec3 pertubNormal(vec3 v) {
