//
// Atmospheric scattering fragment shader
//
// Author: Sean O'Neil
//
// Copyright (c) 2004 Sean O'Neil
//

uniform float4x4 matViewProjection;
uniform float4x4 matRot;

uniform float3 v3CameraPos;
uniform float3 v3LightPos;
uniform float3 v3InvWavelength;
uniform float fCameraHeight2;
uniform float fOuterRadius;
uniform float fOuterRadius2;
uniform float fInnerRadius;
uniform float fScale;
uniform float fScaleOverScaleDepth;

struct VS_INPUT 
{
	float4 Position			: POSITION;
};

struct VS_OUTPUT 
{
	float4 Position			: POSITION;
	float3 FrontColor		: COLOR0;
	float3 SecondaryColor	: COLOR1;
	float3 Direction		: TEXCOORD0;
};

float scale( float fCos )
{
	float fScaleDepth = 0.25;

	float x = 1.0 - fCos;
	return fScaleDepth * exp( -0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25))) );
}

VS_OUTPUT main( VS_INPUT IN )
{
	float fKrESun		= 0.0375;
	float fKmESun		= 0.0225;
	float fKr4PI		= 0.0314159;
	float fKm4PI		= 0.0188496;
	float fScaleDepth	= 0.25;

	VS_OUTPUT OUT;

	// Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
	float3 v3Pos = mul( matRot, IN.Position ).xyz;
	float3 v3Ray = v3Pos - v3CameraPos;
	float fFar = length( v3Ray );
	v3Ray /= fFar;

	// Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)
	float B = 2.0 * dot( v3CameraPos, v3Ray );
	float C = fCameraHeight2 - fOuterRadius2;
	float fDet = max( 0.0, B * B - 4.0 * C );
	float fNear = 0.5 * (-B - sqrt( fDet ));

	// Calculate the ray's starting position, then calculate its scattering offset
	float3 v3Start = v3CameraPos + v3Ray * fNear;
	fFar -= fNear;
	float fStartAngle = dot( v3Ray, v3Start ) / fOuterRadius;
	float fStartDepth = exp( -1.0 / fScaleDepth );
	float fStartOffset = fStartDepth * scale( fStartAngle );

	// Initialize the scattering loop variables
	float fSampleLength = fFar / 2.0;
	float fScaledLength = fSampleLength * fScale;
	float3 v3SampleRay = v3Ray * fSampleLength;
	float3 v3SamplePoint = v3Start + v3SampleRay * 0.5;

	// Now loop through the sample rays
	float3 v3FrontColor = float3( 0.0, 0.0, 0.0 );
	for( int i = 0; i < 2; i++ )
	{
		float fHeight = length( v3SamplePoint );
		float fDepth = exp( fScaleOverScaleDepth * (fInnerRadius - fHeight) );
		float fLightAngle = dot( v3LightPos, v3SamplePoint ) / fHeight;
		float fCameraAngle = dot( v3Ray, v3SamplePoint ) / fHeight;
		float fScatter = (fStartOffset + fDepth * ( scale( fLightAngle ) - scale( fCameraAngle ) ));
		float3 v3Attenuate = exp( -fScatter * (v3InvWavelength * fKr4PI + fKm4PI) );
		v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
		v3SamplePoint += v3SampleRay;
	}

	// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
	OUT.SecondaryColor.rgb = min( v3FrontColor * fKmESun, float3( 100.0, 100.0, 100.0 ) );
	OUT.FrontColor.rgb = min( v3FrontColor * (v3InvWavelength * fKrESun), float3( 100.0, 100.0, 100.0 ) );


	OUT.Position = mul( matViewProjection, IN.Position );
	OUT.Direction = v3CameraPos - v3Pos;

	return( OUT );
}