【Unity&Shader】NGUI实现图集变灰功能

57 篇文章 2 订阅
56 篇文章 0 订阅

原文地址:http://bbs.9ria.com/thread-434334-1-1.html

需求分析

  • 直接调整Sprite或者Texture的Color值达不到美术所要的效果
  • 节省资源,为了实现去色效果,美术不必提供另外一张PS处理过后的图集,只需要程序修改shader动态处理即可怎么做
  • 修改NGUI使用的Shaders(新添加四个,为什么后文讲解)
  • 制作XX_Gary图集
  • 编写统一逻辑接口针对Sprite切换图集,针对UITexture切换Shader

修改NGUI使用的Shader

注意下NGUI使用的默认shader,该例子使用的是NGUI3.6.6,NGUI默认使用的一系列shader为下图 1 2 3 结尾,稍微提前一点的版本shader下图(SoftClip)(AlphaClip)复制出来四个按照原来的规则修改名字,注意一定要前缀一致,因为NGUI内部实现裁剪是需要通过修改后缀找到相应的shader进行替换实现的,然后在修改四个shader的内容,找到每个Shader内容的frag函数,在最后都加上同一段代码即可(前面各种计算得到col,再返回前将最终的col结果在进行一次处理)

更改四个shader

较旧版本NGUI

[C#] 纯文本查看 复制代码
?
1
2
3
4
5
6
7
8
<font color= "#000000" >half4 frag (v2f IN) : COLOR
{
         // ......
         //For gray shader change rgb value
         float grey = dot(col.rgb, float3(0.299, 0.587, 0.114));
         col.rgb = float3(grey, grey, grey);
         return col;
}</font>
制作XX_Gray图集

游戏中针对需要置灰的元素图集需要重新制作一个额外的Gray图集,Normal图集和Gray图集使用同一个2DTexture,使用不同的材质

Gray图集
2015-7-15 19:05:44 上传
下载附件3.21 KB)
Gray图集

编写处理逻辑

统一的处理接口,基本原理是针对UISprite更换图集,针对UITexture更换Shader即可
具体的一种实现方式如下

<font color="#000000">using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GrayUIController : MonoBehaviour
{
    // UISprite : 更换图集(正常和置灰图集切换)
    // UITexture:更换Shader为正常Shader和置灰使用的Shader
    /
      1.将同一个图集中的图片单独的变灰操作
      2.将UITexture变灰去色
     /
    // 维护一个图集List
    // 置灰图集
    // 正常图集
    public List grayAtlas;
    public List normalAtlas;
    public Hashtable shaderTable = new Hashtable();
    public enum AtlasName
    {
        EM_AtlasName_None = -1,
        EM_AtlasName_CommonAtlas,
    }
    private static GrayUIController instance = null;
    public static GrayUIController GetInstance()
    {
        return instance;
    }
    void Awake()
    {
        instance = this;
    }
    ///
    /// 将制定GameObject存在的UITexture和UISprite去色,变灰
    ///
    ///  目标物体
    ///  是否变灰
    ///  制定图集
    public void ChangeObjectsToGray(GameObject targetObj, bool gray, AtlasName atlasName = AtlasName.EM_AtlasName_None)
    {
        UISprite[] sprites = targetObj.GetComponentsInChildren();
        UITexture[] textures = targetObj.GetComponentsInChildren();
        for (int i = 0; i < sprites.Length; i++)
            ChangeUISpriteToGray(sprites, gray, atlasName);
        for (int i = 0; i < textures.Length; i++)
            ChangeUITextureToGary(textures, gray);
    }
    public bool ChangeUISpriteToGray(UISprite targetSprite, bool toGray, AtlasName atlasName = AtlasName.EM_AtlasName_None)
    {
        bool toGarySucess = false;
        if (targetSprite != null)
        {
            List findAtlas = new List();
            if (toGray)
                findAtlas = grayAtlas;
            else
                findAtlas = normalAtlas;
            UISpriteData findData = null;
            string spriteName = targetSprite.spriteName;
            // 如果指定图集,不用查找直接更换图集即可(高效方式)
            if (atlasName != AtlasName.EM_AtlasName_None)
            {
                findData = findAtlas[(int)atlasName].GetSprite(spriteName);
                if (findData != null)
                {
                    targetSprite.atlas = findAtlas[(int)atlasName];
                    toGarySucess = true;
                }
            }
            else
            {
                // 未指定图集,需要从现有的图集中查找对应的图集
                for (int i = 0; i < findAtlas.Count; i++)
                {
                    findData = findAtlas.GetSprite(spriteName);
                    if (findData != null)
                    {
                        targetSprite.atlas = findAtlas;
                        toGarySucess = true;
                        break;
                    }
                }
            }
        }
        return toGarySucess;
    }
    ///
    /// 将UITexture置灰操作
    ///
    ///
    ///
    public void ChangeUITextureToGary(UITexture texture, bool toGray)
    {
        Shader newShader = null;
        if (toGray)
        {
            // 保存UITexture原Shader
            if (!shaderTable.ContainsKey(texture))
                shaderTable[texture] = texture.shader;
            newShader = Shader.Find("Unlit/GrayShader");
        }
        else
        {
            // 恢复原Shader
            if (shaderTable.ContainsKey(texture))
                newShader = (Shader)shaderTable[texture];
            else
                newShader = Shader.Find("Unlit/Transparent Colored");
        }
        texture.shader = newShader;
    }
    private bool changeToGray = true;
    public GameObject targets;
    public void TestChangeGray()
    {
        ChangeObjectsToGray(targets, changeToGray, AtlasName.EM_AtlasName_CommonAtlas);
        changeToGray = !changeToGray;
    }
}</font>



问题

为何要复制出来四个shader,如果只复制出来一个基本shader而没有复制另外三个处理裁剪的shader,当你在ScrollView等还有裁剪的Panel中存在变灰元素的时候就会提示"找不到xxshader"的错误,具体随着错误就知道在UIDrawCall中进行了裁剪过程的Shader替换(UIDrawCall中的CreateMaterial函数)


<font color= "#000000" > void CreateMaterial ()
{
     // .....
     if (shaderName.StartsWith( "Hidden/" ))
             shaderName = shaderName.Substring(7);
     // Legacy functionality
     const string soft = " (SoftClip)" ;
     shaderName = shaderName.Replace(soft, "" );
     if (mClipCount != 0)
     {
         shader = Shader.Find( "Hidden/" + shaderName + " " + mClipCount);
         if (shader == null ) Shader.Find(shaderName + " " + mClipCount);
         // Legacy functionality
         if (shader == null && mClipCount == 1)
         {
             mLegacyShader = true ;
             shader = Shader.Find(shaderName + soft);
         }
     }
     else shader = Shader.Find(shaderName);
     // ......
}</font>



如果创建了一个新的图集,会不会造成内存存在两张贴图,答案是不会,因为两个图集使用的同一张贴图,只不过材质球不一样而已,你可以在运行的时候打开Profiler查看图集引用的贴图,然后比对下内存使用情况即可


效果演示演示

2015-7-15 19:05:44 上传
下载附件(324.86 KB)
演示

Demo下载

(导入的DEMO,的NGUI文件夹可能有问题,请删除,重新导入正确的NGUI)

Unity3D学习笔记——NGUI之UIScrollView











  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值