Unity Shader выделяет перекрытия

Я пытаюсь написать шейдер для единства, который выделит перекрывающиеся фрагменты сеток. Он должен работать для одного объекта, перекрывающего себя, а также для нескольких объектов.

Результат должен выглядеть как прикрепленное изображение. введите описание изображения здесь

Сначала я попытался выполнить это с обнаружением конфликтов, но я думаю, что лучший способ - написать шейдер.

Я не очень хорошо знаком с шейдерами, поэтому, если бы кто-нибудь мог мне помочь, я был бы благодарен.

Я думаю, что это можно сделать с помощью трафаретных шейдеров, например здесь http://docs.unity3d.com/Manual/SL-Stencil.html но эти шейдеры визуализируют только пересечение двух объектов без рендеринга всего объекта.

Я также нашел шейдер на основе Depth (https://chrismflynn.wordpress.com/2012/09/06/fun-with-shaders-and-the-depth-buffer/), но это также работает на двух объектах и ​​не работает на одной сетке, что перекрытие себя

Что касается комментария @Zze с идеей о двух проходах, у меня теперь два шейдера. И он работает на двух объектах, когда у одного есть один шейдер, а другой - второй.

Может кто-нибудь может помочь мне объединить его в один шейдер, который будет работать и в объекте, который будет перекрываться?

ShaderOne

Shader "Custom/ShaderOne"
{
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}
        Pass {
            Stencil {
                Ref 2
                Comp always
                Pass keep 
                Fail decrWrap 
                ZFail keep
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            struct appdata {
                float4 vertex : POSITION;
            };
            struct v2f {
                float4 pos : SV_POSITION;
            };
            v2f vert(appdata v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }
            half4 frag(v2f i) : SV_Target {
                return half4(0,1,0,1);
            }
            ENDCG
        }
        Pass {
            Stencil {
                Ref 2
                Comp equal
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            struct appdata {
                float4 vertex : POSITION;
            };
            struct v2f {
                float4 pos : SV_POSITION;
            };
            v2f vert(appdata v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }
            half4 frag(v2f i) : SV_Target {
                return half4(0,0,1,1);
            }
            ENDCG
        }

    } 
}

ShaderTwo

Shader "Custom/ShaderTwo"
{
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry"}
        Pass {
            Stencil {
                Ref 2
                Comp always
                Pass replace
                ZFail keep
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            struct appdata {
                float4 vertex : POSITION;
            };
            struct v2f {
                float4 pos : SV_POSITION;
            };
            v2f vert(appdata v) {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }
            half4 frag(v2f i) : SV_Target {
                return half4(1,0,0,1);
            }
            ENDCG
        }
    } 
}

Результат выглядит как прикрепленное изображение введите описание изображения здесь

Ответ 1

Эта проблема может быть решена с помощью буфера Stencil и одного двухпроходного шейдера. Идея такова:

  • Первый проход сравнивает значение в буфере трафарета с 0. В обоих случаях (pass/fail) увеличение значения в буфере.
  • Второй проход сравнивает значение в буфере трафарета с 1. Если опорное значение 1 меньше, чем мы переходим и выделить перекрывающийся пиксель.

Возможно, вы захотите добавить больше проходов, которые совпадают со вторым, но с другим ссылочным значением, чтобы выделить области, которые перекрываются два раза, три раза и т.д.

В нотации Shaderlab Unity должно быть что-то вроде этого:

    Pass
    {
        Stencil {
            Ref 0
            Comp Equal
            Pass IncrSat 
            Fail IncrSat 
        }

        // Shader for not overlapping regions goes here.
    }

    Pass
    {
        Stencil {
            Ref 1
            Comp Less
        }

        // Shader for one-time overlapping regions goes here.
    }

    Pass
    {
        Stencil {
            Ref 2
            Comp Less
        }

        // Shader for two-time overlapping regions goes here.
    }

Пример:

введите описание изображения здесь

Shader:

Shader "Unlit/Stencil"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Stencil {
                Ref 0
                Comp Equal
                Pass IncrSat 
                Fail IncrSat 
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = fixed4(0.0, 0.0, 1.0, 1.0);
                return col;
            }
            ENDCG
        }

        Pass
        {
            Stencil {
                Ref 1
                Comp Less
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = fixed4(1.0, 1.0, 0.0, 1.0);
                return col;
            }
            ENDCG
        }

        Pass
        {
            Stencil {
                Ref 2
                Comp Less
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = fixed4(1.0, 0.0, 0.0, 1.0);
                return col;
            }
            ENDCG
        }
    }
}

Ответ 2

У меня только один вопрос, как бы вы сделали этот шейдер для элемента пользовательского интерфейса? Например: допустим, два элемента пользовательского интерфейса перекрывают друг друга, я хочу, чтобы перекрытие было выделено, но когда я делаю это с шейдером, вы, ребята, предоставили шейдеру перезаписать компонент изображения.

Это можно обойти?