Unity&NGUI渲染层级

尊重原创,转载请在文首注明出处:http://blog.csdn.net/cai612781/article/details/78477944

项目中经常遇到层级的显示问题,来总结下。

一,Unity

按优先级从高到低排列:

1,Camera.depth

depth小的先渲染。


2,Renderer.sortingLayerID

sortingLayerID基类Renderer的属性,sortingLayerID小的先渲染。

3,Renderer.sortingOrder

sortingOrder同为基类Renderer的属性,sortingOrder小的先渲染。


sortingLayerIDsortingOrder虽然是基类Renderer的属性,但只适用于Unity2d的排序,因此我们在面板中可以看到只有显示图片的SpriteRenderer和显示粒子的

ParticleSystemRenderer有这两个属性。



4,Material.renderQueue

该属性对应的是Shader的renderQueue,renderQueue的范围必须是[0,5000],数值小的先渲染。Shader中使用SubShader的Queue标签来设定模型属于哪个渲染队列。

Unity内置了几个渲染队列的枚举:

public enum RenderQueue
{
        Background = 1000,//最先渲染,通常用来绘制背景
        Geometry = 2000,//不透明物体的渲染
        AlphaTest = 2450, // 需要透明度测试的物体,在不透明物体渲染后再渲染会更高效
        GeometryLast = 2500, // 分界线,[0,2500]为不透明物体,[2501,5000]为半透明物体
        Transparent = 3000,//半透明物体的渲染,任何使用了透明度混合(例如关闭了深度写入)的物体都用得用该队列
        Overlay = 4000,//用于叠加效果
}


上述排序是在Shader关闭了深度写入(ZWrite Off)的情况下,当开启了深度写入时,物体的空间位置(transform.position.z)会影响层级,z值小的在前面。

另外,Unity菜单Edit->Project Settings->Tags中的Layers,只是一个逻辑的分层,用于Camera的Culling Mask来设置相机渲染哪个Layer的物体,辅助Camera.depth来管理层级。


二,NGUI

按优先级从高到低排列:

1,UIPanel.sortingOrder

通过源码可以看到UIPanel.sortingOrder更改的就是上文中Renderer.sortingOrder。

2,UIPanel.depth

depth小的先渲染

3,UIWidget.depth

depth小的先渲染,NGUI根据UIWidget.depth的排序合并drawcall(因此尽量将相同材质的图集在depth上排在一起可以减少UI上的drawcall)。


UIPanel面板还有个Render Q属性,用来设置该面板的renderQueue的起始值,所有面板总的起始值默认为3000。该属性有三种选项,分别是

Automatic,StartAt,Explicit,后两者可自定义renderQueue的起始值。



当Render Q默认为Automatic时,根据UIPanel.depth的排序对drawcall的材质的renderqueue进行排序。

当Render Q默认为StartAt或Explicit时,UIPanel.depth则不起作用,根据自定义的值对drawcall的材质的renderqueue进行排序。


NGUI所用的shader都是关闭了深度写入的,因此z值不影响层级。所以NGUI本质还是通过Unity的sortingOrder和renderQueue来管理层级。


三,NGUI与模型或特效的夹层问题

我们在处理模型或特效显示在两个UI之间的层级显示问题,有两种方式:

1,利用Camera.depth属性,将界面A,模型或特效,界面B分别放在三个Layer,用三个depth递增的相机来渲染。(该方式不推荐,因为一个界面上可能会打开新的界面,以及两个界面的相对层次不是固定的,这样会导致相机不断增加,层次管理混乱)

2,利用renderQueue属性,给两个UIPanel之间留下一定范围的renderQueue数值,将模型或特效的renderQueue设置在这个范围内。(推荐该方式)


代码如下:

第一步,新增renderQueue控制脚本:RenderQueueContro.cs

using System.Collections.Generic;
using UnityEngine;

public class RenderQueueControl : MonoBehaviour
{
	private UIPanel _panel;
	
	private int _rq;
	private List<Material> _materialList = new List<Material>();
	private int i, len;
	
	void Start()
	{
	}
	
	public void Init()
	{
		_panel = NGUITools.FindInParents<UIPanel>(gameObject);
		UIPanel.AddModelRQ(this);
		InitMaterials();
	}
	
	private void InitMaterials()
	{
		_materialList.Clear();
		Renderer[] ren = GetComponentsInChildren<Renderer>();
		if(ren != null)
		{
			for(i = 0, len = ren.Length; i < len; i++)
			{
				_materialList.AddRange(ren[i].materials);
			}
		}
	}
	
	public void UpdateRQ()
	{
		if(_panel != null && _panel.drawCalls.Count > 0)
		{
			_rq = _panel.drawCalls[_panel.drawCalls.Count - 1].renderQueue + 1;
			for(i = 0, len = _materialList; i < len; i++)
			{
				if(_materialList[i].renderQueue != _rq)
				{
					_materialList[i].renderQueue = _rq;
				}
			}
		}
	}
	
	void OnDestroy()
	{
		_materialList.Clear();
		UIPanel.RemoveRQ(this);
	}
}


第二步,给两个UIPanel之间留下一定范围的renderQueue数值,并在UIPanel更新时修改模型的renderQueue。我们修改UIPanel.LateUpdate(注释为修改处)。

void LateUpdate()
{
	if(mUpdateFrame != Time.frameCount)
	{
		mUpdateFrame = Time.frameCount;
		
		for(int i = 0, imax = list.Count; i < imax; ++i)
		{
			list[i].UpdateSelf();
		}
		
		int rq = 3000;
		for(int i = 0, imax = list.Count; i < imax; ++i)
		{
			UIPanel p = list[i];
			if(p.renderQueue == RenderQueue.Automatic)
			{
				p.startingRenderQueue = rq;
				p.UpdateDrawCalls();
				rq += p.drawCalls.Count * 2;//加上*2
			}
			else if(p.renderQueue == RenderQueue.StartAt)
			{
				p.UpdateDrawCalls();
				if(p.drawCalls.Count != 0)
				{
					rq = Mathf.Max(rq, p.startingRenderQueue + p.drawCalls.Count * 2);//加上*2

				}
			}
			else
			{
				p.UpdateDrawCalls();
				if(p.drawCalls.Count != 0)
				{
					rq = Mathf.Max(rq, p.startingRenderQueue + 1 * 2);//加上*2
				}
			}
		}
		UpdateModelRQ();//加上刷新rq
	}
}

private static List<RenderQueueControl> _modelRQList = new List<RenderQueueControl>();

public static void AddModelRQ(RenderQueueControl control)
{
	if(!_modelRQList.Contains(control))
	{
		_modelRQList.Add(control);
	}
}

public static void RemoveModelRQ(RenderQueueControl control)
{
	_modelRQList.Remove(control);
}

private void UpdateModelRQ()
{
	for(int i = 0, len = _modelRQList.Count; i < len; i++)
	{
		if(_modelRQList[i] == null)
		{
			continue;
		}
		_modelRQList[i].UpdateRQ();
	}
}


第三步,加载模型或特效后的调用

public static void AddModelParent(GameObject modelGo, Transform parentTran)
{
	if(modelGo == null || parentTran == null || parentTran.gameObject == null)
	{
		return;
	}
	modelGo.transform.parent = parentTran;
	
	int layerUi = parentTran.gameObject.layer;
	SetLayerRecursively(parentTran.gameObject, layerUi);
	
	RenderQueueControl rqControl = modelGo.AddMissingComponent<RenderQueueControl>();
	rqControl.Init();
}

public static void SetLayerRecursively(GameObject parent, int layer)
{
	if(parent == null)
	{
		return;
	}
	parent.layer = layer;
	foreach(Transform child in parent.transform)
	{
		if(child == null)
		{
			continue;
		}
		SetLayerRecursively(child.gameObject, layer);
	}
}


效果图


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值