前言
只记录重点,源码放最后
利用mesh类生成网格图形。参考链接1:Unity网格编程-视频版 参考链接2:Unity网格编程-英文版
1.计算顶点序列
- 首先定义x、y、z轴向的边由几个片段组成,定制为xGridCount, yGridCount, zGridCount(4)
- 可计算出顶点数量(4个顶点+12边上数量/不包含顶点+6个面上顶点数量),mesh的顶点数组长度等于顶点数
- 顶点序列计算:计算xz面上点嵌套进y递增的for中,然后再计算顶面和地面的中间点xz面,y分别为0、yGridCount-1
- 代码:
_vertices = new Vector3[GetVerticeCount()];
int index = 0;
//前后左右四个面的点
for (int y = 0; y < yGridCount + 1; y++)
{
for (int x = 0; x < xGridCount + 1; x++, index++)
{
_vertices[index] = new Vector3(x, y, 0);
}
for (int z = 1; z < zGridCount + 1; z++, index++)
{
_vertices[index] = new Vector3(xGridCount, y, z);
}
for (int x = xGridCount - 1; x >= 0; x--, index++)
{
_vertices[index] = new Vector3(x, y, zGridCount);
}
for (int z = zGridCount - 1; z > 0; z--, index++)
{
_vertices[index] = new Vector3(0, y, z);
}
}
//顶面中间面的点
for (int z = 1; z < zGridCount; z++)
{
for (int x = 1; x < xGridCount; x++, index++)
{
_vertices[index] = new Vector3(x, yGridCount, z);
}
}
//底面中间面的点
for (int z = 1; z < zGridCount; z++)
{
for (int x = 1; x < xGridCount; x++, index++)
{
_vertices[index] = new Vector3(x, 0, z);
}
}
2.计算三角面顶点序列
-
概述:两层顶点组成一层平面(以y轴分层),如图四周面V10=V00+1,V01=V00+Ground(Ground为xz面一圈的定点数量)
-
如图顶面或者底面V10=V00+1,V01=V00+Ground-1(Ground为xz面一圈的定点数量)
-
四周面
-
代码:
- for (int y = 0; y < yGridCount; y++)
{
for (int i = 0; i < circleVerticeCount; i++,t+=6,v++)
{
SetQuad(t,v,v+1,v+circleVerticeCount,v+circleVerticeCount+1,circleVerticeCount);
}
}
在四周面一层计算到最后一个面是需要做一些限定,按照以上代码第一层最后一个面三角面序列为(15,16,31,32)但是实际需要(15,0,31,16)才可以填充最后一个面。以下两个函数,第一个生成四周面调用,顶面和地面用第二个。
private void SetQuad(int i, int v00, int v10, int v01, int v11, int circleVerticeCount)
{
//v00/circleVerticeCount算出哪一行,*circleVerticeCount算出每一行第一个索引
//+ v10%circleVerticeCount 算出属于在一行中的第几个
v10 = (v00 / circleVerticeCount) * circleVerticeCount + v10 % circleVerticeCount;
v11 = (v01 / circleVerticeCount) * circleVerticeCount + v11 % circleVerticeCount;
SetQuad(i,v00,v10,v01,v11);
}
private void SetQuad(int i, int v00, int v10, int v01, int v11)
{
_triangles[i] = v00;
_triangles[i + 1] = v01;
_triangles[i + 2] = v10;
_triangles[i + 3] = v01;
_triangles[i + 4] = v11;
_triangles[i + 5] = v10;
}
也可以将最后一个单独拿出来计算,这样第一个SetQuad函数可以省略。
SetQuad(triangles, t, v, v - ring + 1, v + ring, v + 1);
- 顶面
起始点序列等于四周面最后一个顶点序列加一
代码:
for (int x = 0; x < xGridCount - 1; x++, v++, t += 6)
{
SetQuad(t, v, v + 1, v + circleVerticeCount - 1, v + circleVerticeCount);
}
//生成第一行最后一个面
SetQuad(t, v, v + 1, v + circleVerticeCount - 1, v + 2);
t += 6;
//================计算中间面
int vMin = circleVerticeCount * (yGridCount + 1) - 1;//侧面最后一个顶点,计算第一个面用
int vMid = vMin + 1;//抽象v10
int vMax = v + 2;//边上点,计算最后一个面用
for (int z = 0; z < zGridCount-2; z++,vMin--,vMid++,vMax++)
{
//第一个面
SetQuad(t,vMin,vMid,vMin-1,vMid+xGridCount-1);
t += 6;
//中间面
for (int x = 0; x < xGridCount-2; x++,vMid++)
{
SetQuad(t, vMid, vMid+1, vMid + xGridCount - 1,vMid+xGridCount);
t += 6;
}
//最后一个面
SetQuad(t, vMid, vMax, vMid + xGridCount - 1, vMax+1);
t += 6;
}
//============最后一个面
//第一个面
SetQuad(t, vMin, vMid, vMin - 1, vMin-2);
t += 6;
vMin -= 2;
//中见面
for (int x = 0; x < xGridCount - 2; x++, vMid++,vMin--)
{
SetQuad(t, vMid, vMid + 1, vMin, vMin-1);
t += 6;
}
//最后一个面
SetQuad(t, vMid, vMin-2, vMin, vMin-1 );
t += 6;
- 底面
思路同顶面,底面起始点序列等于顶点数组长度减底面个数
3.生成圆角
如图,想让一个交变成圆角,圆角半径内的xy、xz、yz面上点都要移动到圆上。以图左下角举例,在圆角圆上的点坐标x、y、z都小于r,圆心为(r,r,r)。并且可以求出此点的法向量,根据法向量可求出此点在球面上的坐标。点在球面上的x、y、z可能为<r或者大于边长-r都要进行判断。
- 代码:
//判断顶点是否在圆上并且算出顶点的法线,返回此点对应圆心的坐标
private Vector3 SetNormal(int i)
{
Vector3 vertice = _vertices[i];
Vector3 inner = vertice;//圆角的圆心点
if (inner.x<_r)
{
inner.x = _r;
}else if (inner.x>xGridCount-_r)
{
inner.x = xGridCount - _r;
}
if (inner.y < _r)
{
inner.y = _r;
}
else if (inner.y > yGridCount - _r)
{
inner.y = yGridCount - _r;
}
if (inner.z< _r)
{
inner.z= _r;
}
else if (inner.z > zGridCount - _r)
{
inner.z = zGridCount - _r;
}
_normals[i] = (vertice - inner).normalized;
return inner;
}
/// 重新设定顶点的位置
private void ResetVerticePos(int i,Vector3 inner)
{
_vertices[i] = _normals[i] * _r + inner;
}