Mesh uv
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的值