使用三角形重心坐标生成网格内的任意一点
1.重心坐标
三角形重心坐标(Barycentric Coordinates)可以通过alpha、beta、gamma三个权重总和为1的插值系数得到
三角形内任意一点,可以用此求出某点的重心坐标系数,或是给出系数得到一点:
public class Test : MonoBehaviour { public Transform p0; public Transform p1; public Transform p2; [Range(0f, 1f)] public float w1; [Range(0f, 1f)] public float w2; [Range(0f, 1f)] public float w3; private void OnDrawGizmos() { float sum = w1 + w2 + w3; w1 /= sum; w2 /= sum; w3 /= sum; Gizmos.DrawLine(p0.position, p1.position); Gizmos.DrawLine(p1.position, p2.position); Gizmos.DrawLine(p2.position, p0.position); var target = w1 * p0.position + w2 * p1.position + w3 * p2.position; Gizmos.DrawWireSphere(target, 0.1f); } }
2.绘制
使用三角形重心坐标生成Mesh内任意一点时,需要将三角形面积纳入考量,进行加权随机选取三角形,
选取三角形后,再通过重心坐标选取随机点即可:
using System.Collections.Generic; using UnityEngine; public class Scatters : MonoBehaviour { public struct WeightItem { public float weight; public int value; } private int WeightRandom(WeightItem[] weightItems) { const float kEps = 0.00001f; float sumWeight = 0; for (int i = 0; i < weightItems.Length; i++) sumWeight += weightItems[i].weight; float randomValue = Random.Range(0f, sumWeight); float atte = 0f; for (int i = 0; i < weightItems.Length; i++) { float min = atte; atte += weightItems[i].weight; float max = atte; if (randomValue > min && randomValue < max + kEps) { return weightItems[i].value; } } throw new System.Exception(); } public MeshFilter meshFilter; private Vector3 Exec() { float Area(Vector3 a, Vector3 b, Vector3 c) { return Vector3.Cross(b - a, c - a).magnitude * 0.5f; } var triangles = meshFilter.sharedMesh.triangles; var vertices = meshFilter.sharedMesh.vertices; var weightList = new List<WeightItem>(triangles.Length / 3); for (int i = 0; i < triangles.Length; i += 3) { var triInx0 = triangles[i]; var triInx1 = triangles[i + 1]; var triInx2 = triangles[i + 2]; var a = vertices[triInx0]; var b = vertices[triInx1]; var c = vertices[triInx2]; var area = Area(a, b, c); weightList.Add(new WeightItem() { weight = area, value = i }); } var index = WeightRandom(weightList.ToArray()); var vertIndex = index; var selTriA = transform.TransformPoint(vertices[triangles[vertIndex]]); var selTriB = transform.TransformPoint(vertices[triangles[vertIndex + 1]]); var selTriC = transform.TransformPoint(vertices[triangles[vertIndex + 2]]); var w1 = Random.Range(0f, 1f); var w2 = Random.Range(0f, 1f); var w3 = Random.Range(0f, 1f); float sum = w1 + w2 + w3; w1 /= sum; w2 /= sum; w3 /= sum; return w1 * selTriA + w2 * selTriB + w3 * selTriC; } private void OnDrawGizmos() { Gizmos.color = Color.red; for (int i = 0; i < 400; ++i) { var point = Exec(); Gizmos.DrawWireSphere(point, 0.02f); } } }