之前看过一个博主说,研究代码一开始不能思路铺的太开,一开始应该从最核心的部分看起,然后一层一层的向外围展开,一次只看一个类或一个方法,别想看A的时候又想看B,遇到不懂的时候可以先放着,特别时遇到很复杂的调用关系的代码时千万不要深陷其中。从最核心的一小部分开始着手,比如看DrawCall 类的时候完全不管Widget和Panel,只关心这个类对外开放什么接口,需要什么数据等等。
所以研究NGUI ,首先来看看UIDrawCall吧,在此之前,如果还完全对UIDrawCall没有一点了解的话可以先看看 NGUI 必读1 NGUI渲染基础机制:
下面是UIDrawCall的关键方法的介绍:
一.UpdateGeometry() 最核心最重要的方法, 通过顶点,UV,颜色,贴图等信息绘制UI图形,下面是部分关键的代码:
1.if (mFilter == null) mFilter = gameObject.AddComponent<MeshFilter>();//获得MeshFilter组件
2. mMesh = new Mesh(); //创建用来渲染的网格对象
3. mTriangles = (verts.size >> 1); //三角形数量=顶点数/2
4. mMesh.vertices = verts.buffer; //将顶点 UV 颜色信息赋值跟网格对象,将网格赋值给mFilter
mMesh.uv = uvs.buffer;
mMesh.colors32 = cols.buffer;
mFilter.mesh = mMesh;
5.if (mRenderer == null) mRenderer = gameObject.GetComponent<MeshRenderer>();//获得MeshRenderer组件
6.UpdateMaterials();//更新Material 完成一次drawcall
一句话解释UpdateGeometry() 就是:
将顶点 UV 颜色信息赋值给网格对象,然后将网格赋值给MeshFilter,最后MeshRenderer得到网格和材质,进行渲染。 对于Mesh,和MeshFilter,以及MeshRenderer不了解可查看相关博客:Mesh renderer 和 Mesh 和 Mesh Filter
二.UpdateMaterials() 更新Material
void UpdateMaterials ()
{
//如没有材质或使用了裁剪,就需要重建材质
if (mRebuildMat || mDynamicMat == null || mClipCount != panel.clipCount)
{
RebuildMaterial();
mRebuildMat = false;
}
else if (mRenderer.sharedMaterial != mDynamicMat)
{
//将材质赋值给MeshRender组件 ,更新render
mRenderer.sharedMaterials = new Material[] { mDynamicMat };
}
}
三.RebuildMaterial () 重新生成材质
1. CreateMaterial(); // 创建新的材质对象
2. if (mTexture != null) mDynamicMat.mainTexture = mTexture;//将纹理赋值给材质
3. if (mRenderer != null) mRenderer.sharedMaterials = new Material[] { mDynamicMat };//将材质赋值给MeshRender组件 ,更新render
四.CreateMaterial() 创建新的材质
1. string shaderName = (mShader != null) ? mShader.name :
((mMaterial != null) ? mMaterial.shader.name : "Unlit/Transparent Colored");//默认使用NGUI自带的shader
2.
//如果裁剪数量不为0 获得裁剪相关的shader ,如果不需要裁剪,直接获得shader对象
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);
3. mDynamicMat.shader = shader; //将shader赋值给Material对象
其实DrawCall类功能很单一,下面做一个小测试,测试后会发现:完全可以把DrawCall类分离出NGUI,不用管Panel和Widget类,只用一个DrawCall类就可以独立渲染出想要的图形。
下面是DrawCall测试的步骤:
1.新建一个CustomDrawCall类,把UIDrawCall的代码原封不动的复制过去,把部分的属性改为Public属性:
public Texture mTexture;
[HideInInspector]
public int[] mIndices;
2.注释掉OnWillRenderObject()方法
3.在unity3d场景中新建一个空GameObject,命名为“DrawCallObject”,这个对象就是我们绘制的目标对象。然后给这个对象添加MeshFilder,MeshRender组件,再挂上一个CustomDrawCall脚本,随便找一张贴图,拖到CustomDrawCall面板赋值给MTexture属性
4.创建一个脚本TestDrawCall,在Start方法添加代码如下:
5.把TestDrawCall脚本挂到场景的Main Camera上去
6.运行Uinty3d,切换到场景,就可以看到贴图被绘制出来了:
总结:UIDraw类其实并不复杂,它并不关心谁调用了它,我们完全可以把它独立出来,只要外部传入顶点,UV,颜色,贴图等信息给它,就可以绘制图形了。
测试代码的链接是http://pan.baidu.com/s/1i3GRbtb
原文链接:https://blog.csdn.net/cbbbc/article/details/70918719
推荐另一篇写的不错的关于UIDrawCall的文章:https://zhuanlan.zhihu.com/p/102890994