Unity3D使用RenderTexture做UI特效

       这阵子做项目需要实现一个技能冷却的UI组件,这个UI组件相信玩儿过游戏的人都不会陌生,基本上就是一个半透的遮罩盖在技能图标上,随着冷却时间的进行逐渐旋转消失。

       第一时间想到最简单的方法就是每帧对遮盖的UI贴图的alpha值进行一遍设置,这种方法不好的地方就是对CUP的压力比较大,由于项目在移动端上目前主要的负载还是在CPU上(ios的CUP负载相对Android机型感觉压力要大很多),这种方案并没有过多考虑。当然需要说明的是如果是渐进式的更新贴图部分像素的alpha值,计算量其实也不会太大,不过还是觉得这种方法不够漂亮,Pass。

       不想用CUP,自然就要利用GPU来设置Alpha值,于是就想到了使用RenderTexture来实现这个UI控件。简单说来,在绘制GUI的脚本中创建一个RenderTexture,并将其作为一个新的摄像机的RenderTarget,在这个摄像机上通过脚本绘制出全屏的冷却遮罩,这样这个RT就可以当做技能冷却的UI组件来使用了。

       现在的问题就是如何画一个全屏的冷却遮罩,你可以创建一个面片大小正好占满摄像机视锥截面,然后制作一个材质球,编写一段Shader代码来将遮罩贴图部分地渲染到这个面片上。而事实上创建面片这一步完全可以省去,因为基本上所有的后置特效PostEffect都需要创建这样一个面片,这些PostEffect大多就是做一些图像处理如改变色调、模糊、扰动等等。因此Unity3D提供了Graphic.Blit(RenderTexture source, RenderTexturedestination, Material mat)这样一个十分方便的方法,省去了你创建面片这一步,将source这张图作为mat的—_MainTex渲染到destination这张图上。


       那么首先来看附在摄像机上的渲染控制脚本如何来写,利用在Image Effect包中提供了ImageEffectBase基类,这个脚本其实十分简单,我用的是C#:

public class CDMask: ImageEffect
{
    public Texture2D cdMaskTex;
    public float cdTime;
    public float CdTime
    {
        set {material.SetFloat(“_CDPercentage”, value/cdTime); }
    }

    void Awake()
    {
        material.SetTexture(“_MaskTex”,cdMaskTex);
    }

    void OnRenderImage(RenderTexture source, RenderTexturedestination)
    {
        Graphics.Blit(source,destination, material);
    }
}

       ImageEffect这个基类有一个Shader公共变量,和一个material保护变量在调用时创建,同时还负责检查该Shader是否被渲染设备所支持。

       OnRenderImage是一个消息方法,任何与Camera组件附在同一物体下的脚本都会在摄像机将场景渲染至屏幕图像缓存后调用该方法,其中两个传入参数source是缓存中的图像,destination是最终要输出至RenderTarget的图像,而在这个方法中调用Graphics.Blit显然是再合适不过了。


       剩下的就是写一段shader了,没有什么特殊的难度,只是把图像alpha值的设定放在了fragment shader中,对ImageEffect包中的Overlay那个Shader稍加修改即可。

在写这段shader时有几点需要注意:

1. 不要使用surface shader。Unity3D创建的shader文件默认都使用surface shader进行绘制。它的这套surface shader的确是方便,不过要是用在Blit方法中会死的很惨。电脑上的Editor里可能效果没问题,上了真机就完全没东西了。具体原因官方论坛里有人有解释,主要是跟GL2.0的支持和surface shader使用的UnityCG.cginc里的一个函数平台兼容性有问题有关。总之老老实实用CG把vertex和fragment shader都写了就好。其中vertex shader可以直接从ImageEffect包里的Overlay的Shader中直接拷过来就成。

2. SubShader中在对固定管线参数设置时记得写上ColorMask RGBA。要是就写了个RGB,等着惨死吧。

 

        最后要说的是,其实画这个旋转的遮罩有一种更快更巧的方法,是官方论坛上有人提供的,利用了AlphaTest这一功能,配合遮罩图片的Alpha通道节省了好多计算量。只是实际使用时渲染效果并不理想,边缘很不整齐,可能是手机平台对图片的压缩所导致的,不过好像所有AlphaCutout这类shader渲染出来的都有这个问题,后面的回复中也有好多人提到,最终没有采用,但这仍是一个非常好的方案。链接给出我也就不啰嗦了。

http://forum.unity3d.com/threads/42813-Health-Bar-UI

Eric5h5的回复。


你可能感兴趣的:(Unity3D)