﻿Shader "Custom/DepthShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DispTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
    }

    CGINCLUDE
    #include "UnityCG.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float isForward : TEXCOORD1;
        float4 vertex : SV_POSITION;
        fixed4 visibility: COLOR;
    };

    float4x4 _IVP;
    sampler2D _MainTex;
    sampler2D _DispTex;
    float4 _MainTex_ST;
    float _nearClip;
    float _farClip;
    float _viewPortScale;
    float _viewPortScaleWidthOrHeight;
    float _maxPointSize;
    float _isFloating;

    int _OcclusionPlaneNumber;
    float4 _OcclusionPlanePos[4];
    float4 _OcclusionPlaneNormal[4];
    fixed4 _Color;
    float _depthEdgeSensitivity;

    v2f vert (appdata v)
    {
        // calc view port xy -1.0~1.0
        float vpx = (v.vertex.x - 0.5) * 2 * (1.0 * _viewPortScaleWidthOrHeight + _viewPortScale * (1.0 - _viewPortScaleWidthOrHeight));
        float vpy = (v.vertex.y - 0.5) * 2 * (1.0 * (1.0 - _viewPortScaleWidthOrHeight) + _viewPortScale * _viewPortScaleWidthOrHeight);

        float4 dir4 = mul(_IVP, float4(vpx, vpy, 0, 1));
        float3 dir = float3(dir4.x / dir4.w, dir4.y / dir4.w, dir4.z / dir4.w);
        dir = normalize(dir);

        dir.z = sign(dir.z) * dir.z;
        float4 depthUV = float4(v.uv.x, 1 - v.uv.y, 0, 0);
        float4 depthStep = float4(1.0 / 512.0, 1.0 / 512.0, 0, 0);
        float4 dv = tex2Dlod(_DispTex, depthUV);
        float d = dv.r;
        dir *= d / dir.z;

        v.vertex.xyz = dir;

        float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
        float3 worldZeroPos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
        // object to dummy camera
        float3 worldDummyViewDir = normalize(worldPos - worldZeroPos);
        // object to real camera
        float3 worldViewDir = normalize(-UnityWorldSpaceViewDir(worldPos));
        // if occlusionSum < 0.0, it is invisible.
        float isForward = saturate(3.0 * dot(worldViewDir, worldDummyViewDir) + 1.0);

        float occlusionSum = 0.0;
        for (int i = 0; i<_OcclusionPlaneNumber; i++) {
            float3 planeRelativePos = worldPos - _OcclusionPlanePos[i].xyz;
            float planeDist = dot(planeRelativePos, _OcclusionPlaneNormal[i].xyz);
            occlusionSum = occlusionSum + step(0.0, planeDist) - 1.0;
        }

        float neighborDepths[8];
        neighborDepths[0] = tex2Dlod(_DispTex, depthUV + float4(0.0,  depthStep.y, 0, 0));
        neighborDepths[1] = tex2Dlod(_DispTex, depthUV + float4(depthStep.x, 0.0, 0, 0));
        neighborDepths[2] = tex2Dlod(_DispTex, depthUV + float4(0.0, -depthStep.y, 0, 0));
        neighborDepths[3] = tex2Dlod(_DispTex, depthUV + float4(-depthStep.x, 0.0, 0, 0));
        neighborDepths[4] = tex2Dlod(_DispTex, depthUV + float4(-depthStep.x, -depthStep.y, 0, 0));
        neighborDepths[5] = tex2Dlod(_DispTex, depthUV + float4(depthStep.x,  depthStep.y, 0, 0));
        neighborDepths[6] = tex2Dlod(_DispTex, depthUV + float4(depthStep.x, -depthStep.y, 0, 0));
        neighborDepths[7] = tex2Dlod(_DispTex, depthUV + float4(-depthStep.x,  depthStep.y, 0, 0));
        float nearestDepth = 1.0;
        int farNeighbors = 0;
        float sumNeighborsDistance = 0.0;
        for (int i = 0; i < 8; i++)
        {
            float4 depthNeighbor = neighborDepths[i];
            sumNeighborsDistance += abs(depthNeighbor.r - dv.r);
        }

        if (sumNeighborsDistance > _depthEdgeSensitivity)
        {
            occlusionSum = -1.0;
        }

        v2f o;
        fixed isOccluded = step(0.5, (1.0 - step(-0.0001, occlusionSum)) + (1.0 - step(_nearClip, d)) + step(_farClip, d));
        v.vertex.y += _isFloating * sin(_Time.x * 12.56) * 0.10;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        o.visibility = fixed4(isOccluded, 0, 0, 0);
        o.isForward = isForward;
        return o;
    }

    fixed4 frag (v2f i) : SV_Target
    {
        // sample the texture
        if (i.visibility.r > 0.5) {
            discard;
        }
        fixed4 col = i.isForward * tex2D(_MainTex, i.uv) * _Color;
        return col;
    }

    ENDCG

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
            ENDCG
        }
        Pass
        {
			Name "CastShadow"
			Tags { "LightMode" = "ShadowCaster" }
			ZWrite On ZTest LEqual

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
            ENDCG
        }
    }
}
