/**
 * Depth-buffer based cel shading for ENB by kingeric1992
 * http://enbseries.enbdev.com/forum/viewtopic.php?f=7&t=3244#p53168
 *
 * Modified and optimized for ReShade by JPulowski
 * http://reshade.me/forum/shader-presentation/261
 *
 * Do not distribute without giving credit to the original author(s).
 * 
 * 1.0  - Initial release/port
 * 1.1  - Replaced depth linearization algorithm with another one by crosire
 *        Added an option to tweak accuracy
 *        Modified the code to make it compatible with SweetFX 2.0 Preview 7 and new Operation Piggyback which should give some performance increase
 * 1.1a - Framework port
 * 1.2  - Changed the name to "Outline" since technically this is not Cel shading (See https://en.wikipedia.org/wiki/Cel_shading)
 *        Added custom outline and background color support
 *        Added a threshold and opacity modifier
 * 1.2a - Now uses the depth buffer linearized by ReShade therefore it should work with pseudo/logaritmic/negative/flipped depth
 *        It is now possible to use the color texture for edge detection
 *        Rewritten and simplified some parts of the code
 * 1.3  - Rewritten for ReShade 3.0 by crosire
 */

uniform int EdgeDetectionMode <
	ui_type = "combo";
	ui_items = "Normal-depth edge detection\0Color edge detection\0";
	ui_label = "Edge Detection Mode";
> = 1;
uniform float EdgeDetectionAccuracy <
	ui_type = "drag";
	ui_min = 0.0; ui_max = 100.0;
	ui_label = "Edge Detection Accuracy";
> = 1.0;
uniform float EdgeSlope <
	ui_type = "drag";
	ui_min = 0.0; ui_max = 10.0;
	ui_label = "Edge Slope";
	ui_tooltip = "Ignores soft edges (less sharp corners) when increased.";
> = 1.0;

uniform float3 OutlineColor <
	ui_type = "color";
	ui_label = "Outline Color";
> = float3(0.0, 0.0, 0.0);
uniform float OutlineOpacity <
	ui_type = "drag";
	ui_min = 0.0; ui_max = 1.0;
	ui_label = "Outline Opacity";
> = 1.0;

uniform bool CustomBackground <
	ui_label = "Custom Background";
	ui_tooltip = "Uses a custom color as background when set to true.";
> = false;
uniform float3 BackgroundColor <
	ui_type = "color";
	ui_label = "Background Color";
> = float3(0.0, 0.0, 0.0);

#include "ReShade.fxh"

float3 GetEdgeSample(float2 coord)
{
	if (EdgeDetectionMode)
	{
		float4 depth = float4(
			ReShade::GetLinearizedDepth(coord + ReShade::PixelSize * float2(1, 0)),
			ReShade::GetLinearizedDepth(coord - ReShade::PixelSize * float2(1, 0)),
			ReShade::GetLinearizedDepth(coord + ReShade::PixelSize * float2(0, 1)),
			ReShade::GetLinearizedDepth(coord - ReShade::PixelSize * float2(0, 1)));

		return normalize(float3(float2(depth.x - depth.y, depth.z - depth.w) * ReShade::ScreenSize, 1.0));
	}
	else
	{
		return tex2D(ReShade::BackBuffer, coord).rgb;
	}
}

float3 PS_Outline(float4 position : SV_Position, float2 texcoord : TEXCOORD0) : SV_Target
{
	float3 color = CustomBackground ? BackgroundColor : tex2D(ReShade::BackBuffer, texcoord).rgb;
	float3 origcolor = color;

	// Sobel operator matrices
	const float3 Gx[3] =
	{
		float3(-1.0, 0.0, 1.0),
		float3(-2.0, 0.0, 2.0),
		float3(-1.0, 0.0, 1.0)
	};
	const float3 Gy[3] =
	{
		float3( 1.0,  2.0,  1.0),
		float3( 0.0,  0.0,  0.0),
		float3(-1.0, -2.0, -1.0)
	};
	
	float3 dotx = 0.0, doty = 0.0;
	
	// Edge detection
	for (int i = 0, j; i < 3; i++)
	{
		j = i - 1;

		dotx += Gx[i].x * GetEdgeSample(texcoord + ReShade::PixelSize * float2(-1, j));
		dotx += Gx[i].y * GetEdgeSample(texcoord + ReShade::PixelSize * float2( 0, j));
		dotx += Gx[i].z * GetEdgeSample(texcoord + ReShade::PixelSize * float2( 1, j));
		
		doty += Gy[i].x * GetEdgeSample(texcoord + ReShade::PixelSize * float2(-1, j));
		doty += Gy[i].y * GetEdgeSample(texcoord + ReShade::PixelSize * float2( 0, j));
		doty += Gy[i].z * GetEdgeSample(texcoord + ReShade::PixelSize * float2( 1, j));
	}
	
	// Boost edge detection
	dotx *= EdgeDetectionAccuracy;
	doty *= EdgeDetectionAccuracy;

	// Return custom color when weight over threshold
	color = lerp(color, OutlineColor, sqrt(dot(dotx, dotx) + dot(doty, doty)) >= EdgeSlope);
	
	// Set opacity
	color = lerp(origcolor, color, OutlineOpacity);
	
	return color;
}

technique ThermalOverlay
{
	pass
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_Outline;
	}
}