Как получить оригинальную текстуру из SpriteAtlas для использования в шейдере в качестве альфа-маски? - PullRequest
0 голосов
/ 17 декабря 2018

У меня есть проблема, и мне нужна помощь для ее решения.

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

Экран для лучшего понимания:

enter image description here

Это реальная реализация и смешение цветовИбо изображение было реализовано с помощью шейдера, спрайта и маски.

Но я был вынужден использовать атласы для сохранения оперативной памяти, поскольку приходится загружать много спрайтов одновременно (3 состояния и 5 направлений для каждого из различныхединицы).И это было вызвано проблемами.Когда я пытаюсь сделать это в режиме редактирования (с помощью ExecuteInEditMode) все работает нормально.Но когда я нажимаю кнопку «играть», все ломается.

Как это выглядит:

enter image description here

Как я понимаю, проблемаэто то, что когда я нажимаю "играть", атлас был построен.Поэтому, когда я получаю от него спрайт, я получаю его с любовью.Но когда я пытаюсь получить текстуру из Sprite для установки ее в Shader, я получаю большую текстуру (полный атлас).Шейдеры ничего не знают о фактическом положении на этом листе, для проверки необходимо пиксельное окрашивание.

Итак, мой вопрос: как я могу получить «маленькую» текстуру из спрайта, чтобы установить ее для шейдера?

Как установить «маску» для шейдера:

public void UpdateMask(Texture tex)
{
    //Debug.LogFormat("UpdateMask {0}", tex);
    m_SRenderer.GetPropertyBlock(m_SpriteMbp);
    m_SpriteMbp.SetTexture("_Mask", tex);
    m_SRenderer.SetPropertyBlock(m_SpriteMbp);
}

Некоторые части шейдера:

Properties 
{
    [PerRendererData] _MainTex("Sprite Texture (RGB)", 2D) = "white" {}
    [PerRendererData] _Mask("Alpha (A)", 2D)  = "white" {}

    _FriendlyColor("FriendlyColor", Color) = (1,1,1,1)
    _EnemyColor("EnemyColor", Color) = (1,1,1,1)
    _NeutralColor("NeutralColor", Color) = (1,1,1,1)

    _Intencity("BlendingIntencity", Range(0, 1)) = 0.5
    [PerRendererData] _IsFriendly("IsFriendly", Float) = 0

    [PerRendererData] _IsHighlight("Outline", Range(0, 1)) = 0
    [PerRendererData] _HighlightColor("Outline Color", Color) = (1,1,1,1)
}


fixed4 frag(v2f IN) : COLOR
{
    fixed4 mainTex = tex2D(_MainTex, IN.texcoord) * IN.color;
    fixed4 alphaMask = tex2D(_Mask, IN.texcoord) * IN.color;
    fixed4 output;
    fixed4 blendColor;

    if (alphaMask.a > 1.0e-6)
    {
        if (_IsFriendly == 4)
        {
            blendColor = fixed4(_NeutralColor.r, _NeutralColor.g, _NeutralColor.b, alphaMask.r);
        }
        else
        {
            if (_IsFriendly == 1)
            {
                blendColor = fixed4(_FriendlyColor.r, _FriendlyColor.g, _FriendlyColor.b, alphaMask.r);                     
            }
            else
            {
                blendColor = fixed4(_EnemyColor.r, _EnemyColor.g, _EnemyColor.b, alphaMask.r);
            }
        }                   
        output = BlendOverelay(mainTex, blendColor * _Intencity);
    }   
    else
    {
        output = mainTex;
        output.rgb *= output.a;
    }

    if (_IsHighlight != 0)
    {
        fixed4 blendedColor = BlendAdditive(output, _HighlightColor);
        blendedColor.a = output.a;
        blendedColor.rgb *= output.a;
        output = blendedColor;
    }

    return output;
}

1 Ответ

0 голосов
/ 17 декабря 2018

Вы должны указать рендереру спрайтов, где находится его положение в атласе и насколько велик атлас, чтобы он мог преобразовать IN.texcoord UV в пространстве атласа в соответствующее UV в пространстве спрайтов.Затем вы можете произвести выборку из альфа-карты, используя пространство спрайтов UV

В C # установите информацию о смещении и масштабе атласа, например, _AtlasPosition:

public void UpdateMask(Texture tex)
{
    //Debug.LogFormat("UpdateMask {0}", tex);
    m_SRenderer.GetPropertyBlock(m_SpriteMbp);
    m_SpriteMbp.SetTexture("_Mask", tex);
    Vector4 result = new Vector4(sprite.textureRect.position.x, sprite.textureRect.position.y, sprite.textureRect.size.x, sprite.textureRect.size.y)

    m_SpriteMbp.SetVector("_AtlasPosition", result)
    m_SRenderer.SetPropertyBlock(m_SpriteMbp);
}

В шейдере вычислитетекущий UV в пространстве спрайтов, и используйте его для выборки из _Mask:

fixed4 frag(v2f IN) : COLOR
{
    fixed4 mainTex = tex2D(_MainTex, IN.texcoord) * IN.color;
    // multiply both the position offset and size by the texel size to bring them into UV space
    float4 atlasOffsetScale = _AtlasPosition * _MainTex_TexelSize.xyxy;
    // apply UV position offset and scale, sample from alpha mask
    fixed4 alphaMask = tex2D(_Mask, (IN.texcoord - atlasOffsetScale.xy) / atlasOffsetScale.zw) * IN.color; 
    fixed4 output;
    fixed4 blendColor;
    // ...

Вам придется объявить _MainTex_TexelSize в вашем шейдере, если вы этого еще не сделали.

Это не будет работать, если вы используете плотную упаковку.Для упаковщика спрайтов вам потребуется указать DefaultPackerPolicy в пакере спрайтов или указать [RECT] в теге упаковки.Если вы используете SpriteAtlas, вам необходимо отключить Tight Packing.

Код, полученный из этой ветки на форумах Unity

...