• NGUI UIPanel绘制原理学习


    NGUI底层绘制都是调用UIDrawCall来完成的,它会动态实例化出材质球,改变UV偏移和缩放(图集显示)。并且全部是面片

    UIPanel也是面片,但是内部物体遮罩比较特殊,经过查找发现,影响UIPanel内部物体遮罩的是它的shader。

    =====================================

    查找过程:

    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.

    7.但是UI Panel光是面片还不够,这并不能解释其中的每个物体都能被裁剪的问题。

    8.检查了下,不太可能是代码问题。似乎是shader做了手脚。在某个软边裁剪的shader,像素着色器下找到如下内容

    half4 frag (v2f IN) : COLOR
    {
        // Softness factor
        float2 factor = (float2(1.0, 1.0) - abs(IN.worldPos)) * _ClipSharpness;
                
        // Sample the texture
        half4 col = tex2D(_MainTex, IN.texcoord) * IN.color;
        col.a *= clamp( min(factor.x, factor.y), 0.0, 1.0);
        return col;
    }

    调试一下,输出值改为tex2D

    return tex2D(_MainTex, IN.texcoord) * IN.color;

    发现不再显示遮罩效果,但调整offset偏移值也无效。

    9.继续刨根问底,发现顶点着色器的UV变换没加TRANSFORM_TEX,百度了下似乎不加外部偏移等参数就无效。加上之后,可以进行偏移等操作。

    v2f vert (appdata_t v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.color = v.color;
        //!!!!
        o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
        o.worldPos = TRANSFORM_TEX(v.vertex.xy, _MainTex);
        return o;
    }

     确实是公用UV,不再有遮罩效果

     可能是shader里得到屏幕位置再进行计算,达到遮罩效果。时间有限就不继续深究了。总之大概来龙去脉就是这样。

  • 相关阅读:
    正则表达式复习 (?<=) (?=)
    HTML 30分钟入门教程
    C# 多线程详解
    C# List
    C# 枚举
    C# 线程数
    C# 泛型2
    C# 泛型
    C# 结构体
    不用Google Adsense的84个赚钱方法
  • 原文地址:https://www.cnblogs.com/hont/p/3669764.html
Copyright © 2020-2023  润新知