感謝原作者的成果
本文轉自: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;
/// <summary>
/// ngui對Sprite進行渲染時候調用
/// </summary>
/// <value>The material.</value>
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;
}
}
}
/// <summary>
/// 調用此方法可將Sprite變灰
/// </summary>
/// <value>The material.</value>
public void SetGray()
{
Material mat = new Material(Shader.Find("Custom/MaskGray2"));
mat.mainTexture = material.mainTexture;
GrayMaterial = mat;
RefreshPanel(gameObject);
}
/// <summary>
/// 隱藏按鈕,setActive能不用盡量少用,效率問題。
/// </summary>
/// <value>The material.</value>
public void SetVisible(bool isVisible)
{
if (isVisible)
{
transform.localScale = new Vector3(1,1,1);
}
else
{
transform.localScale = new Vector3(0,0,0);
}
}
/// <summary>
/// 將按鈕置為禁止點擊狀態,false為禁用狀態
/// </summary>
/// <value>The material.</value>
public void SetEnabled(bool isEnabled)
{
if (isEnabled)
{
BoxCollider lisener = gameObject.GetComponent<BoxCollider> ();
if (lisener)
{
lisener.enabled = true;
}
SetNormal();
}
else
{
BoxCollider lisener = gameObject.GetComponent<BoxCollider> ();
if (lisener)
{
lisener.enabled = false;
}
SetGray();
}
}
/// <summary>
/// 將GrayMaterial置為null,此時會調用默認材質,刷新panel才會重繪Sprite
/// </summary>
/// <value>The material.</value>
public void SetNormal()
{
GrayMaterial = null;
RefreshPanel(gameObject);
}
///刷新panel,重繪Sprite
void RefreshPanel(GameObject go)
{
if (panelObj == null)
{
panelObj = NGUITools.FindInParents<UIPanel>(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