NGUI Panel裁剪、层级实现原理

1.NGUI渲染顺序。
上面的文章详细的分析了unity中camera depth -> sorting layer -> sorting order -> RenderQueue  -> Z等属性是如何影响渲染的,大家有疑问可以参考这篇文章。
在NGUI中,层级主要是通过sorting order 和 RenderQueue实现的。
源码如下,NGUI默认使用RenderQueue设置显示层级,这里的renderQueue是根据panel+widget的depth递增(+1)得出来的。
for (int i = 0; i < drawCalls.Count; ++i)
      {
         UIDrawCall dc = drawCalls[i];
 
         dc.renderQueue = (renderQueue == RenderQueue.Explicit) ? startingRenderQueue : startingRenderQueue + i;
         dc.alwaysOnScreen = alwaysOnScreen &&
            (mClipping == UIDrawCall.Clipping.None || mClipping == UIDrawCall.Clipping.ConstrainButDontClip);
         dc.sortingOrder = useSortingOrder ? ((mSortingOrder == 0 && renderQueue == RenderQueue.Automatic) ? sortOrder : mSortingOrder) : 0;
         dc.sortingLayerName = useSortingOrder ? mSortingLayerName : null;
         dc.clipTexture = mClipTexture;
      } 
2.NGUI裁剪。
UIPanel的裁剪主要是通过名字为Hidden/Unlit/Transparent Colored x的shader实现的,x小等于3
看不懂这个shader的可以参考https://gameinstitute.qq.com/community/detail/128082
3.简单实现NGUI的层级和裁剪功能。
1.生成两个mesh,设置不同的depth和贴图,便于显示层级关系。
与上一篇的区别在于设置了NGUI用于裁剪shader,以及mRenderer.sortingOrder和裁剪属性。
public class NguiObjTest: MonoBehaviour
{
    private Texture mMeshMatTex;
    private MeshFilter mFilter;
    private MeshRenderer mRenderer;
    private Material mDynamicMat;
    private Vector4 drawingDimensions = new Vector4(0, 0, 200, 400);
    private int mDepth = 0;
    private NguiTest mTest;
    
    //颜色测试
    Color mColor = Color.grey; 
    Color mGradientTop = Color.green; 
    Color mGradientBottom = Color.red;
 
    public void Create(Texture tex, int depth, NguiTest panel) {
        //transform.localScale = Vector3.one * 0.2f;
        transform.localPosition = new Vector3(depth*20, depth*20 , 0);
        mMeshMatTex = tex;
        mDepth = depth;
        mTest = panel;
 
        CreateMesh();
        SetMeshRender();
    }
 
    private void CreateMesh() {
        Vector3[] newVertices;
        Vector2[] newUV;
        GetUITextureVertices(out newVertices, out newUV);
 
        int indexCount = (newVertices.Length >> 1) * 3; //四个顶点构成两个三角形,共2*3 = 6个顶点
        int[] newTriangles = new int[indexCount];
        int index = 0;
        
        for (int i = 0; i < newVertices.Length; i += 4)
        {
            newTriangles[index++] = i;
            newTriangles[index++] = i + 1;
            newTriangles[index++] = i + 2;
 
            newTriangles[index++] = i + 2;
            newTriangles[index++] = i + 3;
            newTriangles[index++] = i;
        }
        
        if (mFilter == null) mFilter = gameObject.GetComponent<MeshFilter>();
        if (mFilter == null) mFilter = gameObject.AddComponent<MeshFilter>();
        
        Mesh mesh = new Mesh();
        mesh.hideFlags = HideFlags.DontSave;
 
        // Do some calculations...
        mesh.vertices = newVertices;
        mesh.uv = newUV;
        mesh.triangles = newTriangles;
        mesh.SetColors(SetColor());
        mesh.name = "NGUI Test";
        mFilter.mesh = mesh;
    }
 
    /// <summary>
    /// 显示一个Texture,顶点数4
    /// </summary>
    /// <param name="vert"></param>
    /// <param name="uvs"></param>
    private void GetUITextureVertices(out Vector3[] vert, out Vector2[] uvs) {
        Vector4 v = drawingDimensions;
        vert = new []{new Vector3(v.x,v.y), new Vector3(v.x,v.w),
            new Vector3(v.z,v.w),new Vector3(v.z,v.y), };
        uvs = new []{new Vector2(0,0), new Vector2(0,1),new Vector2(1,1),new Vector2(1,0), };
    }
 
    /// <summary>
    /// 通过Hidden/Unlit/Transparent Colored 1 shader实现裁剪。_ClipRange0裁剪区域,虚边_ClipArgs0(边缘渐隐效果)
    /// mDynamicMat.renderQueue\mRenderer.sortingOrder都可以修改显示层级
    /// </summary>
    private void SetMeshRender() 
    {
        if (mRenderer == null) mRenderer = gameObject.GetComponent<MeshRenderer>();
        if (mRenderer == null) mRenderer = gameObject.AddComponent<MeshRenderer>();
        
        Shader shader = Shader.Find("Hidden/Unlit/Transparent Colored 1");
        mDynamicMat = new Material(shader);
        mDynamicMat.name = "[NGUI] " + shader.name;
        mDynamicMat.mainTexture = mMeshMatTex;
 
        Vector4 cr = mTest.ClipRange;
        Vector4 soft = mTest.ClipSoftness;
        float angle = 0f;
        
        angle *= -Mathf.Deg2Rad;
 
        Vector2 sharpness = new Vector2(1000.0f, 1000.0f);
        if (soft.x > 0f) sharpness.x = cr.z / soft.x;
        if (soft.y > 0f) sharpness.y = cr.w / soft.y;
 
        mDynamicMat.SetVector(Shader.PropertyToID("_ClipRange0"), 
            new Vector4(-cr.x / cr.z, -cr.y / cr.w, 1f / cr.z, 1f / cr.w));
        mDynamicMat.SetVector(Shader.PropertyToID("_ClipArgs0"), 
            new Vector4(sharpness.x, sharpness.y, Mathf.Sin(angle), Mathf.Cos(angle)));
 
        //mDynamicMat.renderQueue = 3000 + mDepth;
        mRenderer.sortingOrder = mDepth; //层级
        mRenderer.sharedMaterials = new Material[] { mDynamicMat };
    }
 
    /// <summary>
    /// 颜色设置,参考UIBasicSprite.AddVertexColours
    /// 每个顶点对应一个颜色值,通过mesh.SetColors传给mesh做显示
    /// </summary>
    public List<Color> SetColor() 
    {
        List<Color> col = new List<Color>();
        col.Add(mColor * mGradientBottom);//0,0
        col.Add(mColor * mGradientTop);//0,1
        col.Add(mColor * mGradientTop);//1,1
        col.Add(mColor * mGradientBottom);//1,0
 
        return col;
    }
}
2.生成测试mesh。
public class NguiTest: MonoBehaviour {
    public Vector4 mClipRange;
    public Vector4 mClipSoftness;
 
    public Texture mTop;
    public Texture mBottom;
    // Start is called before the first frame update
    void Start() {
        TestPanel();
    }
 
    /// <summary>
    /// 生成两个对象,传入不一样的depth和Texture
    /// 测试层级和裁剪区域
    /// </summary>
    private void TestPanel() 
    {
        GameObject bottom = new GameObject("Bottom");
        NguiObjTest ntBottom = bottom.AddComponent<NguiObjTest>();
        ntBottom.Create(mBottom, 1, this);
        
        GameObject top = new GameObject("Top");
        NguiObjTest ntTop = top.AddComponent<NguiObjTest>();
        ntTop.Create(mTop, 2, this);
    }
 
    public Vector4 ClipRange 
    {
        get {
            return mClipRange;
        }
    }
    
    public Vector4 ClipSoftness 
    {
        get {
            return mClipSoftness;
        }
    }
}
3.测试,基于上述代码实现NGUI的层级、裁剪、颜色功能。
在界面上生成一个空对象并挂载NguiTest组件,设置如下属性,mClipRange是裁剪区域(世界坐标),mClipSoftness是便于的渐隐效果,mTop是显示在上层的图片。
效果如下:这边加了NGUI的Gradient颜色渐变效果。
 
posted @ 2020-08-15 15:38  柯腾_wjf  阅读(765)  评论(0编辑  收藏  举报