precision highp float;
precision highp int;

varying vec2 vUV;

uniform sampler2D textureSampler;
uniform highp sampler2D depthSampler;
uniform vec2 nearFar;
uniform vec2 minMaxSeperation;
uniform float thickness;
uniform float strength;
uniform vec3 lineColor;
#define NORMAL_SCALE 2.5
#define DEPTH_SCALE 0.75

// #include ViewSpacePosition.glsl
// #include NormalsFromDepth.glsl

void compare(inout float depthOutline, inout float normalOutline, float viewSpaceZ, vec3 fragmentViewSpaceNormal, vec2 uv, vec2 offset, vec2 texSizeF){

    vec2 neighborUV = uv + offset;
    // Compute neighbor pixel from UV coordinates (same approach as SSAO)
    ivec2 neighborPixelCoord = ivec2(neighborUV * texSizeF);
    float neighborFragmentDepth = fetchDepthAt(neighborPixelCoord);
    vec3 neighborViewSpacePosition = viewSpacePositionFromDepth(neighborFragmentDepth, neighborUV.x, neighborUV.y);
    
    #ifdef expensiveNormals
        vec3 neighborViewSpaceNormal = expensiveViewSpaceNormalFromNeighbours(neighborUV, neighborViewSpacePosition, neighborFragmentDepth);
    #else
        vec3 neighborViewSpaceNormal = viewSpaceNormalFromNeighbours(neighborUV, neighborViewSpacePosition);
    #endif

    // new
    float depthDifference = min(abs(viewSpaceZ - neighborViewSpacePosition.z), 1.0);
    depthOutline = depthOutline + depthDifference;

    // Use  step function to avoid the normal contributing to the line on the fragments of edges
    float normalDifference = min(pow(length(fragmentViewSpaceNormal - neighborViewSpaceNormal), 2.0), 1.0);
    normalOutline = normalOutline + normalDifference;
}

void main(void)
{
    // Use vUV * textureSize for pixel coordinates (same approach as SSAO)
    ivec2 texSize = getDepthTextureSize();
    vec2 texSizeF = vec2(texSize);
    ivec2 pixelCoord = ivec2(vUV * texSizeF);
    float fragmentDepth = fetchDepthAt(pixelCoord);
    // Check both upper bound (>= 0.9995 means far plane/no geometry)
    // AND lower bound (<= 0.0001 means cleared to black/no geometry written)
    float fragmentWritten = step(0.0001, fragmentDepth) * (1.0 - step(0.9995, fragmentDepth));
    vec4 baseColor = texture2D(textureSampler, vUV);
    if (fragmentWritten == 1.0) {

        vec3 viewSpacePosition = viewSpacePositionFromDepth(fragmentDepth, vUV.x, vUV.y);

        #ifdef expensiveNormals
            vec3 viewSpaceNormal = expensiveViewSpaceNormalFromNeighbours(vUV, viewSpacePosition, fragmentDepth);
        #else
            vec3 viewSpaceNormal = viewSpaceNormalFromNeighbours(vUV, viewSpacePosition);
        #endif

        float depthOutline = 0.0;
        float normalOutline = 0.0;

        float separation = mix(minMaxSeperation.x, minMaxSeperation.y, viewSpacePosition.z/nearFar.y);

        compare(depthOutline, normalOutline, viewSpacePosition.z, viewSpaceNormal, vUV, vec2(depthDimensions.x, 0.0) * separation, texSizeF);
        compare(depthOutline, normalOutline, viewSpacePosition.z, viewSpaceNormal, vUV, vec2(0, depthDimensions.y) * separation, texSizeF);
        compare(depthOutline, normalOutline, viewSpacePosition.z, viewSpaceNormal, vUV, vec2(0, -depthDimensions.y) * separation, texSizeF);
        compare(depthOutline, normalOutline, viewSpacePosition.z, viewSpaceNormal, vUV, vec2(-depthDimensions.x, 0) * separation, texSizeF);

        // Fade out the line strength the closer we get to the short distance view frustum
        float distanceFade = 1.0 - smoothstep(nearFar.y * 0.1, nearFar.y * 0.9, viewSpacePosition.z);
        float depthDifferenceScaling = 1.0 + pow((1.0 - (viewSpacePosition.z / nearFar.y)), 16.0) * 5.0;
        // There are 4 samples, each sample may get contribution from either depth different to neighbors, or normal difference to neighbor, hence 4 * 2 as denominator
        float denominator = (4.0 * 2.0);
        float lineContribution = strength * distanceFade * ((depthDifferenceScaling * depthOutline * DEPTH_SCALE) + normalOutline * NORMAL_SCALE) / denominator;

        gl_FragColor = mix(baseColor, vec4(0.0, 0.0, 0.0, 1.0), lineContribution);
    } else {
        gl_FragColor = baseColor;
    }
}