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的回复。

 
 
  Posted: 04:49 PM 03-05-2010
Hi, I would like to know if anyone know how to do Resident Evil 5 Health bar GUI style which doing in circle.

Is there a way to just use 2 pictures or maybe 4 at the most to do this style of health Bar UI.

I read about advanced health bar which is in traditional long rectangle, it only need 2 picture and scale the full health bar and leave the empty health bar under the full health, so it would animate as if the full health is reduced to 0. but mine is circle, and I need to know how to do the scaling or masking so it does the same thing as the rectangle.

Help is really appreciated Thanks
 Attached Images Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第1张图片    Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第2张图片 
 
 
 
  Eric5h5  :

 
  Yes, that's the way to do it. For example,  this  (web player). There are two textures; one for the regular graphics (using normal alpha for nice anti-aliasing) and one for the health bar (using alpha cutoff). The health bar's alpha looks like this:

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第3张图片

The non-alpha part of the texture is just a block of solid green.

The code is this:
 
  function Update () {
    renderer.material.SetFloat("_Cutoff", Mathf.InverseLerp(0, Screen.width, Input.mousePosition.x));
}
 
  This is much faster, easier, and less memory-using than having an array of circular textures. 

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第4张图片

--Eric
 
 
  http://blog.csdn.net/sparrowfc/article/details/8770421





=====================================================




游戏蛮牛Unity3D系列教程使用RenderTexture制作动态阴影(四十八)




我手头上有一个三星的Nexus7平板测试机,可是我用unity4制作阴影放在Nexus7上动态阴影死活不显示,可是我在同样的机器Nexus7装上TempRun动态阴影就显示的非常清楚。接着我查了一下发现Unity4只是在大部分移动平台手机支持动态阴影,Nexus7 属于Tegra 3 并不支持动态阴影。TempRun是怎么实现的呢?   后来,我尝试使用RenderTexture来制作动态阴影。做是做出来了,不过要做的很精细那么有点麻烦。文章的最后我会把我的想法说出来。如下图所示,这个阴影我就是用RenderTexture来制作的。  

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第5张图片 


下面开始学习,在Project视图中,选择Creat->RenderTexture ,接着在Hierarchy视图中在创建一个摄像机。(用来渲染RenderTexures)
Background :保持背景为透明,颜色值都改成 0 ,表示完全透明(截取只能按矩形所以背景需要做成透明了 不然就穿帮了)。
Culling Mshk :摄像机照射的层,这个摄像机会一直跟随主角移动,始终保持侧面观察主角。取得主角侧面每一帧的图像,并且渲染在地面上。从侧面观察难免会取到主角背景的图像,所以在这里我们把主角放在TransparentFX这个layer上,摄像机也只去这个Layer上截取模型。(当然你也可以重新创建一个Layer)
Target Texture :就是把这个摄像机每一帧所看到的图像复制给刚刚创建的RenderTexutre(刚刚创建的RenderTexure拖拽到这里即可)

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第6张图片 

然后在创建一个平面,记住一定要给它一个透明的材质球。这里我选了Transparent/Diffuse。然后是颜色,选择一个黑色偏灰色的颜色就可以。因为阴影是灰黑色的嘛。

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第7张图片 

然后在创建一条简单的脚本,把它绑在影子所在的平面上。像这样给他赋值。

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第8张图片 




using UnityEngine;
using System.Collections;
 
public class NewBehaviourScript : MonoBehaviour {
 
        public RenderTexture t;        
 
        void Update () {
                 renderer.material.mainTexture = t;
        }
 
        void OnGUI()
        {
                GUI.DrawTexture(new Rect(0,0,100,100),t);
        }
}



代码比较简单我也就不做解释了。。运行后效果,小牛头人一直在播放奔跑动画,它的影子动态的投影在身后的墙上了。

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第9张图片 


最后我在文章开始说过美中不足的地方
1.需要根据光照的旋转角度来动态计算阴影面的角度。如果光比较多那么计算量是非常庞大的,而且还有影子的根据光线的拉伸效果。
2.影子投射在地面时,如果地面凹凸不平,所以影子不能绘制在一个Plane上。而需要动态的计算影子的网格,目前我能想到的办法就是通过N条射线,从主角身后(根据主角的坐标与旋转角度来计算周围一定区域每一个点的坐标)从上方向垂直的地面发射射线,得到地面凹凸的每个点的坐标,在根据这些坐标动态的生成一个凹凸不平的网面。(如果你用的是unity3d地形那么有专门的方法得到地形的高度,如果地形使用美术建的模型来做的那么只能用这个方法喽)最后把上面做出来的RenderTexure渲在这上面。
补充。其实在StandardAssets中就有一个用投影做的简单阴影的包。在Project视图中  importPackage -> Projectors 。如下图所示,把Blob Shadows Projector 拖进Hierarchy视图中。简单的调节一下就可以了。
还有一个叫fastshadows的一个阴影插件。前几天我简单看了一下,如果是自身不会改变的话用它就挺好, 只占一个drawcall。

Unity3D 使用 RenderTexture 做 UI 特效、动态阴影_第10张图片 
最后是本文的下载地址:http://vdisk.weibo.com/s/sCouU





http://www.unitymanual.com/thread-5774-1-1.html

你可能感兴趣的:(Unity3d)