• Unity动态渲染一个Mesh


     
    在unity里动态渲染一个Mesh,需要设置MeshFilter、MeshRenderer、Mesh。
    mesh负责提供渲染的网格信息,对于NGUI,最简单的UITexture的Mesh包含:四个顶点,每个顶点是UITexture的世界坐标,四个uv是显示的图片的4个角(0,0 0,1 1,1 1,0),两个三角形,四个颜色值。
    MeshRenderer需要设置指定的材质球,该材质球的mainTexture是UITexture显示的图片。
    下面代码实际上就可以把指定的mMeshMatTex贴图会知道屏幕上。
    Vector3[] newVertices = new []{new Vector3(0,0), new Vector3(0,1),new Vector3(1,1),new Vector3(1,0),};
    Vector2[] newUV= new []{new Vector2(0,0), new Vector2(0,1),new Vector2(1,1),new Vector2(1,0),};
    int[] triangles = new[] {0, 1, 2, 2, 3, 0};
     
    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.name = "NGUI Test";
    mFilter.mesh = mesh;
     
    if (mRenderer == null) mRenderer = gameObject.GetComponent<MeshRenderer>();
    if (mRenderer == null) mRenderer = gameObject.AddComponent<MeshRenderer>();
     
    Shader shader = Shader.Find("Unlit/Transparent Colored");
    mDynamicMat = new Material(shader);
    mDynamicMat.name = "[NGUI] " + shader.name;
    mDynamicMat.mainTexture = mMeshMatTex;
    mRenderer.sharedMaterials = new Material[] { mDynamicMat };
    1.MeshFilter MeshRenderer Mesh。
    在unity中渲染一个Mesh需要包含MeshFilter、MeshRenderer两个组件以及一个Mesh。
    Mesh:网格
    作用:Mesh是指模型的网格,建模就是建网格。细看Mesh,可以知道Mesh的主要属性内容包括顶点坐标vertices,法线normals,纹理坐标uv、颜色colors,三角形绘制序列triangles等其他有用属性和功能。因此建网格,就是画三角形;画三角形就是定位三个点。
     
    Mesh Filter: 网格过滤器
    作用:把Mesh扔给MeshRender将模型或者说是几何体绘制显示出来。
     
    Mesh renderer:网格渲染器
    从网格过滤器中获得几何体的形状然后进行渲染。
     
    Mesh Filter和Mesh renderer的作用:
    In a component based system it's common to seperate concepts into seperate components. At the moment the MeshFilter doesn't have many other uses besides being a Mesh-provider for a MeshRenderer. Though the only additional "feature" is when you have a MeshFilter attached with a referenced Mesh, when you add a MeshCollider to the gameobject it automatically takes that mesh reference. This does not happen with the SkinnedMesh renderer. Mainly because it's usually pointless to have a MeshCollider on a skinned mesh as the collider mesh is not skinned anyways.
     
    先来看下面一段代码:有点长
    using System.Collections.Generic;
    using UnityEngine;
     
    public class Test: MonoBehaviour {
        // Start is called before the first frame update
        public Texture mUITexture;//UITexture的图片名字,用于显示图片
        public string mSpriteName;//Sample/Slider模式下的图片名字,用于显示图片
        public string mTiledName; //Tiled模式下的图片名字,用于显示图片
        public UIAtlas mAtlas;
     
        [Range(0,1)]
        public float mFillAmount = 0.5f;//Filled模式的进度值
     
        private Texture mMeshMatTex;
        private MeshFilter mFilter;
        private MeshRenderer mRenderer;
        private Material mDynamicMat;
        private Vector4 drawingDimensions = new Vector4(0, 0, 200, 400);
        
        void Start() {
            CreateMesh();
            SetMeshRender();
        }
     
        /// <summary>
        /// 参考UIDrawCall.GenerateCachedIndexBuffer生成newTriangles
        /// 参考UIDrawCall.UpdateGeometry生成Mesh并设置参数
        /// </summary>
        private void CreateMesh() {
            Vector3[] newVertices;
            Vector2[] newUV;
            //GetUITextureVertices(out newVertices, out newUV);
            //GetNormalSpriteVertices(out newVertices, out newUV);
            //GetSliderSpriteVertices(out newVertices, out newUV);
            //GetTiledSpriteVertices(out newVertices, out newUV);
            GetFilledSpriteVertices(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.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), };
            mMeshMatTex = mUITexture;
        }
        
        /// <summary>
        /// 显示Sample的Sprite,参考UISprite.OnFill,顶点数4
        /// </summary>
        /// <param name="vert"></param>
        /// <param name="uvs"></param>
        private void GetNormalSpriteVertices(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), };
     
            UISpriteData sd = mAtlas.GetSprite(mSpriteName);
            var tex = mAtlas.texture;
            mMeshMatTex = tex;
            
            var outer = new Rect(sd.x, sd.y, sd.width, sd.height);
            outer = NGUIMath.ConvertToTexCoords(outer, tex.width, tex.height);
            uvs = new []{new Vector2(outer.xMin,outer.yMin), new Vector2(outer.xMin,outer.yMax),
                new Vector2(outer.xMax,outer.yMax),new Vector2(outer.xMax,outer.yMin), };
        }
        
        /// <summary>
        /// 显示Slider的Sprite,参考UIBasicSprite.SlicedFill。九宫格模式
        /// 顶点数4*9(九宫格分成的9块区域,每块区域的四个顶点)
        /// </summary>
        /// <param name="vert"></param>
        /// <param name="uv"></param>
        private void GetSliderSpriteVertices(out Vector3[] vert, out Vector2[] uv) 
        {
            UISpriteData sd = mAtlas.GetSprite(mSpriteName);
            var tex = mAtlas.texture;
            mMeshMatTex = tex;
            
            var outer = new Rect(sd.x, sd.y, sd.width, sd.height);
            var inner = new Rect(sd.x + sd.borderLeft, sd.y + sd.borderTop,
                sd.width - sd.borderLeft - sd.borderRight,
                sd.height - sd.borderBottom - sd.borderTop);
            
            outer = NGUIMath.ConvertToTexCoords(outer, tex.width, tex.height);
            inner = NGUIMath.ConvertToTexCoords(inner, tex.width, tex.height);
            
            Vector2[] mTempPos = new Vector2[4];
            Vector2[] mTempUVs = new Vector2[4];
            Vector4 v =drawingDimensions;//设置渲染的Sprite长高为100
            Vector4 br = new Vector4(sd.borderLeft, sd.borderBottom, sd.borderRight, sd.borderTop);
            
            mTempPos[0] = new Vector2(v.x, v.y);
            mTempPos[1] = new Vector2(v.x + br.x, v.y + br.y);
            mTempPos[2] = new Vector2(v.z - br.z, v.w - br.w);
            mTempPos[3] = new Vector2(v.z, v.w);
     
            mTempUVs[0] = new Vector2(outer.xMin, outer.yMin);
            mTempUVs[1] = new Vector2(inner.xMin, inner.yMin);
            mTempUVs[2] = new Vector2(inner.xMax, inner.yMax);
            mTempUVs[3] = new Vector2(outer.xMax, outer.yMax);
            
            List<Vector3> verts = new List<Vector3>();
            List<Vector2> uvs = new List<Vector2>();
            
            for (int x = 0; x < 3; ++x)
            {
                int x2 = x + 1;
     
                for (int y = 0; y < 3; ++y)
                {
                    int y2 = y + 1;
     
                    verts.Add(new Vector3(mTempPos[x].x, mTempPos[y].y));
                    verts.Add(new Vector3(mTempPos[x].x, mTempPos[y2].y));
                    verts.Add(new Vector3(mTempPos[x2].x, mTempPos[y2].y));
                    verts.Add(new Vector3(mTempPos[x2].x, mTempPos[y].y));
     
                    uvs.Add(new Vector2(mTempUVs[x].x, mTempUVs[y].y));
                    uvs.Add(new Vector2(mTempUVs[x].x, mTempUVs[y2].y));
                    uvs.Add(new Vector2(mTempUVs[x2].x, mTempUVs[y2].y));
                    uvs.Add(new Vector2(mTempUVs[x2].x, mTempUVs[y].y));
                }
            }
     
            vert = verts.ToArray();
            uv = uvs.ToArray();
        }
        
        /// <summary>
        /// 显示Tiled的Sprite,参考UIBasicSprite.TiledFill。平铺模式
        /// 重复次数Tiled.x = widget.width/(tex.width + padding.left + padding.right)
        /// 重复次数Tiled.y = widget.height/(tex.height + padding.top + padding.bottom)
        /// 顶点数4 * Mathf.Ceil(Tiled.x) * Mathf.Ceil(Tiled.y)
        /// </summary>
        /// <param name="vert"></param>
        /// <param name="uv"></param>
        private void GetTiledSpriteVertices(out Vector3[] vert, out Vector2[] uv) 
        {
            UISpriteData sd = mAtlas.GetSprite(mTiledName);
            var tex = mAtlas.texture;
            mMeshMatTex = tex;
            
            var inner = new Rect(sd.x + sd.borderLeft, sd.y + sd.borderTop,
                sd.width - sd.borderLeft - sd.borderRight,
                sd.height - sd.borderBottom - sd.borderTop);
            
            inner = NGUIMath.ConvertToTexCoords(inner, tex.width, tex.height);
            
            Vector4 u = new Vector4(inner.xMin, inner.yMin, inner.xMax, inner.yMax);
            Vector4 p = new Vector4(sd.paddingLeft,sd.paddingBottom,sd.paddingRight,sd.paddingTop);
            var size = new Vector2(sd.width, sd.height);
            Vector4 v = drawingDimensions;//设置渲染的Sprite长高为100
            
            List<Vector3> verts = new List<Vector3>();
            List<Vector2> uvs = new List<Vector2>();
            
            float x0 = v.x;
            float y0 = v.y;
            float u0 = u.x;
            float v0 = u.y;
            
            while (y0 < v.w)
            {
                y0 += p.y;
                x0 = v.x;
                float y1 = y0 + size.y;
                float v1 = u.w;
     
                if (y1 > v.w) //超出上边界时处理
                {
                    v1 = Mathf.Lerp(u.y, u.w, (v.w - y0) / size.y);
                    y1 = v.w;
                }
     
                while (x0 < v.z)
                {
                    x0 += p.x;
                    float x1 = x0 + size.x;
                    float u1 = u.z;
     
                    if (x1 > v.z)//超出左边界时处理
                    {
                        u1 = Mathf.Lerp(u.x, u.z, (v.z - x0) / size.x);
                        x1 = v.z;
                    }
     
                    verts.Add(new Vector3(x0, y0));
                    verts.Add(new Vector3(x0, y1));
                    verts.Add(new Vector3(x1, y1));
                    verts.Add(new Vector3(x1, y0));
     
                    uvs.Add(new Vector2(u0, v0));
                    uvs.Add(new Vector2(u0, v1));
                    uvs.Add(new Vector2(u1, v1));
                    uvs.Add(new Vector2(u1, v0));
                    
     
                    x0 += size.x + p.z;
                }
     
                y0 += size.y + p.w;
            }
            
            vert = verts.ToArray();
            uv = uvs.ToArray();
        }
        
        /// <summary>
        /// 显示Filled的Sprite,参考UIBasicSprite.FilledFill。进度条模式
        /// 顶点数4*1(实际只有一块经过裁切后的区域,不显示区域直接被丢弃了),这边只展示最简单的FillDirection.Horizontal模式
        /// </summary>
        /// <param name="vert"></param>
        /// <param name="uv"></param>
        private void GetFilledSpriteVertices(out Vector3[] vert, out Vector2[] uv) {
            Vector4 v = drawingDimensions;
            UISpriteData sd = mAtlas.GetSprite(mSpriteName);
            var tex = mAtlas.texture;
            mMeshMatTex = tex;
            
            var outer = new Rect(sd.x, sd.y, sd.width, sd.height);
            outer = NGUIMath.ConvertToTexCoords(outer, tex.width, tex.height);
            Vector4 u = new Vector4(outer.xMax, outer.yMin, outer.xMin, outer.yMax);
            
            float fill = (u.z - u.x) * mFillAmount;
            v.z = v.x + (v.z - v.x) * mFillAmount;
            u.z = u.x + fill;
            
            vert = new Vector3[4];
            uv = new Vector2[4];
            vert[0] = new Vector2(v.x, v.y);
            vert[1] = new Vector2(v.x, v.w);
            vert[2] = new Vector2(v.z, v.w);
            vert[3] = new Vector2(v.z, v.y);
     
            uv[0] = new Vector2(u.x, u.y);
            uv[1] = new Vector2(u.x, u.w);
            uv[2] = new Vector2(u.z, u.w);
            uv[3] = new Vector2(u.z, u.y);
        }
     
        /// <summary>
        /// 参考UIDrawCall.RebuildMaterial生成一个Mesh,这边忽略了裁剪的逻辑
        /// </summary>
        private void SetMeshRender() 
        {
            if (mRenderer == null) mRenderer = gameObject.GetComponent<MeshRenderer>();
            if (mRenderer == null) mRenderer = gameObject.AddComponent<MeshRenderer>();
            
            Shader shader = Shader.Find("Unlit/Transparent Colored");
            mDynamicMat = new Material(shader);
            mDynamicMat.name = "[NGUI] " + shader.name;
            mDynamicMat.mainTexture = mMeshMatTex;
            mRenderer.sharedMaterials = new Material[] { mDynamicMat };
        }
    }
    测试方式:在场景中创建空对象并挂载Test脚本,注意设置如下参数,并注意相机的位置,就可以正确测试NGUI对UITexture、UISprite的SampleSliderTiledFilled的模式的实现。
    2.CreateMesh。
    创建了一个Mesh,并设置了Mesh渲染所必需的的vertices、uv、triangles参数。
    动态挂载了Mesh渲染所依托的MeshFilter的组件。
    3.SetMeshRender。
    创建了一个MeshRenderer,并设置了render的材质球属性,包括shader、mainTexture。
    4.三角形计算:三角形实际指示了Mesh渲染的正反面。
    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;
    }
    5.根据不同Type计算顶点值和UV值。
    具体计算看上面的注释就很清楚了。
    GetUITextureVertices(out newVertices, out newUV);
    GetNormalSpriteVertices(out newVertices, out newUV);
    GetSliderSpriteVertices(out newVertices, out newUV);
    GetTiledSpriteVertices(out newVertices, out newUV);
    GetFilledSpriteVertices(out newVertices, out newUV);
    当然,NGUI做了很多额外的工作,这边只是简单地展示其显示原理,方便后续对NGUI的源码的学习。
  • 相关阅读:
    Centos7 ifconfig命令找不到
    request的各种方法
    linux开放端口
    easyui datagrid 部分参数
    设置tomcat内存
    tomcat做成系统服务
    Meta-analysis with complex research designs: dealing with dependence from multiple measures and multiple group comparisons
    多重校正
    DTI
    learning source archive
  • 原文地址:https://www.cnblogs.com/wang-jin-fu/p/13508953.html
Copyright © 2020-2023  润新知