Предотвращение затенения прозрачных областей проекционным шейдером - PullRequest
0 голосов
/ 09 апреля 2019

Я пытаюсь сделать декаль-шейдер для использования с проектором в Unity. Вот что я собрал:

Shader "Custom/color_projector"
{
    Properties {
        _Color ("Tint Color", Color) = (1,1,1,1)
        _MainTex ("Cookie", 2D) = "gray" {}
    }
    Subshader {
        Tags {"Queue"="Transparent"}

        Pass {

            ZTest Less
            ColorMask RGB
            Blend One OneMinusSrcAlpha
            Offset -1, -1

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f {
                float4 uvShadow : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            float4x4 unity_Projector;
            float4x4 unity_ProjectorClip;

            v2f vert (float4 vertex : POSITION)
            {
                v2f o;
                o.pos = UnityObjectToClipPos (vertex);
                o.uvShadow = mul (unity_Projector, vertex);
                return o;
            }

            sampler2D _MainTex;
            fixed4 _Color;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 tex = tex2Dproj (_MainTex, UNITY_PROJ_COORD(i.uvShadow));
                return _Color * tex.a;

            }
            ENDCG
        }
    }
}

Это хорошо работает в большинстве ситуаций:

enter image description here

Однако всякий раз, когда он проецируется на прозрачную поверхность (или несколько поверхностей), для каждой поверхности создается дополнительное время. Здесь я разбил пропасть между травой и дорожным покрытием, используя текстуры травы с прозрачными областями:

enter image description here

Я пробовал многочисленные варианты смешивания и параметры, а также все параметры ZTesting. Это лучшее, что я могу заставить его посмотреть.

Из чтения я понял, что это может быть потому, что прозрачный шейдер не записывает в буфер глубины. Я попытался добавить ZWrite On и попытался пройти перед основным проходом:

Pass {
   ZWrite On
   ColorMask 0
}

Но ни один из них не имел никакого эффекта.

Как можно изменить этот шейдер, чтобы он только один раз проецировал текстуру на ближайшие геометрии?

Желаемый результат (фотошоп):

enter image description here

1 Ответ

1 голос
/ 09 апреля 2019

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

Сначала добавьте следующее к тегам прозрачного (шейдерного) шейдера:

"IgnoreProjector"="True"

Затем измените очередь рендеринга вашего проектора с «Прозрачный» на «Прозрачный + 1». Это означает, что сначала будет отображаться земля, затем края травы, и, наконец, проектор будет проецироваться на землю (кроме появления сверху, так как оно отображается последним).

Что касается смешивания, я думаю, вы хотите регулярное альфа-смешивание:

Blend SrcAlpha OneMinusSrcAlpha

Другой вариант, если вы используете отложенный рендеринг, это использовать отложенные переводные картинки . Они оба дешевле и обычно проще в использовании, чем проекторы.

...