// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.

namespace Stride.Rendering.Images
{
    /// <summary>
	/// Screen Space Local Reflections shader for Combine Pass
    /// </summary>
    shader SSLRCombinePass : ImageEffectShader, SSLRCommon, Utilities
    {
		// Enable/disable blurring SSR during sampling results and mixing with reflections buffer
		#define SSR_MIX_BLUR 1
		
		float3 SampleSSR(float2 uv)
		{
			float4 ssr = Texture4.SampleLevel(LinearSampler, uv, 0);
			
		#if SSR_MIX_BLUR
			ssr += Texture4.SampleLevel(LinearSampler, uv + float2(0, Texture4TexelSize.y), 0);
			ssr += Texture4.SampleLevel(LinearSampler, uv - float2(0, Texture4TexelSize.y), 0);
			ssr += Texture4.SampleLevel(LinearSampler, uv + float2(Texture4TexelSize.x, 0), 0);
			ssr += Texture4.SampleLevel(LinearSampler, uv - float2(Texture4TexelSize.x, 0), 0);
			ssr *= (1.0f / 5.0f);
		#endif

			return ssr;
		}

		// [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"]
		float3 EnvBRDFApprox(float3 specularColor, float roughness, float NoV)
		{
			// Approximate version, base for pre integrated version
			const half4 c0 = {-1, -0.0275, -0.572, 0.022};
			const half4 c1 = {1, 0.0425, 1.04, -0.04};
			half4 r = roughness * c0 + c1;
			half a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
			half2 AB = half2(-1.04, 1.04) * a004 + r.zw;
			return specularColor * AB.x + saturate(50.0 * specularColor.g) * AB.y;
		}
		
		override stage float4 Shading()
		{
			// Inputs Mapping:
			// Texture0 - Scene Color
			// Texture1 - Depth
			// Texture2 - World Space Normals
			// Texture3 - Specular Color + Roughness
			// Texture4 - Reflections result
			
			float2 uv = streams.TexCoord;
			
			// Sample inputs
			float4 sceneColor = Texture0.SampleLevel(PointSampler, uv, 0);
			float3 ssr = SampleSSR(uv);
			float3 positionWS = ComputeWorldPosition(uv);
			
			// Calculate view space normal vector
			float4 normalsBuffer = Texture2.SampleLevel(PointSampler, uv, 0);
			float3 normalWS = DecodeNormal(normalsBuffer.rgb);
			float3 normalVS = mul(normalWS, (float3x3)V);
			
			// Sample material specular color and roughness
			float4 specularRoughnessBuffer = Texture3.SampleLevel(PointSampler, uv, 0);
			float3 specularColor = specularRoughnessBuffer.rgb;
			float roughness = specularRoughnessBuffer.a;
			
			// Calculate reflection color
			float3 viewVector = normalize(CameraPosWS.xyz - positionWS);
			float NoV = saturate(dot(normalWS, viewVector));
			sceneColor.rgb += ssr * EnvBRDFApprox(specularColor, roughness, NoV);
			
			return sceneColor;
		}
    };
}
