前言
之前在项目中遇到的问题,需要渐变透明度遮罩与Unity的粒子特效遮罩,而FGUI中只是简单使用模板测试来完成的硬遮罩,显然不能完成更多有趣的效果。
遮罩的问题:遮罩的区域,渲染顺序,遮罩的目标
层级的问题:Unity中的UI层级不要与FGUI混用,如果使用UGUI那就建立相应的UI层级,如果用FGUI那就保证只有一个层级,混用容易导致层级问题出现。
模版测试(硬遮罩)
需要注意一下渲染顺序的问题,等最终所有渲染结束后,再进行一次模版测试即可。
渐变透明度的实现
- 透明度渐变遮罩图
- 遮罩区域的计算,反向
- 渲染顺序
- 实现与集成与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++);
}
}
}
}
}