_DrawLightShadows_OMNI.vsh
{
	include generic_vertex.vsh
}

_DrawLightShadows_OMNI.fsh
{
	uniform sampler2DRect	u_NormalBuffer;
	uniform sampler2DRect	u_SpecularBuffer;
	uniform sampler2DRect	u_DepthBuffer;
	uniform sampler2DRect	u_ColorBuffer;
#if defined(Stages1)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform vec2		u_AttenuationSizes;
	uniform vec4		u_AttenuationColor;
#endif
#if defined(Stages2)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform sampler2DRect	u_AttenuationMap1;
	uniform vec2		u_Blend;
	uniform vec2		u_AttenuationSizes[2];
	uniform vec4		u_AttenuationColor[2];
#endif
#if defined(Stages3)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform sampler2DRect	u_AttenuationMap1;
	uniform sampler2DRect	u_AttenuationMap2;
	uniform vec2		u_Blend[2];
	uniform vec2		u_AttenuationSizes[3];
	uniform vec4		u_AttenuationColor[3];
#endif
#if defined(Stages4)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform sampler2DRect	u_AttenuationMap1;
	uniform sampler2DRect	u_AttenuationMap2;
	uniform sampler2DRect	u_AttenuationMap3;
	uniform vec2		u_Blend[3];
	uniform vec2		u_AttenuationSizes[4];
	uniform vec4		u_AttenuationColor[4];
#endif
	uniform vec3		u_LightOrigin;
	uniform vec3		u_ViewOrigin;
	uniform vec3		u_LightColor;
	uniform mat4		u_LightAttenuationMatrix;
	uniform mat4		u_UnprojectMatrix;
	uniform float		u_LightRadius;
	uniform float		u_noSpecular;
#ifndef Stages1
	include Blend.inc
#endif
	uniform samplerCube	u_ShadowMap;
	uniform	vec2		u_rpJitterOffset;
	uniform	vec2		u_shadowBiasScale;	/// x = penumbraSize, y = penumbraSize / mapSize
	out	vec3	fragData;

	include poissonDisk.inc
	include randomRotation.inc
	include pbr.inc
#if defined(FAKE_PPL)
	include UnPackNormal.inc
#endif

	void MakeNormalVectors(const vec3 forward, inout vec3 right, inout vec3 up)
	{
		// this rotate and negate guarantees a vector not colinear with the original
		right = vec3(forward.z, -forward.x, forward.y);
		right -= forward * dot(right, forward);
		up = cross(normalize(right), forward);
	}

	float shadowCube(const vec3 I, in float vertexDistance, const float l)
	{
		vec3 forward, right, up;
		forward = normalize(I);
		MakeNormalVectors(forward, right, up);

		vertexDistance -= u_shadowBiasScale.x + l * (1.0 / 512.0);	// bias
		mat2 rmat = randomRotation(pow(gl_FragCoord.xy, u_rpJitterOffset)) * u_shadowBiasScale.y;
		float shadow = 0.0;
		for(int i = 0; i < NUM_OF_TAPS; i++)
		{
			vec2 offset = rmat * poissonDisk[i];
			vec3 jitter = forward + right * offset.x + up * offset.y;
			shadow += clamp(textureLod(u_ShadowMap, jitter, 0.0).r - vertexDistance, 0.0, 1.0);
		}
		return shadow * ONE_OVER_NUM_OF_TAPS;
	}

	void main(void)
	{
		vec4 specular = texture(u_SpecularBuffer, gl_FragCoord.xy);
		if (specular.a == 1.0)
		{
			discard;	// no portal surfaces draw
			return;
		}

		// reconstruct vertex position in world space
		float depth = texture(u_DepthBuffer, gl_FragCoord.xy).r;
		vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0);
		P.xyz /= P.w;

		// compute incident ray
		vec3 I = u_LightOrigin - P.xyz;

		// fast culling
		float dist = length(I);
		if (dist > u_LightRadius)
		{
			discard;
			return;
		}

		// compute color attenuation
		vec3 texAtten = (u_LightAttenuationMatrix * vec4(P.xyz, 1.0)).xyz;
		texAtten.z = abs(texAtten.z - 0.5);
		if (texAtten.z >= 0.5)
		{
			discard;
			return;
		}

		vec4 sN = texture(u_NormalBuffer, gl_FragCoord.xy);
#if defined(FAKE_PPL)
		vec3 N = UnPackNormal(sN.xy);
#else
		vec3 N = sN.xyz;
#endif

		// compute light direction in world space
		vec3 L = I / dist;

		float NL = dot(N, L);
		if (NL <= 0.0)		// back face
		{
			discard;
			return;
		}

		if (sN.a < 0.0)
		{
			P.xyz += N * 2.0;	/// shift pixel for parallax mapping ;)
			// compute incident ray again
			I = u_LightOrigin - P.xyz;
		}

		// compute view direction in world space
		vec3 V = u_ViewOrigin - P.xyz;
		float l = length(V);
		float shadow = shadowCube(-I, dist, l);
		if (shadow < 0.004)
		{
			discard;
			return;
		}

		vec3 lit = 2.0 * (0.5 - texAtten.z) * u_LightColor * shadow * NL;

#if defined(Stages1)
		lit *= (texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes) * u_AttenuationColor).rgb;
#endif
#if defined(Stages2)
		vec4 d = texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes[0]) * u_AttenuationColor[0];
		vec4 s = texture(u_AttenuationMap1, texAtten.xy * u_AttenuationSizes[1]) * u_AttenuationColor[1];
		lit *= (Blend(s,d,u_Blend)).rgb;
#endif
#if defined(Stages3)
		vec4 d = texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes[0]) * u_AttenuationColor[0];
		vec4 s = texture(u_AttenuationMap1, texAtten.xy * u_AttenuationSizes[1]) * u_AttenuationColor[1];
		d = Blend(s,d,u_Blend[0]);
		s = texture(u_AttenuationMap2, texAtten.xy * u_AttenuationSizes[2]) * u_AttenuationColor[2];
		lit *= (Blend(s,d,u_Blend[1])).rgb;
#endif
#if defined(Stages4)
		vec4 d = texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes[0]) * u_AttenuationColor[0];
		vec4 s = texture(u_AttenuationMap1, texAtten.xy * u_AttenuationSizes[1]) * u_AttenuationColor[1];
		d = Blend(s,d,u_Blend[0]);
		s = texture(u_AttenuationMap2, texAtten.xy * u_AttenuationSizes[2]) * u_AttenuationColor[2];
		d = Blend(s,d,u_Blend[1]);
		s = texture(u_AttenuationMap3, texAtten.xy * u_AttenuationSizes[3]) * u_AttenuationColor[3];
		lit *= (Blend(s,d,u_Blend[2])).rgb;
#endif
		if (dot(lit,lit) < 0.00001526)	// instead: if (length(lit) < 1.0/256.0)
		{
			discard;	// attenuated color is too dark
			return;
		}

		// normalize view direction
		V = V / l;

		DoPBR(lit, specular, N, L, V, NL, sN.a);
	}
}


_DrawLightShadows_PROJ.vsh
{
	include generic_vertex.vsh
}

_DrawLightShadows_PROJ.fsh
{
	uniform sampler2DRect	u_NormalBuffer;
	uniform sampler2DRect	u_SpecularBuffer;
	uniform sampler2DRect	u_DepthBuffer;
	uniform sampler2DRect	u_ColorBuffer;
#if defined(Stages1)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform vec2		u_AttenuationSizes;
	uniform vec4		u_AttenuationColor;
#endif
#if defined(Stages2)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform sampler2DRect	u_AttenuationMap1;
	uniform vec2		u_Blend;
	uniform vec2		u_AttenuationSizes[2];
	uniform vec4		u_AttenuationColor[2];
#endif
#if defined(Stages3)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform sampler2DRect	u_AttenuationMap1;
	uniform sampler2DRect	u_AttenuationMap2;
	uniform vec2		u_Blend[2];
	uniform vec2		u_AttenuationSizes[3];
	uniform vec4		u_AttenuationColor[3];
#endif
#if defined(Stages4)
	uniform sampler2DRect	u_AttenuationMap0;
	uniform sampler2DRect	u_AttenuationMap1;
	uniform sampler2DRect	u_AttenuationMap2;
	uniform sampler2DRect	u_AttenuationMap3;
	uniform vec2		u_Blend[3];
	uniform vec2		u_AttenuationSizes[4];
	uniform vec4		u_AttenuationColor[4];
#endif
	uniform vec3		u_LightOrigin;
	uniform vec3		u_ViewOrigin;
	uniform vec3		u_LightColor;
	uniform mat4		u_LightAttenuationMatrix;
	uniform mat4		u_UnprojectMatrix;
	uniform float		u_LightRadius;
	uniform float		u_noSpecular;
#ifndef Stages1
	include Blend.inc
#endif
	uniform sampler2D	u_ShadowMap;
	uniform	vec2		u_rpJitterOffset;
	uniform	vec2		u_shadowBiasScale;	/// x = penumbraSize, y = penumbraSize / mapSize
	uniform	vec4		u_LightViewPlane;
	uniform vec4		u_depthParms;		/// x = -zFar * zNear, y = zFar - zNear, z = zFar, w = zNear
	out	vec3	fragData;

	include poissonDisk.inc
	include randomRotation.inc
	include pbr.inc
#if defined(FAKE_PPL)
	include UnPackNormal.inc
#endif

	float shadow2D(const vec2 st, in float vertexDistance)
	{
		// normalize vertexDistance to (0.0 - 1.0) range
		float distNorm = (u_depthParms.x / (vertexDistance - u_shadowBiasScale.x + u_depthParms.w) + u_depthParms.z) / u_depthParms.y;
		mat2 rmat = randomRotation(pow(gl_FragCoord.xy, u_rpJitterOffset)) * u_shadowBiasScale.y;
		float shadow = 0.0;
		for (int i = 0; i < NUM_OF_TAPS; i++)
		{
			vec2 uv = st + rmat * poissonDisk[i];
			float sample = textureLod(u_ShadowMap, uv, 0.0).r;
			if (sample > distNorm)
				shadow += ONE_OVER_NUM_OF_TAPS;
		}
		return shadow;
	}

	void main(void)
	{
		vec4 specular = texture(u_SpecularBuffer, gl_FragCoord.xy);
		if (specular.a == 1.0)
		{
			discard;	// no portal surfaces draw
			return;
		}

		// reconstruct vertex position in world space
		float depth = texture(u_DepthBuffer, gl_FragCoord.xy).r;
		vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0);
		P.xyz /= P.w;

		// compute incident ray
		vec3 I = u_LightOrigin - P.xyz;

		// fast culling
		float dist = length(I);
		if (dist > u_LightRadius)
		{
			discard;
			return;
		}

		vec4 sN = texture(u_NormalBuffer, gl_FragCoord.xy);
#if defined(FAKE_PPL)
		vec3 N = UnPackNormal(sN.xy);
#else
		vec3 N = sN.xyz;
#endif

		// compute light direction in world space
		vec3 L = I / dist;

		float NL = dot(N, L);
		if (NL <= 0.0)		// back face
		{
			discard;
			return;
		}

		if (sN.a < 0.0)
			P.xyz += N * 2.0;	/// shift pixel for parallax mapping ;)

		// compute color attenuation
		vec4 texAtten = u_LightAttenuationMatrix * vec4(P.xyz, 1.0);
		if(texAtten.w <= 0.0)
		{
			discard;	// fragment is behind the near clip plane
			return;
		}
		texAtten.xy /= texAtten.w;

		float shadow = shadow2D(texAtten.xy, dot(P.xyz, u_LightViewPlane.xyz) - u_LightViewPlane.w);
		if (shadow < 0.004)
		{
			discard;
			return;
		}

		vec3 lit = (1.0 - texAtten.z) * u_LightColor * shadow * NL;

#if defined(Stages1)
		lit *= (texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes) * u_AttenuationColor).rgb;
#endif
#if defined(Stages2)
		vec4 d = texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes[0]) * u_AttenuationColor[0];
		vec4 s = texture(u_AttenuationMap1, texAtten.xy * u_AttenuationSizes[1]) * u_AttenuationColor[1];
		lit *= (Blend(s,d,u_Blend)).rgb;
#endif
#if defined(Stages3)
		vec4 d = texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes[0]) * u_AttenuationColor[0];
		vec4 s = texture(u_AttenuationMap1, texAtten.xy * u_AttenuationSizes[1]) * u_AttenuationColor[1];
		d = Blend(s,d,u_Blend[0]);
		s = texture(u_AttenuationMap2, texAtten.xy * u_AttenuationSizes[2]) * u_AttenuationColor[2];
		lit *= (Blend(s,d,u_Blend[1])).rgb;
#endif
#if defined(Stages4)
		vec4 d = texture(u_AttenuationMap0, texAtten.xy * u_AttenuationSizes[0]) * u_AttenuationColor[0];
		vec4 s = texture(u_AttenuationMap1, texAtten.xy * u_AttenuationSizes[1]) * u_AttenuationColor[1];
		d = Blend(s,d,u_Blend[0]);
		s = texture(u_AttenuationMap2, texAtten.xy * u_AttenuationSizes[2]) * u_AttenuationColor[2];
		d = Blend(s,d,u_Blend[1]);
		s = texture(u_AttenuationMap3, texAtten.xy * u_AttenuationSizes[3]) * u_AttenuationColor[3];
		lit *= (Blend(s,d,u_Blend[2])).rgb;
#endif
		if (dot(lit,lit) < 0.00001526)	// instead: if (length(lit) < 1.0/256.0)
		{
			discard;	// attenuated color is too dark
			return;
		}

		// compute view direction in world space
		vec3 V = normalize(u_ViewOrigin - P.xyz);

		DoPBR(lit, specular, N, L, V, NL, sN.a);
	}
}


_DrawLightShadows_PARALLEL.vsh
{
	include generic_vertex.vsh
}

_DrawLightShadows_PARALLEL.fsh
{
	uniform sampler2DRect	u_NormalBuffer;
	uniform sampler2DRect	u_SpecularBuffer;
	uniform sampler2DRect	u_DepthBuffer;
	uniform sampler2DRect	u_ColorBuffer;
	uniform vec3		u_LightOrigin;
	uniform vec3		u_ViewOrigin;
	uniform vec3		u_LightColor;
	uniform mat4		u_LightAttenuationMatrix0;
	uniform mat4		u_LightAttenuationMatrix1;
	uniform mat4		u_LightAttenuationMatrix2;
	uniform mat4		u_LightAttenuationMatrix3;
	uniform mat4		u_UnprojectMatrix;
	uniform float		u_noSpecular;
	uniform sampler2D	u_ShadowMap0;
	uniform sampler2D	u_ShadowMap1;
	uniform sampler2D	u_ShadowMap2;
	uniform sampler2D	u_ShadowMap3;
	uniform	vec2		u_rpJitterOffset;
	uniform	float		u_invMapSize;	/// 1.0 / mapSize
	uniform	vec4		u_LightViewPlane0;
	uniform	vec4		u_LightViewPlane1;
	uniform	vec4		u_LightViewPlane2;
	uniform	vec4		u_LightViewPlane3;
	uniform	vec3		u_LightBaseDir;
	uniform	vec4		u_LightCascadeCosines;	/// u_LightCascadeCosines.w = 0.0 if r_showcascades = 1,	else 1.0
	uniform vec4		u_depthParms;		/// x = -zFar * zNear, y = zFar - zNear, z = zFar, w = zNear
	out	vec3		fragData;

	include pbr.inc
	include poissonDisk.inc
	include randomRotation.inc

	float shadow2D(const sampler2D shadowMap, const vec2 st, in float vertexDistance, const float cosine, float invCascade)
	{
		float smoothSize = u_invMapSize * invCascade / 4.0;
		float bias = pow(1.01 - cosine * cosine, 1.0 / (6.0 - invCascade));

		// normalize vertexDistance to (0.0 - 1.0) range
		float distNorm = (u_depthParms.x / (vertexDistance - bias + u_depthParms.w) + u_depthParms.z) / u_depthParms.y;
		mat2 rmat = randomRotation(pow(gl_FragCoord.xy, u_rpJitterOffset)) * smoothSize;
		float shadow = 0.0;
		for (int i = 0; i < NUM_OF_TAPS; i++)
		{
			vec2 uv = st + rmat * poissonDisk[i];
			float sample = textureLod(shadowMap, uv, 0.0).r;
			if (sample > distNorm)
				shadow += ONE_OVER_NUM_OF_TAPS;
		}
		return pow(shadow, 1.0 / (17.0 - invCascade * invCascade));
	}

	void main(void)
	{
		vec4 specular = texture(u_SpecularBuffer, gl_FragCoord.xy);
		if (specular.a == 1.0)
		{
			discard;	// no portal surfaces draw
			return;
		}

		// reconstruct vertex position in world space
		float depth = texture(u_DepthBuffer, gl_FragCoord.xy).r;
		vec4 P = u_UnprojectMatrix * vec4(gl_FragCoord.xy, depth, 1.0);
		P.xyz /= P.w;
		P.w = 1.0;

		// compute incident ray
		vec3 L = normalize(u_LightOrigin - P.xyz);

		vec4 sN = texture(u_NormalBuffer, gl_FragCoord.xy);
		vec3 N = sN.xyz;

		float NL = dot(N, L);
		if (NL <= 0.0)		// back face
		{
			fragData = vec3(0.0);
			return;
		}

		if (sN.a < 0.0)
			P.xyz += N * 2.0;	/// shift pixel for parallax mapping ;)

		// compute view direction in world space
		vec3 V = normalize(u_ViewOrigin - P.xyz);

		float cosine = dot(u_LightBaseDir, L);
		float f03 = 0.3 + 0.7 * u_LightCascadeCosines.w;

		vec3 mask = vec3(1.0, f03, f03);	/// red
		vec4 texAtten;

		float shadow = 0.0;
		if (cosine >= u_LightCascadeCosines.x)
		{
			// bias for eliminate light-leak artefact
			P.xyz += (2.0 + (1.0 - cosine) / (1.0 - u_LightCascadeCosines.x)) * V;

			texAtten = u_LightAttenuationMatrix0 * P;
			shadow = shadow2D(u_ShadowMap0, texAtten.xy / texAtten.w, dot(P.xyz, u_LightViewPlane0.xyz) - u_LightViewPlane0.w, cosine, 4.0);
		}
		if (cosine < u_LightCascadeCosines.x && cosine >= u_LightCascadeCosines.y)
		{
			// bias for eliminate light-leak artefact
			P.xyz += (2.0 + 2.0 * (u_LightCascadeCosines.x - cosine) / (u_LightCascadeCosines.x - u_LightCascadeCosines.y)) * V;

			mask = vec3(f03, 1.0, f03);	/// green
			texAtten = u_LightAttenuationMatrix1 * P;
			shadow = shadow2D(u_ShadowMap1, texAtten.xy / texAtten.w, dot(P.xyz, u_LightViewPlane1.xyz) - u_LightViewPlane1.w, cosine, 3.0);
		}
		if (cosine < u_LightCascadeCosines.y && cosine >= u_LightCascadeCosines.z)
		{
			// bias for eliminate light-leak artefact
			P.xyz += (2.0 + 3.0 * (u_LightCascadeCosines.y - cosine) / (u_LightCascadeCosines.y - u_LightCascadeCosines.z)) * V;

			mask = vec3(f03, f03, 1.0);	/// blue
			texAtten = u_LightAttenuationMatrix2 * P;
			shadow = shadow2D(u_ShadowMap2, texAtten.xy / texAtten.w, dot(P.xyz, u_LightViewPlane2.xyz) - u_LightViewPlane2.w, cosine, 2.0);
		}
		if (cosine < u_LightCascadeCosines.z)
		{
			// bias for eliminate light-leak artefact
			P.xyz += 5.0 * V;

			mask = vec3(1.0, 1.0, 1.0);	/// white
			texAtten = u_LightAttenuationMatrix3 * P;
			shadow = shadow2D(u_ShadowMap3, texAtten.xy / texAtten.w, dot(P.xyz, u_LightViewPlane3.xyz) - u_LightViewPlane3.w, cosine, 1.0);
		}

		if (shadow < 0.004)
		{
			fragData = vec3(0.0);
			return;
		}
		
		vec3 lit = shadow * u_LightColor * NL * mask;

		DoPBR(lit, specular, N, L, V, NL, sN.a);
	}
}
