Как получить оригинальную текстуру из SpriteAtlas для использования в шейдере в качестве альфа-маски?
У меня есть проблема, и мне нужна помощь, чтобы решить ее.
Я пытаюсь раскрасить некоторые части спрайта динамически с помощью пользовательского шейдера и дополнительных текстур в качестве масок. Это не проблема, и я решаю ее легко.
Экран для лучшего понимания:
Это реальная реализация, и смешение цветов для их изображения было реализовано с помощью шейдера, спрайта и маски.
Но я был вынужден использовать атласы для сохранения оперативной памяти, потому что приходится загружать много спрайтов одновременно (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;
}
1 ответ
Вы должны сообщить спрайдеру, где находится его положение в атласе и насколько велик атлас, чтобы он мог преобразовать IN.texcoord
УФ в пространстве атласа к соответствующему УФ в пространстве спрайта. Затем вы можете взять образец из альфа-карты, используя пространство спрайтов 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);
}
В шейдере рассчитайте текущее УФ в пространстве спрайтов и используйте его для выборки из _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 вам нужно будет указать DefaultPackerPolicy
в пакере спрайтов или укажите [RECT] в теге упаковки. Если вы используете SpriteAtlas
, вам нужно будет отключить Tight Packing
,
Код из этой темы на форумах Unity