初学ngui的时候对UIPanel绘制原理不是很了解。所以就找了相关的文章,在这分享一下。
       UIPanel其实是面片,UIDrawCall动态实例化出材质球,再通过UV偏移和缩放,达到滑动显示的效果 。因为是动态实例化出的材质球,加上UIPanel是Editor的界面,所以看不见。网格的Gizmos显示似乎改动过,算是藏的比较隐蔽。

查找过程:

      1.首先是在UIPanel中找到mClipRange,然后在Fill中找到似乎和 UIDrawCall 有关。
void Fill (Material mat)
{
 // Cleanup deleted widgets
for (int i = mWidgets.size; i > 0; ) if (mWidgets[--i] == null) mWidgets.RemoveAt(i);
 // Fill the buffers for the specified material
for (int i = 0, imax = mWidgets.size; i < imax; ++i)
{
UIWidget w = mWidgets.buffer[i];
if (w.visibleFlag == 1 && w.material == mat)
{
UINode node = GetNode(w.cachedTransform);
if (node != null)
{
if (generateNormals) w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans);
else w.WriteToBuffers(mVerts, mUvs, mCols, null, null);
}
else
{
Debug.LogError("No transform found for " + NGUITools.GetHierarchy(w.gameObject), this);
}
}
}
if (mVerts.size > 0)
{
 // Rebuild the draw call's mesh
UIDrawCall dc = GetDrawCall(mat, true);
dc.depthPass = depthPass;
dc.Set(mVerts, generateNormals ? mNorms : null, generateNormals ? mTans : null, mUvs, mCols);
}
else
{
 // There is nothing to draw for this material -- eliminate the draw call
UIDrawCall dc = GetDrawCall(mat, false);
if (dc != null)
{
mDrawCalls.Remove(dc);
NGUITools.DestroyImmediate(dc.gameObject);
}
}
 // Cleanup
mVerts.Clear();
mNorms.Clear();
mTans.Clear();
mUvs.Clear();
mCols.Clear();
}
Fill

       2. 发现 UIDrawCall 是其对材质球创建控制的底层。不过没做成单例的形式,而是组合进来,缺点是UI组件的粒度比较大。

       3. UpdateMaterials方法里是其对Panel软硬边裁剪的实现。
void UpdateMaterials()
{
bool useClipping = (mClipping != Clipping.None);
 // If clipping should be used, create the clipped material
if (useClipping)
{
Shader shader = null;
if (mClipping != Clipping.None)
{
const string alpha = " (AlphaClip)";
const string soft = " (SoftClip)";
// Figure out the normal shader's name
string shaderName = mSharedMat.shader.name;
shaderName = shaderName.Replace(alpha, "");
shaderName = shaderName.Replace(soft, "");
// Try to find the new shader
if (mClipping == Clipping.HardClip ||
mClipping == Clipping.AlphaClip) shader = Shader.Find(shaderName + alpha);
else if (mClipping == Clipping.SoftClip) shader = Shader.Find(shaderName + soft);
 // If there is a valid shader, assign it to the custom material
if (shader == null) mClipping = Clipping.None;
}
 // If we found the shader, create a new material
if (shader != null)
{
if (mClippedMat == null)
{
mClippedMat = mSharedMat;
mClippedMat.hideFlags = HideFlags.DontSave;
}
mClippedMat.shader = shader;
mClippedMat.mainTexture = mSharedMat.mainTexture;
}
else if (mClippedMat != null)
{
NGUITools.Destroy(mClippedMat);
mClippedMat = null;
}
}
else if (mClippedMat != null)
{
NGUITools.Destroy(mClippedMat);
mClippedMat = null;
}
 // If depth pass should be used, create the depth material
if (mDepthPass)
{
if (mDepthMat == null)
{
Shader shader = Shader.Find("Unlit/Depth Cutout");
mDepthMat = new Material(shader);
mDepthMat.hideFlags = HideFlags.DontSave;
}
mDepthMat.mainTexture = mSharedMat.mainTexture;
}
else if (mDepthMat != null)
{
NGUITools.Destroy(mDepthMat);
mDepthMat = null;
}
 // Determine which material should be used
Material mat = (mClippedMat != null) ? mClippedMat : mSharedMat;
if (mDepthMat != null)
{
 // If we're already using this material, do nothing
if (mRen.sharedMaterials != null && mRen.sharedMaterials.Length == 2 && mRen.sharedMaterials[1] == mat) return;
 // Set the double material
mRen.sharedMaterials = new Material[] { mDepthMat, mat };
}
else if (mRen.sharedMaterial != mat)
{
mRen.sharedMaterials = new Material[] { mat };
}
}

void UpdateMaterials()

4.OnWillRenderObject()方法里对材质球的调用十分可疑。

mClippedMat.mainTextureOffset = new Vector2(-mClipRange.x / mClipRange.z, -mClipRange.y / mClipRange.w);

mClippedMat.mainTextureScale = new Vector2(1f / mClipRange.z, 1f / mClipRange.w);

5.为了验证想法,把动态实例化的材质球改掉,手动调节UV。在UpdateMaterials ()中

 //mClippedMat = new Material(mSharedMat);
mClippedMat = mSharedMat;

6.

ngui UIPanel绘制原理

测试完成,看来确实是面片。