(转)NGUI sprite 变灰shader

感谢原作者的成果
本文转自: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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容