最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
ngui panel遮罩
在之前的开发过程中,我使用NGUI开发了一个圆形小地图。小地图上除了地图背景,还有大量零碎的角色提示信息。为了进行绘制,我创建了一个Panel。接下来的问题是,如何让这个Panel只在圆形区域内显示。
NGUI本身为Panel提供了alpha clip和soft clip两种剪裁模式。因此,很自然地就会想到为其添加一种mask clip模式,即为该面板提供一张遮罩贴图,从而实现更灵活的自定义切割。
NGUI的渲染系统及其Panel实现Clip的方式
NGUI采用3D渲染方式来绘制2D/3D UI。每个UI元素都是一个简单的面片,通过一套坐标系统,将2D坐标系中设置的值转换为3D世界坐标系中的一系列顶点,设置好正确的UV及材质后,通过摄像机渲染到屏幕上。
其中,UIPanel是NGUI的绘制管理单元,负责管理其所有子UI元素的顶点生成、三角形索引列表、更新、深度排序等操作。可以将其类比为MeshFilter,它构建并维护着一个Mesh,这个Mesh包含多个submesh,每个submesh使用不同材质渲染,且包含一个或多个面片。
UIPanel还管理着一个UIDrawcall列表,UIDrawcall是NGUI的基本绘制单元,类似于MeshRender,只不过一个UIDrawcall只对一个submesh进行绘制。
3D渲染除了设置顶点索引和UV缓冲区外,还有一个重要元素是材质,它包括贴图等一系列参数的设置。在NGUI中,渲染使用的材质由UIDrawcall根据其绘制的元素动态生成。以下是UIDrawcall中的一段代码,展示了其创建材质的方式:
void CreateMaterial ()
{
const string alpha = " (AlphaClip)";
const string soft = " (SoftClip)";
const string mask = " (MaskClip)";
string shaderName = (mShader != null) ? mShader.name :
((mMaterial != null) ? mMaterial.shader.name : "Unlit/Transparent Colored");
// 确定普通Shader的名称
shaderName = shaderName.Replace("GUI/Text Shader", "Unlit/Text");
shaderName = shaderName.Replace(alpha, "");
shaderName = shaderName.Replace(soft, "");
shaderName = shaderName.Replace(mask, "");
// 尝试查找新的Shader
Shader shader;
if (mClipping == Clipping.SoftClip)
{
shader = Shader.Find(shaderName + soft);
}
else if (mClipping == Clipping.AlphaClip)
{
shader = Shader.Find(shaderName + alpha);
}
// 其他逻辑...
}
这表明UIDrawcall会根据当前绘制元素所使用的材质或Shader以及当前的剪裁类型,使用由对应Shader自动创建的材质。同时,UIDrawcall中缓存了材质的其他参数,用于为新材质赋值。具体的操作逻辑可以查看源码。
下面简述一下NGUI实现剪裁的过程:首先,改变剪裁状态会触发UIPanel的重绘。在UIPanel的重绘过程中,会更新每个UIDrawcall的绘制内容及设置,包括剪裁(UIPanel.UpdateUIDrawcall)。当UIDrawcall的剪裁发生改变时,会触发其材质的更新(UIDrawcall.UpdateMaterials)。UIDrawcall使用更新后的材质对UI元素进行绘制,而最终的剪裁操作完全由Shader完成。
要实现图片遮罩Panel,只需以下3个步骤:
- 为每个NGUI基础Shader编写一个实现纹理遮罩功能的扩展Shader。
- 将该Shader的设置集成到NGUI工具中。
- 将该材质的遮罩图片的设置集成到NGUI工具中。
编写图片遮罩Shader
Shader的编写其实并不复杂,参考一下内置的Shader即可。NGUI的内置Shader如下:
初次看到这些Shader时,我感到眼花缭乱。现在看过UIDrawcall的代码后,我明白了NGUI渲染使用的Shader主要有Dynamic Font、Text、Premultiplied Colored、Transparent Colored这几种,其他都是在其基础上加入剪裁功能的。
我们需要基于这四种基础Shader编写添加遮罩剪裁功能的新Shader。如果觉得麻烦,只编写Transparent Colored的遮罩Shader即可(因为小地图中只有纹理和精灵,没有文字)。另外,如果以后想在UI中使用一些特效Shader,别忘了为这些Shader添加剪裁版本,以确保UI元素能正确被剪裁。
具体编写方法如下:首先,在xxxx/Resources/Shaders/目录下创建一个Shader,名称可以随意取。为了与NGUI统一,我将其命名为Unlit - Transparent Colored (MaskClip)。实际上,文件名称是否统一并不重要,关键是Shader代码中给这个Shader起的真正名称要符合规范。
可以直接将SoftClip的Shader复制到新的Shader中,将后缀改为自定义名称。接下来,为这个Shader添加一个遮罩的纹理属性,并在片段着色器中基于这个纹理对最终像素颜色或Alpha值进行过滤。这里比较重要的是遮罩纹理的UV坐标,由于说明起来比较复杂,后续有时间再详细介绍。
以下是遮罩Shader的代码示例:
Shader "Unlit/Transparent Colored (MaskClip)"
{
Properties
{
_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
_PanelMaskTex ("Mask (A)", 2D) = "white" {}
}
SubShader
{
LOD 200
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass
{
Cull Off
Lighting Off
ZWrite Off
Offset -1, -1
Fog { Mode Off }
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _PanelMaskTex;
float4 _MainTex_ST;
struct appdata_t
{
float4 vertex : POSITION;
half4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : POSITION;
half4 color : COLOR;
float2 texcoord : TEXCOORD0;
float2 worldPos : TEXCOORD1;
};
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.color = v.color;
o.texcoord = v.texcoord;
o.worldPos = TRANSFORM_TEX(v.vertex.xy, _MainTex);
return o;
}
half4 frag (v2f IN) : COLOR
{
// 计算面板坐标系中遮罩纹理的UV
float2 panelUV = (1 - IN.worldPos) * 0.5f;
// 采样纹理
half4 col = tex2D(_MainTex, IN.texcoord) * IN.color;
col.a *= tex2D(_PanelMaskTex, panelUV).a;
return col;
}
ENDCG
}
}
}
集成到NGUI工具中
关键的一步是添加UIDrawCall.Clipping枚举项,并修改上文给出的UIDrawcall.CreateMaterial()代码,将新的Shader后缀与新的剪裁枚举结合起来。另外,为UIDrawcall和UIPanel添加一个Texture2D成员变量,用于存储遮罩纹理的设置。添加方式和调用位置可以完全参照UIPanel.mClipSoftness以及UIDrawcall.mTexture。
此外,还需要修改相应的UIPanelInspector,以便在Panel的Inspector中设置遮罩纹理。由于这些修改比较零碎,这里只展示Inspector的修改内容。