UV的概念

uv实际上是u,v,w,和xyz类似,都是记录坐标的三维点。但是,对于游戏来说,通常使用的是uv,或者说绝大多数图片,粒子,模型都是uv贴图实现的。

uv记录的是贴图怎么贴,贴在哪

而uv是一个平面,左下角为(0,0),右上角为(1,1),那么这一张平面如何贴在一个3D物体上呢(mesh)

前几篇文章简单描述过mesh,mesh其中包含一组重要信息——顶点数组。

情景1:平面mesh

平面mesh和uv刚好都是平面,加入mesh是4*4,那么我么可以给UV设置关键点(顶点)

我们把mesh中的顶点和uv关联起来,就完成了贴图的步骤,注意uv是[0,1]的值,顶点和uv坐标一一对应,否则会出现错误

(uv坐标可以少于顶点,这样只会得到部分贴图,用简单的话来讲就是人的身体就是模型,衣服就是uv贴图,其中uv的坐标就是衣服的袖子,裤子等,只有一一对应时才会标准的显示,否则会很诡异或者无法显示)

情景2:曲面mesh

步骤:

  • 确定mesh的长宽高,长宽我们可以用Vector2表示,高度暂定为0
  • 确定mesh的段数,即长度分为几段,宽度分为几段,同样可以用Vector2来记录表示
  • 计算三角形索引(平面)
  • 利用AnimationCurve(动画曲线)来计算曲面的高度值
  • 根据曲线值计算顶点位置
  • 计算uv点,uv和顶点一一对应

直接上代码

计算三角形数组索引

    private int[] GetTriangles()
    {
        //三角形顶点总数:假设是1*1的网格,会有2个顶点复用,因此是6个顶点。假设是2*2的网格,则是4个1*1的网格,即4*6即2*2*6!
        int sum = Mathf.FloorToInt(segment.x * segment.y * 6);
        triangles = new int[sum];
        uint index = 0;
        for (int i = 0; i < segment.y; i++)
        {
            for (int j = 0; j < segment.x; j++)
            {
                int role = Mathf.FloorToInt(segment.x) + 1;
                int self = j + (i * role);
                int next = j + ((i + 1) * role);
                //顺时针
                //第一个三角形
                triangles[index] = self;
                triangles[index + 1] = next + 1;
                triangles[index + 2] = self + 1;
                //第二个三角形
                triangles[index + 3] = self;
                triangles[index + 4] = next;
                triangles[index + 5] = next + 1;
                index += 6;
            }
        }
        return triangles;
    }

计算顶点

    /*计算顶点,存入顶点数组*/
    private void computeVertexes()
    {
        int sum = Mathf.FloorToInt((segment.x + 1) * (segment.y + 1));//顶点总数
        float w = size.x / segment.x;//每一段的长度
        float h = size.y / segment.y;

        GetTriangles();//计算三角形索引顶点序列

        int index = 0;
        vertexes = new Vector3[sum];
        for (int i = 0; i < segment.y + 1; i++)
        {
            for (int j = 0; j < segment.x + 1; j++)
            {
                float tempHeight = 0;
                float tempZ = 0;
                float v = aCurve.Evaluate(i * 1f / (segment.y + 1));
                /*
                 * v:aCurve计算后的y值
                 * a=i*h a:平均分配的z值
                 * newz=a-(v-a)/2
                 * newy=a+(v-a)/2
                 * */
                float a = i * 1f / (segment.y + 1);
                float newz = a - (v - a) / 2;
                float newy = a + (v - a) / 2;
                tempHeight = newy * size.x;
                tempZ = newz * size.x;
                vertexes[index] = new Vector3(j * w, tempHeight, tempZ);//计算完顶点
                index++;
            }
        }       
    }

上面一堆计算,蒙了吧,没关系,画图解释

  • 如果按照平均分配原则,把uv贴图分成等份的若干段,那么黑色点b,a就是0.25,0.5对应的值,也就是动画曲线计算出的原始值
  • 但是问题来了uv贴上去后 (0,b)向量,(b,a)向量并不是等长,uv是等分,但是实际效果会出现拉伸(0,b)明显要长
  • 我们可以以中间的虚线为等分依据
  • 找到该虚线每段的法线与抛物线相交的点得到f,g,h三个点
  • 现在再看(0,f),(f,g),(g,h)三段长度基本相似了
  • 最终我们记录的点就是f,g,h…..

计算uv

        Vector2[] uv = new Vector2[vertexes.Length];
        int iduv = 0;
        for (int i = 0; i < segment.y+1; i++)
        {
            for (int j = 0; j < segment.x+1; j++)
            {
                uv[iduv] = new Vector2((j / (segment.x + 1)), (i / (segment.y + 1)));
                iduv++;
            }
        }

最后赋值

        mesh = gameObject.GetComponent<MeshFilter>().mesh;
        mesh.Clear();
        mesh.vertices = vertexes;
        mesh.triangles = triangles;
        mesh.uv = uv;
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();

最终效果

我们把数据(长宽高,段数)显示在Inspector面板上,便于在运行中调试

想要不同方向的,可以设置xyz来控制,本例是x轴不变,根据z的段数更改y的值

 

版权声明:本文为joemono原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/joemono/p/13158275.html