ПрограммированиеФорумГрафика

Unity: Как сделать alpha blending mask в UI?

#0
12:55, 23 фев 2016

Привет всем!
Делаю текст на UI канвасе, пытаюсь сделать так, чтобы текст плавно исчезал.
В UI unity есть замечательная штука "Mask"& Как выяснилось она не умеет блендить.
Хотел отнаследовать свой скрипт от Mask и с ним поработать, но он не доступен в MonoDevelop.
Как можно блендить UI элементы?)

#1
13:34, 23 фев 2016

Регулировка прозрачности(алфа канал) цвета текста не?

#2
14:29, 23 фев 2016

Не-не-не. В том-то и фишка, что текст должен быть не везде прозрачен.
Вот так должно это выглядеть:
Безымянный | Unity: Как сделать alpha blending mask в UI?

Скрипт Mask делает либо alpha = 1 либо 0.

#3
15:40, 23 фев 2016

Ситуация пока такая:
UI состоит из канваса в котором дочерний обьект Foo содержит Image с моим материалом.
Foo содержит в себе game object с Text:
Canvas-
          |-Foo-(Image component)
                  |-GameObject (Text component)

Получилось изменять картинку, которая находится в Foo с помощью alpha-маски, которая передается в материал и вешается на Image. Тут достаточно все просто:

Cull Off
    Lighting Off
    //ZWrite off
    //ZTest [unity_GUIZTestMode]
    
    ZTest Always//GEqual 
    ZWrite off
    
    Fog { Mode Off }
    Blend SrcAlpha OneMinusSrcAlpha
    ColorMask [_ColorMask]
    Tags { "Queue"="Overlay" "IgnoreProjector"="True" "RenderType"="Transparent" } 

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

      struct appdata{
        float4 vertex : SV_POSITION; 
        float2 uv : TEXCOORD0;
        float2 depth : TEXCOORD1;
        //float4 color : COLOR;
      };

      struct v2f{
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
        float2 depth : TEXCOORD1;
        //float4 color : COLOR;
      };

      uniform sampler2D _MainTex;
      uniform sampler2D _MaskTex;
      float4 _MainTex_ST;
      
      v2f vert (appdata v){
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        UNITY_TRANSFER_DEPTH(o.depth);
        return o;
      }
      
      fixed4 frag (v2f i) : SV_Target{
        fixed4 col = tex2D(_MainTex, i.uv);
        float alpha = tex2D(_MaskTex, i.uv);
        col.a *= alpha;  
        
        float2 depth = i.depth;
        UNITY_OUTPUT_DEPTH(depth);    
        return col;
      }
      ENDCG
    } 

Что хотел бы достичь, не знаю это так делается или нет:
как-то получить доступ к пикселям текста, который должен отрисоваться и сблендить их с маской, которая в материале Foo.
Как достучаться до пикселей текста? Через буффер глубины? Пока я не имел опыта с такого рода махинациями)

#4
16:48, 23 фев 2016

В сети, на сколько я понял, многие интересовались этим эффектом, но unity пока не реализовало его, то есть нет готового скрипта так что вставил и все заработало. Неужто никаких идей по этому поводу?

#5
17:12, 23 фев 2016

У компонента Text тоже есть материал, в который можно вставить любой шейдер.

#6
10:19, 24 фев 2016

Перепробовал что мог попробовать. Непонятно как этот материал использовать. Я не спец в шейдерах, и не понимаю как UI Text работает.
Похоже, что каждая буква имеет свои вершины. Но тогда шейдер будет работать относительно вершин этой буквы.
Хорошо, можно попробовать как-то передать в шейдер текста вершины UI Image.
И это уже хорошо. Я верно мыслю?

#7
19:23, 26 фев 2016

Вообщем, пришла такая мысль: в материал передать текстуру по которй альфу настраиваем.
Вообщем-то идея норм, вот код:

Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #pragma target 3.0
      #include "UnityCG.cginc"

      struct appdata{
        float2 uv : TEXCOORD0;// tex
        float4 vertex : SV_POSITION; // pos
      } ;

      struct v2f{
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;// WPOS
        
        float4 scrPos;
      } ;

      uniform sampler2D _MainTex;
      uniform sampler2D _MaskTex;
      
      // (беру эти данные из RectTransform)
      uniform float _BackgroundParamMinY;// нижняя граница картинки с маской
      uniform float _BackgroundParamH;// высотка маски

      fixed4 _Color;
      
      v2f vert (appdata v){
        v2f o;
        o.uv = v.uv;//ComputeScreenPos(v.uv);
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);//
        
        o.scrPos = ComputeScreenPos(o.vertex);
        
        return o;
      }
      
      fixed4 frag (v2f i, float4 sp:WPOS) : COLOR{
        fixed4 col = tex2D(_MainTex, i.uv);
        col.rgb = (1.0-col.rgb)*_Color.rgb;
        
        sp.x = 0;
        sp.y = (sp.y - _BackgroundParamMinY) / _BackgroundParamH;// uv 
        
        float alpha = tex2D(_MaskTex, sp.xy);
        col.a *= alpha;  
        return col;
      }

      ENDCG
    }  

sp:WPOS - нашел в сети, вроде как позиция пиуселя в мире.
Беда в том, что на вращение этот код не расчитан + считает все в координатах экрана.
Как правильно получать sp.xy?

#8
23:27, 26 фев 2016

С шейдером действительно оказалось довольно заморочено. Сходу не сообразил, как перевести входящие в шейдер координаты вершин (заданные в локальной системе координат родительского Canvas) в uv-координаты для маски. Не уверен, что в шейдере для этого есть вся нужная информация. Если нету, то нужен скрипт, который скормит шейдеру эту информацию.

Но раз всё равно нужен скрипт, то можно обойтись и без шейдера. Так, например:

+ Показать
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class VerticalTextGradient : BaseMeshEffect
{
  public Color topColor = new Color(1, 1, 1, 0);
  public Color bottomColor = new Color(1, 1, 1, 1);
  private List<UIVertex> vertices = new List<UIVertex>();
  private Vector3[] corners = new Vector3[4]; // lower-left, upper-left, upper-right, lower-right

  public override void ModifyMesh(VertexHelper vh)
  {
    if (IsActive() == false || vh.currentVertCount == 0)
    {
      return;
    }

    graphic.rectTransform.GetLocalCorners(corners);
    var top = corners[1].y;
    var bottom = corners[0].y;

    vh.GetUIVertexStream(vertices);
    for (int i = 0; i < vertices.Count; i++)
    {
      var uiVertex = vertices[i];
      var t = Mathf.InverseLerp(bottom, top, uiVertex.position.y);
      uiVertex.color *= Color.Lerp(bottomColor, topColor, t);
      vertices[i] = uiVertex;
    }
    vh.Clear();
    vh.AddUIVertexTriangleStream(vertices);
  }
}
#9
0:28, 27 фев 2016

Получилось сделать настоящую маску:

mask | Unity: Как сделать alpha blending mask в UI?

Для этого потребовались:

1. Скрипт, который пересчитывает координаты прямоугольника с текстом в uv-координаты для маски и сохраняет результат в дополнительный канал uv, который можно будет читать из шейдера.

+ Показать

2. Шейдер, накладывающий маску. Чуть модифицировал стандартный GUI/Text Shader.

+ Показать
#10
9:16, 27 фев 2016

Что-то странное творитcя.
ModifyMesh(VertexHelper vh) этой функции нет в BaseMeshEffect:
public abstract class BaseMeshEffect : UIBehaviour, IMeshModifier
  {
    //
    // Properties
    //
    protected Graphic graphic{...

    //
    // Methods
    //
    public abstract void ModifyMesh (Mesh mesh);

    protected override void OnDidApplyAnimationProperties ()...

    protected override void OnDisable ()...

    protected override void OnEnable ()...

    protected override void OnValidate ()...
  }

#11
10:06, 27 фев 2016

Я не особо силен в ModifyMesh, пока не разобрался как он работает и как из него выудить VertexHelper. Похоже этот метод устарел и его исключили: http://forum.unity3d.com/threads/basemesheffect-that-removes-elements.365783/

#12
15:05, 27 фев 2016

Обнови Unity.

#13
22:03, 27 фев 2016

alexzzzz
Спасибо! Очень помог)

ПрограммированиеФорумГрафика

Тема в архиве.