实现的效果
先绘制一个五边形的mesh,然后在给边缘绘制一圈mesh。
对uv进行重新赋值,实现描边效果
第一步 绘制mesh
绘制多边形mesh,首先先绘制一个五边形。
mesh绘制要素:顶点,三角形,uv信息
顶点信息
就是勾勒三角形用的几个顶点。
按照五边形来说的画, 五边形是五个边。
因为每个边的顶点要做拉伸,所以以中心点为原点,向外扩展画三角形,方便后续操作。
顶点顺序可以按照下图顺序。
绘制三角形
绘制三角形的时候,顶点排列顺序要按照顺时针的方向进行排列才可以绘制出来
所以第一个三角形,顶点依次是:0,1,2
剩下的依次进行循环赋值就可以了
uv信息
为了先绘制出五边形,我们先把uv信息设置为顶点信息显示。
代码实现
[SerializeField]
private MeshFilter m_MeshFilter;
[SerializeField]
[Range(3, 10)]
private int m_Sides = 4; //边数量
[SerializeField]
private float[] m_DistanceList; //对应各个边长
[SerializeField] private Vector3 m_Origin; //中心点
//绘制mesh
private void DrawMesh()
{
if (m_MeshFilter == null)
{
return;
}
Vector3[] vertices = new Vector3[m_Sides + 2];
Vector2[] uv = new Vector2[vertices.Length];
int[] triangles = new int[m_Sides * 3];
vertices[0] = m_Origin;
uv[0] = new Vector2(0.5f, 0.5f);
float angle = 0;
float angleIncrease = 360f / m_Sides;
int verticesIndex = 1;
int triangleIndex = 0;
for (int i = 0; i <= m_Sides; i++)
{
var dir = GetVectorFromAngle(angle);//一定的边长下,根据角度得到方向
vertices[verticesIndex] = m_Origin + dir * GetDisance(i);
uv[verticesIndex] = uv[0] + new Vector2(dir.x, dir.y) * 0.5f;
if (i > 0)
{
triangles[triangleIndex] = 0;
triangles[triangleIndex + 1] = verticesIndex - 1;
triangles[triangleIndex + 2] = verticesIndex;
triangleIndex += 3;
}
verticesIndex++;
angle -= angleIncrease;
}
if (m_Mesh == null)
{
m_Mesh = new Mesh();
}
m_Mesh.Clear();
m_Mesh.vertices = vertices;
m_Mesh.uv = uv;
m_Mesh.triangles = triangles;
m_MeshFilter.mesh = m_Mesh;
}
private Vector3 GetVectorFromAngle(float angle)
{
float angleRad = angle * (Mathf.PI / 180f);
return new Vector3(Mathf.Cos(angleRad), Mathf.Sin(angleRad));
}
private float GetDisance(int index)
{
if (index >= m_DistanceList.Length)
{
index = 0;
}
return m_DistanceList[index];
}
可以调整参数绘制多边形
第二部 绘制描边
绘制描边的思路,是围绕多边形顶点,像内部偏移一定的距离,在绘制一层
需要注意的是定长的是紫色H,而不是顶点到内部顶点的长度。外部顶点到内部顶点的长度,会随着边长的不一定,导致画出来的边是不一定的。只有保持H的宽度不变, 才能保证描边是均匀宽度。否则可能是这样
可简化成三角函数问题。如下图。
C角可以通过 , 360/边数得到
a,b就是所在的边长
H是定长,已知 的数值
如果想要求?的长度,就需要知道A角的大小
推到过程 (学的三角函数都还给老师了, 硬给推导出来的,如果有简单方案,欢迎提供)
//获得内圈顶点的长度
private float CountMoveLength(float a , float b)
{
float c = Mathf.Sqrt(Mathf.Pow(a, 2) + Mathf.Pow(b, 2) - 2 * a * b * Mathf.Cos(72f * Mathf.Deg2Rad));
float sinA = a * Mathf.Sign(72 * Mathf.Deg2Rad) / c;
if (sinA == 0) // 处理一下sinA等于0的情况,除数不能为0
sinA = 0.1f;
return b - m_lineWidth / sinA;
}
由于相邻两个三角形的角度不同所以会导致计算出来的两个点不在同一点上,所以我们就把三角形顶点分开来计算
效果图:
但是如果把边长拉的很小,在边缘的部分可能会出现的问题:
所以我们需要进一步进行顶点边缘优化
第三步 边缘优化
出现上面的原因是,由于在同一个边上,分出来了两个顶点,分别给了左右两边的三角形,但是如果很夸张的话会导致三角形最外层的顶点不能重合,所以就需要我们计算两个交点, 让最外层的两个顶点在相交的地方重合,就可以避免了。计算交点,顶点重新赋值就可以了。
计算交点的代码:
private Vector3 GetIntersection(Vector3 lineAStart, Vector3 lineAEnd, Vector3 lineBStart, Vector3 lineBEnd)
{
float x1 = lineAStart.x, y1 = lineAStart.y;
float x2 = lineAEnd.x, y2 = lineAEnd.y;
float x3 = lineBStart.x, y3 = lineBStart.y;
float x4 = lineBEnd.x, y4 = lineBEnd.y;
//两向量相互垂直,返回0
if (x1 == x2 && x3 == x4 && x1 == x3)
{
return Vector3.zero;
}
//两向量相互平行。返回0
if (y1 == y2 && y3 == y4 && y1 == y3)
{
return Vector3.zero;
}
//两向量相互垂直,返回0
if (x1 == x2 && x3 == x4)
{
return Vector3.zero;
}
//两向量相互平行。返回0
if (y1 == y2 && y3 == y4)
{
return Vector3.zero;
}
float x, y;
if (x1 == x2)
{
float m2 = (y4 - y3) / (x4 - x3);
float c2 = -m2 * x3 + y3;
x = x1;
y = c2 + m2 * x1;
}
else if (x3 == x4)
{
float m1 = (y2 - y1) / (x2 - x1);
float c1 = -m1 * x1 + y1;
x = x3;
y = c1 + m1 * x3;
}
else
{
float m1 = (y2 - y1) / (x2 - x1);
float c1 = -m1 * x1 + y1;
float m2 = (y4 - y3) / (x4 - x3);
float c2 = -m2 * x3 + y3;
x = (c1 - c2) / (m2 - m1);
y = c2 + m2 * x;
}
return new Vector3(x, y, 0);
}
第四步 Reset uv
为了调整描边的大小,uv需要做一个调整。
uv调整的前提需要了解uv是什么, 怎么工作的才能更好的做一个拉伸
把一个四边形,拉伸到一个五边形,让外部边缘重合。
内部的一圈可以拉一个变量出来,然后可以根据图片的边缘,来调整真正的描边效果
private void ResetUV()
{
Vector2[] uv = m_Mesh.uv;
uv[0] = new Vector2(0.5f, 0.5f);
List<Vector2> rectUV = new List<Vector2>(4);
rectUV.Add(new Vector2(0,0));
rectUV.Add(new Vector2(0,1));
rectUV.Add(new Vector2(1,1));
rectUV.Add(new Vector2(1,0));
List<Vector2> rectInUV = new List<Vector2>(4);
rectInUV.Add(new Vector2(linePercent,linePercent));
rectInUV.Add(new Vector2(linePercent,1 -linePercent));
rectInUV.Add(new Vector2(1 - linePercent,1-linePercent));
rectInUV.Add(new Vector2(1-linePercent,linePercent));
if (m_Sides > rectUV.Count)
{
int diff = m_Sides - rectUV.Count;
float everyLength = 1f / (diff + 1);
for (int i = 0; i < diff; i++)
{
rectUV.Insert(3+i,new Vector2(1,everyLength*(i+1)));
rectInUV.Insert(3+i,new Vector2(1-linePercent,everyLength*(i+1)));
}
}
for (int i = 0; i < m_Sides; i++)
{
uv[i*4 + 1] = uv[GetVerticesIndexByIndex(i*4 - 1)] = rectUV[i];
uv[i*4 + 2] = uv[GetVerticesIndexByIndex(i*4)] = rectInUV[i];
}
m_Mesh.uv = uv;
}
private int GetVerticesIndexByIndex(int index)
{
if (index <= 0)
{
return vertices.Length + index -1;
}else if (index >= vertices.Length)
{
return vertices.Length + index - 1;
}
else
return index;
}
public Vector2 GetPointByIndex(int index)
{
float angle = m_Roatation;
float angleIncrease = 360f / m_Sides;
var dir = GetVectorFromAngle(angle);
dir = GetVectorFromAngle(angle-angleIncrease*index);
return m_Origin + dir * GetDisance(index);
}
效果图
第五步 待解决-抗锯齿