﻿#ifndef BEAUTIFY_PPSSF_FX
#define BEAUTIFY_PPSSF_FX		

	// Copyright 2020-2021 Kronnect - All Rights Reserved.
    #include "BeautifyCommon.hlsl"

	TEXTURE2D_X(_MainTex);
	TEXTURE2D(_FlareTex);
    TEXTURE2D(_OcclusionTex);

	float4 _MainTex_ST;
	float4 _MainTex_TexelSize;
	float4 _SunPos;
	float3 _SunDir;
	float4 _SunData;	// x = sunIntensity, y = disk size, z = ray difraction, w = ray difraction amount
	float4 _SunCoronaRays1;  // x = length, y = streaks, z = spread, w = angle offset
	float4 _SunCoronaRays2;  // x = length, y = streaks, z = spread, w = angle offset
	float4 _SunGhosts1;  // x = reserved, y = size, 2 = pos offset, 3 = brightness
	float4 _SunGhosts2;  // x = reserved, y = size, 2 = pos offset, 3 = brightness
	float4 _SunGhosts3;  // x = reserved, y = size, 2 = pos offset, 3 = brightness
	float4 _SunGhosts4;  // x = reserved, y = size, 2 = pos offset, 3 = brightness
   	float3 _SunHalo;  // x = offset, y = amplitude, z = intensity
   	float4 _SunTint;
	float  _SunFlaresAspectRatio;
	float  _SunOcclusionThreshold;
	#define SUN_TINT_COLOR _SunTint.rgb
	#define OCCLUSION_SPEED _SunTint.a
   	#define OCCLUSION_THRESHOLD _SunOcclusionThreshold

	struct VaryingsSF {
		float4 positionCS : SV_POSITION;
		float2 uv     : TEXCOORD0;
		float2 sunPos : TEXCOORD1;
		UNITY_VERTEX_OUTPUT_STEREO
	};

	VaryingsSF VertSF(AttributesSimple input) {
		VaryingsSF output;
		UNITY_SETUP_INSTANCE_ID(input);
		UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
		output.positionCS = input.positionOS;
		output.positionCS.y *= _ProjectionParams.x * _FlipY;
		output.uv = input.uv.xy;

		float4 clipPos = TransformWorldToHClip(_WorldSpaceCameraPos.xyz - _SunDir.xyz * 1000.0);
		float2 sunPos = float2(clipPos.x, clipPos.y * _ProjectionParams.x) * 0.5 / clipPos.w + 0.5;
		output.sunPos = sunPos;

		return output;
	}

	void rotate(inout float2 uv, float ang) {
		float2 sico;
		sincos(ang, sico.x, sico.y);
		float2 cosi = float2(sico.y, -sico.x);
		uv = float2(dot(cosi, uv), dot(sico, uv));
	}
	
   	float3 sunflare(VaryingsSF input) {

		// general params
		float2 uv = input.uv;
		float2 sunPos = input.sunPos;

		#if BEAUTIFY_SF_OCCLUSION_SIMPLE
   			float depth  = BEAUTIFY_GET_SCENE_DEPTH_01(sunPos);
			if (depth < 1.0) return 0;
			const float occlusion = 1.0;
		#elif BEAUTIFY_SF_OCCLUSION_SMOOTH
			float occlusion = SAMPLE_TEXTURE2D(_OcclusionTex, sampler_LinearRepeat, float2(0,0)).r;
			if (occlusion <= 0.02) return 0.0.xxx;
		#else
			const float occlusion = 1.0;
		#endif

   		float2 grd = uv - sunPos;
		float aspectRatio = _SunFlaresAspectRatio;
   		grd.y *= aspectRatio; 
   		float len = length(grd);

   		// sun disk
   		float s0 = pow( 1.0 + saturate(_SunData.y - len), 75) - 1.0;
        
   		// corona rays
		float gang = _SunPos.w; //atan2(0.5 - sunPos.y, sunPos.x - 0.5);
   		float ang = atan2(grd.y, grd.x) + gang;
   		float ray1 = _SunCoronaRays1.z + abs(_SunCoronaRays1.x * cos(_SunCoronaRays1.w + ang * _SunCoronaRays1.y));	// design
   		ray1 *= pow( 1.0 + len, 1.0/_SunCoronaRays1.x);	
   		s0 += 1.0 / ray1;

   		float ray2 = _SunCoronaRays2.z + abs(_SunCoronaRays2.x * sin(_SunCoronaRays2.w + ang * _SunCoronaRays2.y));	// design
   		ray2 *= pow( 1.0 + len, 1.0/_SunCoronaRays2.x);	
   		s0 += 1.0 / ray2;
   		
   		s0 *= _SunData.x;
   		
   		float3 flare = s0.xxx;
		
		#if BEAUTIFY_SF_USE_GHOSTS // defined(UNITY_SINGLE_PASS_STEREO) && !defined(UNITY_STEREO_INSTANCING_ENABLED) && !defined(UNITY_STEREO_MULTIVIEW_ENABLED)
   		// ghosts circular (not compatible with XR due to how projection works)

   		float2 ghost1Pos  = 1.0 - sunPos;
   		grd = uv - ghost1Pos + (ghost1Pos - 0.5) * _SunGhosts1.z;
		grd.y *= aspectRatio;

		float g0 = saturate(_SunGhosts1.y / length(grd)); 
		g0 = pow(g0, 12);
   		flare += g0 * _SunGhosts1.w / len;

   		float2 ghost2Pos  = 1.0 - sunPos;
   		grd = uv - ghost2Pos + (ghost2Pos - 0.5) * _SunGhosts2.z;
		grd.y *= aspectRatio;
		g0 = saturate(_SunGhosts2.y / length(grd)); 
		g0 = pow(g0, 12);
   		flare +=  g0 * _SunGhosts2.w / len;

   		float2 ghost3Pos  = 1.0 - sunPos;
   		grd = uv - ghost3Pos + (ghost3Pos - 0.5) * _SunGhosts3.z;
		grd.y *= aspectRatio;
		g0 = saturate(_SunGhosts3.y / length(grd)); 
		g0 = pow(g0, 12);
   		flare +=  g0 * _SunGhosts3.w / len;

   		float2 ghost4Pos  = 1.0 - sunPos;
   		grd = uv - ghost4Pos + (ghost4Pos - 0.5) * _SunGhosts4.z;
		grd.y *= aspectRatio;
		g0 = saturate(_SunGhosts4.y / length(grd)); 
		g0 = pow(g0, 12);
   		flare +=  g0 * _SunGhosts4.w / len;

   		#endif

		// light rays
		float2 uv2 = uv - sunPos;
		float clen = length(uv2);
		rotate(uv2, gang);
		uv2.x *= aspectRatio;
		uv2.x *= 0.1;
		uv2 /= len;
		float lr = saturate(SAMPLE_TEXTURE2D(_FlareTex, sampler_LinearRepeat, uv2 + _SunPos.zz).r - _SunData.w);
		float3 rays = lr * sin(float3(len, len + 0.1, len + 0.2) * 3.1415927);
		float atten = pow(1.0 + clen, 13.0);
		rays *= _SunData.z / atten;
		flare += rays;

		// halo
		float hlen = clamp( (len - _SunHalo.x) * _SunHalo.y, 0, 3.1415927);
		float3 halo = pow(sin(float3(hlen, hlen + 0.1, hlen + 0.2)), 12.0.xxx);
		halo *= _SunHalo.z / atten;
		flare += halo; 
		
		return max(0, flare * SUN_TINT_COLOR * occlusion);
   	}  
   	
  	float4 FragSF (VaryingsSF i) : SV_Target {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
        //i.uv = UnityStereoTransformScreenSpaceTex(i.uv);

   		return float4(sunflare(i), 1.0);
   	}  

  	float4 FragSFAdditive (VaryingsSF i) : SV_Target {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
        float2 stereoUV = UnityStereoTransformScreenSpaceTex(i.uv);

  		float4 p = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, stereoUV);
   		return p + float4(sunflare(i), 1.0);
   	}  

    float GetDepth(float2 sunPos, float2 offset) {
        float2 pos = saturate(sunPos + offset * _MainTex_TexelSize.xy);
        return BEAUTIFY_GET_SCENE_DEPTH_01(pos);
    }

   	float OcclusionTest(float2 sunPos) {

		float  depth1 = GetDepth(sunPos, float2(-1, -1));
        float  depth2 = GetDepth(sunPos, float2( 1,  1));
        float  depth3 = GetDepth(sunPos, float2( 3,  3));
        float  depth4 = GetDepth(sunPos, float2(-3, -3));
		float occlusion = (depth1 + depth2 + depth3 + depth4) * 0.25;
		occlusion *= step(OCCLUSION_THRESHOLD, occlusion);
        return occlusion;
    }

  	float4 FragSFOcclusion (VaryingsSF input) : SV_Target {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
        float occlusion = OcclusionTest(input.sunPos);
        #if BEAUTIFY_SF_OCCLUSION_INIT
            return float4(occlusion, occlusion, occlusion, 1.0);
        #else
            return float4(occlusion, occlusion, occlusion, unity_DeltaTime.x * OCCLUSION_SPEED);
        #endif
   	}

#endif