您的位置:首页 > 产品设计 > UI/UE

NGUI学习笔记(七):UIPanel剪裁粒子效果

2017-07-30 15:13 2081 查看
一.简介

关于这部分的学习说起来真的有点绕,个人感觉这个属于进阶级别的开发问题了,但又属于不得不会的内容,于是硬着头皮搞下去!

二.UISprite的材质

长期以来,一直搞不懂怎样设置UISprite的材质,UGUI的Sprite材质很easy,在Image组件上可以轻松找到:



NGUI的在哪呢?



找到使用的图集:



终于惊喜的发现,这个图集上有Material,其实这个应该是我一直以来没有注意的地方。



检视一下Shader资源:



为什么会有这么多相似的Shader呢?

三.UIDrawCall

观察下来,这个脚本控制着所有NGUI渲染相关的Shader替换。

当物体将要被渲染时,NGUI首先会更新一次UI所有使用的材质:



更新材质的时候需要创建材质,新材质的Shader会根据剪裁次数(ClipCount)来选择不同版本的Shader,比如剪裁1次则使用 “Hidden/Unlit/Transparent Colored 1”



四.剪裁区域参数的设置

UIPanel的剪裁设置有4种,这里研究的是最常用的SoftClip



当选择了Soft Clip后可以设置剪裁区域的大小、中心、边缘的柔和度。

这些参数的效果会在UIDraw中的被调用,每次OnWillRenderObject函数都会调用SetClipping函数设置剪裁区域:



SetClipping函数将计算好的参数设置到实际用到的Shader中。

看一下剪裁1次所使用Shader的顶点与片元着色器的实现:



在顶点着色器的最后一步

o.worldPos = v.vertex.xy * _ClipRange0.zw + _ClipRange0.xy;


这个是把被剪裁的UI Mesh的模型空间的顶点先按照剪裁区域的长宽进行缩放,然后按照剪裁区域的中心点进行平移,将模型顶点转换到Panel的剪裁区域的局部空间下,并将其“归一化”。

在片元着色器的第一步

float2 factor = (float2(1.0, 1.0) - abs(IN.worldPos)) * _ClipArgs0;


这个将用来判断该顶点的x、y分量是否处于剪裁区域[-1,1]内。

后面正式用到

col.a *= clamp( min(factor.x, factor.y), 0.0, 1.0);


如果factor的任意x、y分量小于0(即不在剪裁区域内),那么输出颜色的透明度alpha将会为0,从而达到剪裁的效果。

五.NGUI DrawCall的基本统计

将UIDraw最上面的宏定义打开:



保存后,在编辑器界面稍等片刻,会发现这些:



原来,NGUI会将所有能够一同提交渲染的UIMesh合并成了一个网格,而每个合并的网格就对应着1个NGUI DrawCall。

同时合并后的网格的缩放大小与UIPanel相同,所以UIPanel下的UI Mesh的模型顶点和Panel实际上处在同一坐标系下。

【NGUI内置shader 的计算方式也验证了这点】

滑动列表合并后的Mesh:



【注意右下角】

六.正式剪裁粒子

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
[RequireComponent(typeof(ParticleSystemRenderer))]
public class UIParticle : MonoBehaviour {

private UIPanel panel;
private ParticleSystemRenderer pRenderer;
private Material dyMaterial;
private UIWidget coverWidget;

// Use this for initialization
void Start () {

//找到这个粒子系统最近的父节点的UIPanel
panel = GetComponentInParent<UIPanel> ();
//找到这个粒子系统的Renderer
pRenderer = GetComponent<ParticleSystemRenderer> ();

dyMaterial = new Material (Shader.Find ("Hidden/Unlit/Transparent Colored 1"))
{
//提升其渲染队列至4000
renderQueue = 4000,
//使用原始material的texture
mainTexture = pRenderer.sharedMaterial.mainTexture
};

pRenderer.material = dyMaterial;

/* 为什么不采用下面这种做法?
* pRenderer.material.renderQueue = 4000;
* 这样的话,Unity会帮助我们自动创建动态材质
* 如此,该动态引用无法得到,很难判断
* Destroy时也不好操作
*/
}

void OnWillRenderObject()
{
if (panel.hasClipping)
{
//裁剪区域
Vector4 cr = panel.drawCallClipRange;
//裁剪边儿的柔和度
Vector2 soft = panel.clipSoftness;

Vector2 sharpenss = new Vector2 (1000.0f, 1000.0f);

if (soft.x > 0f)
sharpenss.x = cr.z / soft.x;
if (soft.y > 0f)
sharpenss.y = cr.w / soft.y;

//经过测试粒子系统产生的Mesh是不受UIPanel缩放比影响的
//所以要将其缩放比记录下来
float scale = panel.transform.lossyScale.x;
//粒子系统的顶点坐标系相对于panel会有一定的偏移,所以要将其position记录下来
Vector3 position = panel.transform.position;

Debug.Assert (dyMaterial != null,
aa65
"dyMaterial 创建失败!!!!!!");

//坐标变化的顺序:缩放、旋转、平移,这里不考虑粒子系统的旋转
dyMaterial.SetVector
(
Shader.PropertyToID ("_ClipRange0"),
new Vector4 (
-cr.x / cr.z - position.x/scale / cr.z,
-cr.y / cr.w - position.y/scale / cr.w,
1f / cr.z / scale,
1f / cr.w / scale
)
);

dyMaterial.SetVector(Shader.PropertyToID("_ClipArgs0"),new Vector4(sharpenss.x,sharpenss.y,0,1));
}
}

void OnDestroy()
{
DestroyImmediate (dyMaterial);
dyMaterial = null;
}
}


七.应用





八.参考链接

http://blog.csdn.net/tkokof1/article/details/52089289

九.完整脚本

http://pan.baidu.com/s/1kU8o9vx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ngui