﻿Shader "Unlit/FMVHFOVCompositingShader"
{
	Properties
	{
		_Tex0 ("LowFovInputImage", 2D)		= "white" {}
		_Tex1 ("MediumFovInputImage", 2D)	= "white" {}
		_Tex2 ("HighFovInputImage", 2D)		= "white" {}
		_Tex3 ("HighestFovInputImage", 2D)	= "white" {}
		_tex0Fov ("LowFovInputImage     Fov Degrees", float) = 30
		_tex1Fov ("MediumFovInputImage  Fov Degrees", float) = 30
		_tex2Fov ("HighFovInputImage    Fov Degrees", float) = 30
		_tex3Fov ("HighestFovInputImage Fov Degrees", float) = 30 
		_imgScaleFactor0("LowFov imageScalingFactor", float)     = 0.1
		_imgScaleFactor1("MediumFov imageScalingFactor", float)  = 0.2
		_imgScaleFactor2("HighFov imageScalingFactor", float)    = 0.3
		_imgScaleFactor3("HighestFov imageScalingFactor", float) = 0.5
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			//using square power of two images for simplicity
			float4 _Tex0_ST;
			sampler2D _Tex0;
			sampler2D _Tex1;
			sampler2D _Tex2;
			sampler2D _Tex3;

			//the fov values of the input images

			float _tex0Fov;
			float _tex1Fov;
			float _tex2Fov;
			float _tex3Fov;

			

			float  _imgScaleFactor0;
			float  _imgScaleFactor1;
			float  _imgScaleFactor2;
			float  _imgScaleFactor3;

			#define PiDiv2 1.570796//3267948966192313216916398
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _Tex0);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}

			//generate the output FMHFOV image with a circular region of large size, low fov, and an expontial taper
			//to the max fov edge of the image
			//FMHFOV stands for Fovia Magnified High Field of View Image

			fixed4 getSample(float xFovPct, float yFovPct, float sampleImageFovPct, sampler2D texSampler)
			{
				//map the fov percentage to the pixel of the the flat projection matrix projected image
				//(trapezoid surface placed inside of a unit sphere)
				//ray from the center to the edge of the circle that would map to the given fov angle 
				///      -        //  fov angle sphere
				///  - ----- -    //  projected image plane
				/// -    .    -   //  camera center point
				///  -       -
				///      -
				///to find the pixel coordinate find the intersection between the fov ray and the projected image plane
				//equation of a ray p = s + t * d (point = ray start point + time multiplied with direction)
				//equation of a plane 0 = (p - c) . n (0 = (point - plane center) dot product normal)
				///(the points minus center must be orthogonal to the normal)
				//solving the ray plane intersection gives t = Cz / dz (ray intersection time = plane distance from center / ray depth component)
				//given that the sphere has radius 1 (normalized), the plane depth is cos(fov) (cosine(angle) = adjacent side length / hypotenuse (1)),
				//the ray length is also 1 (normalized) so the square root of it's depth component squared + x component squared + y component squared
				// = 1
				//this gives the depth component (dz) to be = sqrt( 1-(dx^2 + dy^2) )
				//dx and dy are  dx = sin(fovx) dy = sin(fovy) because sin(angle) = opposite side length / hupotenuse (1)

				///input bounds
				//since the image plane is fit into the sphere with it's maximum / minumum x and y values touching the spheres surface,
				//the corners of the plane are outside the sphere and invalid coordinates for this mapping
				//also any image fov (maxFov) greater than 180 degrees is invalid

				
				float cz = 1-sampleImageFovPct;		//plane depth
				float dx = xFovPct / sampleImageFovPct;// sin(xFovPct * PiDiv2);				//unit circle sample point ray x component
				float dy = yFovPct / sampleImageFovPct;// sin(yFovPct * PiDiv2);				//unit circle sample point ray y component
				
				float dz = sqrt(1 - dx * dx - dy * dy);// cos(ray2dLength * PiDiv2); //unit circle sample point ray z component (depth)
				
				float rayIntersectionTime = cz / dz;
				float samplePlaneX = rayIntersectionTime * dx;
				float samplePlaneY = rayIntersectionTime * dy;

				float x = (dx) / 2 + 0.5;
				float y = (dy) / 2 + 0.5;

				float sampleCoordLength = sqrt(dx*dx + dy * dy);

				//float retCol = sampleCoordLength;
				//return half4(retCol, retCol, retCol, 1);

				return tex2D(texSampler, float2(x, y));
			}

			float fovPctEquation(float fov)
			{
				float a = 0.000000706349 * pow(fov,3);
				float b = 0.000113968 * pow(fov,2);
				float c = 0.00854048 * fov;
				return a - b  + c - 0.100714;
			}

			fixed4 lookupSample(float xFovPct, float yFovPct, float samplePointFov)
			{
				float tex0FovPct =  fovPctEquation(_tex0Fov);
				float tex1FovPct =  fovPctEquation(_tex1Fov);
				float tex2FovPct =  fovPctEquation(_tex2Fov);
				float tex3FovPct =  fovPctEquation(_tex3Fov);

				if (samplePointFov < tex0FovPct) //first image (smallest fov)
				{
					return getSample(xFovPct, yFovPct, tex0FovPct, _Tex0);
				}
				else if (samplePointFov < tex1FovPct)
				{
					return getSample(xFovPct, yFovPct, tex1FovPct, _Tex1);
				}
				else if (samplePointFov < tex2FovPct)
				{
					return getSample(xFovPct, yFovPct, tex2FovPct, _Tex2);
				}
				else								 //last image (max of the sample image Fov's)
				{
					//float retVal = samplePointFov/1;
					//return half4(retVal, retVal, retVal, 1);
					return getSample(xFovPct, yFovPct, tex3FovPct, _Tex3);
				}
				
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				//lookup coordinates (x and y are percentages of half of the unit sphere (180 deg))
				float xFovPct = (i.uv.x - 0.5) * 2; //map the (0.0,1.0) uv coordinates to (-1.0, 1.0) range
				float yFovPct = (i.uv.y - 0.5) * 2;

				float samplePointFovPct = sqrt(xFovPct*xFovPct + yFovPct * yFovPct);

				if (samplePointFovPct > 1)
					return half4(0, 0, 0, 1);

				float scaleFactor = _imgScaleFactor0;
				if (samplePointFovPct > _imgScaleFactor1)
				{
					float pctToEdgeOfImage = (1 - samplePointFovPct) / (1- _imgScaleFactor1);
					scaleFactor = pctToEdgeOfImage *  _imgScaleFactor0 + (1-pctToEdgeOfImage);
				}

				xFovPct *= scaleFactor;
				yFovPct *= scaleFactor;
					

				samplePointFovPct = sqrt(xFovPct*xFovPct + yFovPct*yFovPct);



				//float samplePointFov = (sqrt(xFovPct*xFovPct + yFovPct * yFovPct) );

				//float retVal = samplePointFov;
				//return half4(retVal, retVal, retVal, 1);

				/*
				float sampleFov = 0;
				if (distFromCenter < _outputImageParameters.y)
				{
					//inner high resolution circular region, lerp between 0 and circular region fov limit (_outputImageParameters.x)
					float innerRegionPct = distFromCenter / _outputImageParameters.y;
					sampleFov = innerRegionPct * _outputImageParameters.x;
				}
				else
				{
					//exponential falloff from circular region fov limit to image3 fov _fovValues.w
					//-----
					//      -
					//       -
					float outerRegionPct = (distFromCenter - _outputImageParameters.y) / (1- _outputImageParameters.y);
					float sqrdOuterRegionPct = (outerRegionPct);// *outerRegionPct);
					sampleFov = (1- sqrdOuterRegionPct) * _outputImageParameters.x + sqrdOuterRegionPct * _fovValues.w;
				}
				*/
				

				// sample the texture
				fixed4 col = lookupSample(xFovPct, yFovPct, samplePointFovPct);
				

				// apply fog
				UNITY_APPLY_FOG(i.fogCoord, col);
				return col;
			}
			ENDCG
		}
	}
}
