Calmer的文章

  • 首页
  • 文章归档
  • 关于页面

  • 搜索
体验游戏 笔记 推荐 工具链 工具使用 小游戏 插件 UI 软件 教程

FGUI渐变透明遮罩与粒子特效遮罩的支持

发表于 2020-11-27 | 分类于 游戏开发 | 0 | 阅读次数 1666

前言

之前在项目中遇到的问题,需要渐变透明度遮罩与Unity的粒子特效遮罩,而FGUI中只是简单使用模板测试来完成的硬遮罩,显然不能完成更多有趣的效果。

遮罩的问题:遮罩的区域,渲染顺序,遮罩的目标

层级的问题:Unity中的UI层级不要与FGUI混用,如果使用UGUI那就建立相应的UI层级,如果用FGUI那就保证只有一个层级,混用容易导致层级问题出现。


模版测试(硬遮罩)

需要注意一下渲染顺序的问题,等最终所有渲染结束后,再进行一次模版测试即可。

渐变透明度的实现

  1. 透明度渐变遮罩图
  2. 遮罩区域的计算,反向
  3. 渲染顺序
  4. 实现与集成与FGUI

修改Shader

Shader "FairyGUI/Image"
{
    //1.Properties中新增 
    Properties
    {
        //...
        _AlphaMask("AlphaMask",2D) = "white"{} //用于接收遮罩图
        //...
    }
    SubShader
    {
        //...
        Pass
        {
            CGPROGRAM
            //...
            //2.在第一个Pass通道中声明两个预定义变量
            #pragma shader_feature IsAlpha //是否开启渐变透明遮罩
            #pragma shader_feature ReverseMask  //是否反向
            //...
            //3.在v2f 结构体中添加
            struct v2f
            {
                //...
                #ifdef IsAlpha
                    float2 worldPosition:TEXCOORD2;
                    float2 clipUV:TEXCOORD3;
                #endif
                //...
            }
            //4.声明透明遮罩图和遮罩区域
            sampler2D _AlphaMask;  //透明遮罩图
            float4 _AlphaRect;  //遮罩区域

            v2f vert(appdata_t v)
            {
                //...
                //5.顶点着实器中计算UV
                #ifdef IsAlpha
                    o.worldPosition = mul(unity_ObjectToWorld,v.vertex).xy;
                    float _u = (o.worldPosition.x - _AlphaRect.x)/(_AlphaRect.z - _AlphaRect.x);
                    float _v = (o.worldPosition.y - _AlphaRect.w)/(_AlphaRect.y - _AlphaRect.w);
                    o.clipUV = float2(_u,_v);
                #endif
                //...
            }
            fixed4 frag(v2f i) : SV_Target
            {
                //...
                //6.片元着色器中计算颜色混合
                #ifdef IsAlpha
                    float2 pos = i.worldPosition.xy;
                    fixed4 colorAlpha = tex2D(_AlphaMask,i.clipUV);  //采样
                    col.a = saturate(col.a-0.01);
                    fixed curAlpha = 1-colorAlpha.a;
                    #ifdef ReverseMask
                        
                    #endif
                    color.a *=(pos.x >=_AlphaRect.x && pos.x<=_AlphaRect.z && pos.y<=_AlphaRect.y && pos.y>=_AlphaRect.w)? curAlpha:0;
                #endif
                //...
            }
            //...
            ENDCG
        }
        //...
    }
}

修改FGUI源码,修改Container类

public class Container:DisplayObject
{
    //...
    private bool isAlphaMask = false;
    public DisplayObject mask
    {
        get
        {
            return _mask;
        }
        set
        {
            if(_mask!=value)
                _mask = value;
            if(_mask.gOwner as GImage == null) //这里做了一个巧妙的判断,当给予GImage作为渐变透明遮罩,否则用GGraph则是使用硬遮罩
            {
                isAlphaMask = false;
            }
            else
            {
                isAlphaMask = true;
            }
        }
    }
    
    private Texture2D tex;
    private Vector4 alphaRect;
    private static Dictionary<int,Material> dicMats = new Dictionary<int, Material>();
    public void UpdateAlphaMask()
    {
        if(mask == null || !isAlphaMask)
            return;
        GImage image = mask.gOwner as GImage;
        if(Application.isPlaying)
        {
            int w = (int)image.width;
            int h = (int)image.height;
            Texture sourceTex = image.displayObject.graphics.texture.nativeTexture;
            Rect rect = image.displayObject.graphics.texture.uvRect;
            int width = sourceTex.width;
            int height = sourceTex.height;
            Camera camera = StageCamera.main;
            
            rect.x *=width;
            rect.y =(1-rect.y-rect.height) *height; //FGUI与Unity的坐标系不一样需要转换
            rect.width *= width;
            rect.height *=height;
            
            //左上角
            Vector2 pos = image.LocalToGlobal(Vector2.zero);
            pos.y = Screen.height - pos.y;
            Vector3 p = camera.ScreenToWorldPoint(pos);

            //右下角
            pos = image.LocalToGlobal(new Vector2(w,h));
            pos.y = Screen.height - pos.y;
            Vector3 p1 = camera.ScreenToWorldPoint(pos);
            
            alphaRect = new Vector4(p.x,p.y,p1.x,p2.y);
        
            RenderTexture rt = RenderTexture.GetTemporary(width,height,0,RenderTextureFormat.Default,RenderTextureReadWrite.Linear);
            Graphics.Blit(sourceTex,rt);
            RenderTexture per = RenderTexture.active;
            RenderTexture.active = rt;
            tex = new Texture2D((int)image.texture.originalSize.x,(int)iamge.texture.originalSize.y);
            tex.ReadPixels(rect,0,0);
            tex.Apply();
            RenderTexture.active = per;
            RenderTexture.ReleaseTemporary(rt);
            image.touchable = false;
            image.alpha = 0;
            
            MeshRenderer[] meshrenderers = image.parent.displayObject.gameObject.GetComponentsInChildren<MeshRenderer>(true);
            Array.ForEach(meshrenderers, render =>
            {
                if(render!=null && render.shareMaterial !=null)
                {
                    DisplayObjectInfo doi = render.gameObject.GetComponent<DisplayObjectInfo>();
                    if(doi!=nul)
                    {
                        DisplayObject displayObject = doi.displayObject;
                        if(displayObject is Image || displayObject is TextField)
                        {
                            Material curMat;
                            string name = displayObject is Image? "Image":"Text";
                            int insID = displayObject.material.GetInstanceID();
                            if(!dicMats.TryGetValue(insID,out cutMat))
                            {
                                curMat = new Material(Shader.Find("FairyGUI/"+name));
                                disMats.Add(insID,curMat);
                            }
                            curMat.EnableKeyword("IsAlpha");
                            if(reversedMask)
                            curMat.EnableKeyword("ReverseMask");
                            else
                            curMat.DisableKeyword("ReverseMask");
                            curMat.SetTexture("_AlphaMask",tex);
                            curMat.SetVector("_AlphaRect",alphaRect);
                            if(displayObject.material != curMat)
                            {
                                displayObject.material = curMat;
                            }
                        }
                    }
                }
            }); 
        }
    }
    //...

    override public void Update(UpdateContext context)
    {
        //...
        if(_mask!=null)
        {
            context.EnterClipping(this.id,reverseMask);
            if(_mask.graphics!=null)
            {
                if(isAlphaMask)
                {
                    UpdateAlphaMask();
                }
                else
                {
                    _mask.graphics._PreUpdateMask(context,_mask.id);
                }
            }
        }
        //...

        if(_mask!=null)
        {
            if(_mask.graphics !=null)
            {
                if(isAlphaMask)
                {
                    UpdateAlphaMask();
                }
                else
                {
                    _mask.graphics._SetStencilEraserOrder(context.renderingOrder++);
                }
            }
        }
    }

    private void SetRenderingOrder(UpdateContext context)
    {
        if(_mask!=null)
        {
            if(_mask.graphics !=null)
            {
                if(isAlphaMask)
                {
                    UpdateAlphaMask();
                }
                else
                {
                    _mask.graphics._SetStencilEraserOrder(context.renderingOrder++);
                }
            }
        }
    }
}

粒子遮罩的实现(待完善)

  • 本文作者: Calmer
  • 本文链接: https://mytechplayer.com/archives/fgui渐变透明遮罩与例子特效遮罩的支持
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
# 笔记
ADDRESSABLE ASSETS SYSTEM 翻译(六)
游玩《原神》
  • 文章目录
  • 站点概览
Calmer

Calmer

88 日志
7 分类
10 标签
RSS
Creative Commons
0%
© 2020 — 2025 Calmer
由 Halo 强力驱动
蜀ICP备20010026号-1川公网安备51019002006543
Copyright © 2020-2025 Calmer的文章