【问题标题】:How to get original texture from SpriteAtlas to use in a shader as alpha mask?如何从 SpriteAtlas 获取原始纹理以在着色器中用作 alpha 蒙版?
【发布时间】:2018-12-17 17:31:16
【问题描述】:

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

我尝试通过自定义着色器和附加纹理作为蒙版动态着色精灵的某些部分。这不是问题,我很容易解决。

更好理解的屏幕:

这是真正的实现,这张图片的颜色混合是通过着色器、精灵和蒙版实现的。

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

它的样子:

据我了解,问题是当我按下“播放”时,地图集已构建。所以当我从中得到精灵时,我会成功地得到它。但是当我尝试从 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;
}

【问题讨论】:

    标签: c# unity3d shader shaderlab


    【解决方案1】:

    你需要告诉精灵渲染器它在图集中的位置以及图集有多大,以便它可以将图集空间中的IN.texcoord UV 转换为精灵空间中对应的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

    【讨论】:

    • 我无法获取精灵的 textureRect。当我尝试获取它时,我得到了异常:>UnityException:Sprite 不是矩形包装的。 TextureRect 无效。 UnityEngine.Sprite.get_textureRect () (at C:/buildslave/unity/build/artifacts/generated/common/runtime/SpritesBindings.gen.cs:152) TestAtlas.Start () (at Assets/TestAtlas.cs:14) 这个问题与新的 Unity 精灵图集有关。 docs.unity3d.com/Manual/class-SpriteAtlas.html
    • 谢谢。您需要在 sprite 打包器中指定 DefaultPackerPolicy 或在打包标签中指定 [RECT]。我已经编辑了我的答案以包含该部分。
    • 这与 Sprite Packer 无关。这是关于 Unity 2017+ 中的 SpriteAtlas 类。
    • @SergiyKlimkov 在这种情况下,请为该图集禁用 Tight Packing
    • 谢谢!看起来这是一个解决方案。打包密度比启用“tight”时要小,但它是有效的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多