感谢原作者的成果
本文转自:http://blog.csdn.net/konglingbin66/article/details/51880153
1.初衷
最近做一个装备的滚动条,需要将因为金钱不足不能购买的装备置灰,原来置灰是使用texture+shader,现在是使用sprite+shader,但是NGUI原有的sprite不具备添加shader的能力。为什么要用sprite,这样可以减少drawcall,提高一些效率。
2.置灰shader
shader就是在片段阶段时通过float grey = dot(col.rgb, float3(0.299, 0.587, 0.114)); 这句语句来实现灰化的,就是将顶点的像素与一个值进行点乘,来影响每个顶点的像素来呈现出整体灰化的效果。下面是参考网上大神给出的灰化shader代码:
Shader "Custom/MaskGray2" {
Properties
{
_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
}
SubShader
{
LOD 200
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass
{
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
Offset -1, -1
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
struct appdata_t
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f
{
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
v2f o;
v2f vert (appdata_t v)
{
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = v.texcoord;
o.color = v.color;
return o;
}
fixed4 frag (v2f i) : COLOR
{
fixed4 col;
col = tex2D(_MainTex, i.texcoord);
col.rgb = dot(col.rgb, fixed3(.222,.707,.071));
return col;
}
ENDCG
}
}
}
这个shader里面核心代码只有一句 col.rgb = dot(col.rgb, fixed3(.222,.707,.071));
3.给UISprite换材质
要给单独的UISprite更换材质,才不会影响通图集中的其他图片。
using UnityEngine;
using System.Collections;
using System;
public class UISprite_shader:UISprite
{
protected UIPanel panelObj = null;
protected Material GrayMaterial;
///
/// ngui对Sprite进行渲染时候调用
///
/// The material.
public override Material material
{
get
{
Material mat = base.material;
if (mat == null)
{
mat = (atlas != null) ? atlas.spriteMaterial : null;
}
if (GrayMaterial !=null)
{
return GrayMaterial;
}
else
{
return mat;
}
}
}
///
/// 调用此方法可将Sprite变灰
///
/// The material.
public void SetGray()
{
Material mat = new Material(Shader.Find("Custom/MaskGray2"));
mat.mainTexture = material.mainTexture;
GrayMaterial = mat;
RefreshPanel(gameObject);
}
///
/// 隐藏按钮,setActive能不用尽量少用,效率问题。
///
/// The material.
public void SetVisible(bool isVisible)
{
if (isVisible)
{
transform.localScale = new Vector3(1,1,1);
}
else
{
transform.localScale = new Vector3(0,0,0);
}
}
///
/// 将按钮置为禁止点击状态,false为禁用状态
///
/// The material.
public void SetEnabled(bool isEnabled)
{
if (isEnabled)
{
BoxCollider lisener = gameObject.GetComponent ();
if (lisener)
{
lisener.enabled = true;
}
SetNormal();
}
else
{
BoxCollider lisener = gameObject.GetComponent ();
if (lisener)
{
lisener.enabled = false;
}
SetGray();
}
}
///
/// 将GrayMaterial置为null,此时会调用默认材质,刷新panel才会重绘Sprite
///
/// The material.
public void SetNormal()
{
GrayMaterial = null;
RefreshPanel(gameObject);
}
///刷新panel,重绘Sprite
void RefreshPanel(GameObject go)
{
if (panelObj == null)
{
panelObj = NGUITools.FindInParents(go);
}
if (panelObj != null)
{
panelObj.enabled = false;
panelObj.enabled = true;
}
}
}
在SetGray()中,动态的创建了一个material并且赋值给Sprite,然后刷新panel时,在重绘这个Sprite时候,会调用material,这个时候,会返回我们创建好的GrayMaterial,在我们需要将Sprite恢复正常时候,仅需将我们的GrayMaterial置为null就行了。Shader "Custom/MaskGray2" 算是shader的目录吧,我个人理解啊,当给材质指定shader的时候,就是按照这个菜单来的。
我们实现功能的时候使用UISprite_shader,而不是原来的UISprite,如下图:
4.在ScrollView中添加clip shader
置灰之后的图片可以看到当灰色装备在ScrollView中,并且选中softClip选项时,灰色UISprite不会被剪裁,会超出边界。这是因为剪切也是shader实现的,但是该shader并不知道如何剪裁置灰的图片(我的个人理解啊)。NGUI的渲染都是通过UIDrawCall类进行的,通过断点发现(其实是别人发现的),当ScrollView进行遮罩时会在UIDrawCall的CreateMaterial()函数内进行动态换Shader操作:
if (panel != null && panel.clipping == Clipping.TextureMask)
{
mTextureClip = true;
shader = Shader.Find("Hidden/" + shaderName + textureClip);
}
else if (mClipCount != 0)
{
shader = Shader.Find("Hidden/" + shaderName + " " + mClipCount);
if (shader == null) shader = Shader.Find(shaderName + " " + mClipCount);
// Legacy functionality
if (shader == null && mClipCount == 1)
{
mLegacyShader = true;
shader = Shader.Find(shaderName + soft);
}
}
else shader = Shader.Find(shaderName);
通过上述代码可知,当为SoftClip状态下时,NGUI会自动给Sprite寻找名字为”Hidden/” + shaderName + ” ” + mClipCount的shader,显然,在Normal状态下,此时shader会变为NGUI自带的”Hidden/Unlit/Transparent Colored 1”,进入该shader后,我们会发现在frag()函数内有这么句代码col.a *= clamp( min(factor.x, factor.y), 0.0, 1.0);问题找到了,原来ScrollView是通过句代码来实现遮罩的,那么当我们将shader换成我们自己的shader时,首先在SoftClip下找不到一个叫”Hidden/MaskGray2 1”的shader进行替换,这时NGUI会强行换成默认状态的shader,因此,这就是导致我们上述问题的关键。
我的shader路径名是Shader "Custom/MaskGray2 1",匹配UIDrawcall中的CreateMaterial中的语句:
if (shader == null) shader = Shader.Find(shaderName + " " + mClipCount);
也可以是“Hidden/Custom/MaskGray2 1”,匹配上语句:
shader = Shader.Find("Hidden/" + shaderName + " " + mClipCount);
Shader "Custom/MaskGray2 1"
{
Properties
{
_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
_CutoffTex ("Cutoff (RGB), Alpha (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
AlphaTest Greater .01
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _CutoffTex;
float4 _ClipRange0 = float4(0.0, 0.0, 1.0, 1.0);
float2 _ClipArgs0 = float2(1000.0, 1000.0);
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 = v.vertex.xy * _ClipRange0.zw + _ClipRange0.xy;
return o;
}
half4 frag (v2f IN) : COLOR
{
// Softness factor
float2 factor = (float2(1.0, 1.0) - abs(IN.worldPos)) * _ClipArgs0;
// Sample the texture
half4 col = tex2D(_MainTex, IN.texcoord) * IN.color;
half4 alpha = tex2D(_CutoffTex, IN.texcoord);
col = fixed4(col.rgb,alpha.a * col.a);
col.rgb = dot(col.rgb, fixed3(.222,.707,.071));
col.a *= clamp( min(factor.x, factor.y), 0.0, 1.0);
return col;
}
ENDCG
}
}
SubShader
{
LOD 100
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass
{
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
ColorMask RGB
AlphaTest Greater .01
Blend SrcAlpha OneMinusSrcAlpha
ColorMaterial AmbientAndDiffuse
SetTexture [_MainTex]
{
Combine Texture * Primary
}
}
}
}
上面这个shader就是把Shader "HIDDEN/Unlit/Transparent Colored 1"拷贝过来,这个是scrollView选中softClip之后的默认shader,然后在这个shader的frag语句中发现一条col.a *= clamp( min(factor.x, factor.y), 0.0, 1.0);,然后我们添加一条置灰的语句col = fixed4(col.rgb,alpha.a * col.a);,即可实现置灰UISprite被剪切的效果。
最终效果如下图:
参考:http://blog.csdn.NET/lixiang9166/article/details/46851887