NGUI UIPanel绘制原理学习

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

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

 

 

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

查找过程:

1.首先是在UIPanel中找到mClipRange,然后在Fill中找到似乎和UIDrawCall有关。

NGUI UIPanel绘制原理学习
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软硬边裁剪的实现。

NGUI UIPanel绘制原理学习
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绘制原理学习

 

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

NGUI UIPanel绘制原理学习

 

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;

}

 

NGUI UIPanel绘制原理学习

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

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

 

你可能感兴趣的:(Panel)