如何从SpriteAtlas获取原始纹理以在着色器中用作Alpha蒙版?

时间:2018-12-17 17:31:17

标签: c# unity3d shader shaderlab

我有一个问题,我需要帮助来解决。

我尝试通过自定义着色器和其他纹理作为遮罩动态地对精灵的某些部分进行着色。这不是问题,我很容易解决。

为了更好地理解屏幕:

enter image description here

这是真正的实现,并且通过着色器,子画面和蒙版实现了它们图像的颜色混合。

但是我被迫使用地图集来节省RAM,因为必须同时加载许多精灵(每个单元3个状态和5个方向)。这是造成问题的原因。当我尝试在编辑模式下(通过使用ExecuteInEditMode)执行此操作时,一切正常。但是当我按下“播放”按钮时,一切都崩溃了。

外观如何:

enter image description here

据我了解,问题在于,当我按“播放”图集时,就会生成该图集。因此,当我从中获得精灵时,我就会得到怀疑。但是,当我尝试从Sprite获取纹理以将其设置为Shader时,我得到了很大的纹理(完整图集)。着色器不知道该薄片上的实际位置,因为像素需要着色。

所以我的问题是:如何从子画面获取“小”纹理并将其设置为着色器?

如何将“ mask”设置为着色器:

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 :(得分:2)

您需要告诉子画面渲染器,其在地图集中的位置以及地图集的大小,以便可以将IN.texcoord UV转换为Atlas空间中的UV,并将其转换为Sprite空间中相应的UV。然后,您可以使用精灵空间UV从alpha贴图采样

在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

如果使用紧密包装,则此方法将无效。对于Sprite Packer,您需要在Sprite Packer中指定DefaultPackerPolicy或在包装标签中指定[RECT]。如果您使用的是SpriteAtlas,则需要禁用Tight Packing

源于this thread on the unity forums

的代码