NGUI sprite 变灰shader

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,如下图:

NGUI sprite 变灰shader_第1张图片


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被剪切的效果。

最终效果如下图:


NGUI sprite 变灰shader_第2张图片




参考:http://blog.csdn.net/lixiang9166/article/details/46851887

你可能感兴趣的:(C#)