﻿Shader "BlendWithMask" 
{
	Properties 
	{
		_MainTex ("Base Color (RGB)", 2D) = "white" {}
		_TargetTex ("Target Color (RGB)", 2D) = "white" {}
		_MainNormalTex ("Base Normal (RGB)", 2D) = "white" {}
		_TargetNormalTex ("Target Normal (RGB)", 2D) = "white" {}
		_TransitionMaskTex ("Transition Mask (RGB)", 2D) = "white" {}
		_TransitionRange ("_TransitionRange", Range(0,1)) = 0.0 	
	}
	SubShader 
	{
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert
		#pragma target 3.0

		sampler2D _MainTex, _TargetTex, _MainNormalTex, _TargetNormalTex, _TransitionMaskTex ;
		float _TransitionRange;

		struct Input 
		{
			float2 uv_MainTex;
		};
		
		
		float overlay(float x, float y)
		{
			float case1LessHalf = 2.0*x*y;
			float case2GreaterHalf = 1.0 - 2.0*(1.0 - x)*(1.0 - y);
			  
			return lerp( case1LessHalf, case2GreaterHalf, clamp(sign(x - .5 ),0,1) );
		}
		 // Linear Blending
		float3 linearBlend( float3 n1, float3 n2 )
	    {
	    	return normalize(n1 + n2);
	    }
	    // Overlay Blending
		float3 overlayBlend( float3 n1, float3 n2 )
	    {	
			return normalize( ( overlay(n1.x, n2.x),
								overlay(n1.y, n2.y),
								overlay(n1.z, n2.z) )
							);
	    }
	    // Partial Derivatives
		float3 partialDerivativeBlend( float3 n1, float3 n2 )
	    {
	        return normalize(float3(n1.xy*n2.z + n2.xy*n1.z, n1.z*n2.z));
	    }
	    // Whiteout
		float3 whiteoutBlend( float3 n1, float3 n2 )
	    {
	       return normalize(float3(n1.xy + n2.xy, n1.z*n2.z));
	    }
	    // UDN
		float3 udnBlend( float3 n1, float3 n2 )
	    {
	       return normalize(float3(n1.xy + n2.xy, n1.z));
	    }
	    // RNM
	    float3 rnmBlend( float3 n1, float3 n2 )
	    {
	        n1 = n1*float3( 2,  2, 2) + float3(-1, -1,  0);
	        n2 = n2*float3(-2, -2, 2) + float3( 1,  1, -1);
	        return normalize (n1*dot(n1, n2)/n1.z - n2);
	    }
	    float3 rnmBlendRepack( float3 n1, float3 n2 )
	    {
	    	n1 = n1.xyz / 2 + .5;
	    	n2 = n2.xyz / 2 + .5;
			return rnmBlend(n1,n2); 
	    }
	    float3 rnmBlendUnpacked(float3 n1, float3 n2)
		{
		    n1 += float3( 0,  0, 1);
		    n2 *= float3(-1, -1, 1);
		    return n1*dot(n1, n2)/n1.z - n2;
		}
		float3 UnpackNormalSafer(float4 packednormal)
		{
		    float3 newNormal;
		    newNormal.xy = packednormal.wy*2 - 1;
		    float d = dot(newNormal.xy, newNormal.xy);
		    newNormal.z = (d <= 1) ? sqrt(1 - d) : 0;
		    return normalize(newNormal);
		}
		float3 rnmBlendUnpackedClampZ(float3 n1, float3 n2)
		{
		    n1 += float3( 0,  0, 1);
		    n2 *= float3(-1, -1, 1);
		    float3 n = n1*dot(n1, n2)/n1.z - n2;
		    if (n.z < 0)
		        n = normalize(float3(n.x, n.y, 0));
		    return n;
		}
	    // Unity
		float3 unityBlend( float3 n1, float3 n2 )
		{
	       	float3x3 nBasis = float3x3(
	            float3(n1.z, n1.y, -n1.x), // +90 degree rotation around y axis
	            float3(n1.x, n1.z, -n1.y), // -90 degree rotation around x axis
	            float3(n1.x, n1.y,  n1.z));
	        return normalize( mul( n2 , nBasis) ) ; // n2.x*nBasis[0] + n2.y*nBasis[1] + n2.z*nBasis[2]);
	    }

		void surf (Input IN, inout SurfaceOutput o) 
		{
			float4 mainColor = tex2D (_MainTex, IN.uv_MainTex);
			float4 targetColor = tex2D (_TargetTex, IN.uv_MainTex);
			float4 unpackedMainNormal = tex2D (_MainNormalTex, IN.uv_MainTex);
			float3 mainNormal = UnpackNormal(tex2D (_MainNormalTex, IN.uv_MainTex));
			float4 unpackedTargetNormal = tex2D(_TargetNormalTex, IN.uv_MainTex);
			float3 targetNormal = UnpackNormal(tex2D (_TargetNormalTex, IN.uv_MainTex));
			float4 blendMask = tex2D (_TransitionMaskTex, IN.uv_MainTex);
			
			float blendValue = clamp(sign(_TransitionRange - blendMask.r), 0,1 )  * _TransitionRange ;
			
			o.Albedo = lerp ( mainColor.rgb, targetColor.rgb, blendValue );
			
			float3 safeUnpackMainNormal = UnpackNormalSafer(unpackedMainNormal);
			float3 safeUnpackTargetNormal = UnpackNormalSafer(unpackedTargetNormal);
			
			//o.Normal = lerp ( mainNormal, rnmBlendUnpackedClampZ( mainNormal, targetNormal ), blendValue);
			//o.Normal = lerp ( mainNormal, rnmBlendUnpackedClampZ( safeUnpackMainNormal,safeUnpackTargetNormal ), blendValue);
			o.Normal = lerp ( mainNormal, udnBlend( mainNormal,targetNormal ), blendValue);
			o.Alpha = mainColor.a;
		}
		ENDCG

///pasted for reference from UnityCG.cginc		
//		inline fixed3 UnpackNormal(fixed4 packednormal)
//		{
//		#if defined(SHADER_API_GLES) && defined(SHADER_API_MOBILE)
//			return packednormal.xyz * 2 - 1; 
//		#else
//			fixed3 normal;
//			normal.xy = packednormal.wy * 2 - 1;
//			normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y);
//			return normal;
//		#endif
//		}

	} 
	FallBack "Diffuse"
}
