# 泊松盘采样(Poisson Disk Sampling)生成均匀随机点

圆形为含半径的点，圆形的中心代表生成点

B站有一个不错的搬运教程(Bridson方法)：

https://www.bilibili.com/video/BV1KV411x7LM

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class PoissonDiscSampling
{
public static List<Vector3> GeneratePoints(float radius, Vector2 sampleRegionSize, int numSamplesBeforeRejection = 32)
{
bool IsValid(Vector3 candidate, Vector2 sampleRegionSize, float cellSize, float radius, List<Vector3> points, int[,] grid)
{
if (candidate.x - radius >= 0f && candidate.x + radius < sampleRegionSize.x && candidate.z - radius >= 0f && candidate.z + radius < sampleRegionSize.y)
{
int cellX = Mathf.RoundToInt(candidate.x / cellSize);
int cellZ = Mathf.RoundToInt(candidate.z / cellSize);
int searchStartX = Mathf.Max(0, cellX - 3);
int searchEndX = Mathf.Min(cellX + 3, grid.GetLength(0) - 1);
int searchStartZ = Mathf.Max(0, cellZ - 3);
int searchEndZ = Mathf.Min(cellZ + 3, grid.GetLength(1) - 1);
//如果要检测其它格子内的球，需要遍历周围6个格子

for (int x = searchStartX; x <= searchEndX; x++)
{
for (int z = searchStartZ; z <= searchEndZ; z++)
{
int pointIndex = grid[x, z] - 1;//存长度不存索引，取时减1,0就变成了-1，不需要初始化数组了
if (pointIndex != -1)
{
float dst = (candidate - points[pointIndex]).magnitude;
if (dst < radius * 2f)
{
return false;
}
}
}
}

return true;
}

return false;
}

float cellSize = radius / Mathf.Sqrt(2);

int[,] grid = new int[Mathf.CeilToInt(sampleRegionSize.x / cellSize), Mathf.CeilToInt(sampleRegionSize.y / cellSize)];
List<Vector3> points = new List<Vector3>();
List<Vector3> spawnPoints = new List<Vector3>();

spawnPoints.Add(new Vector3(sampleRegionSize.x / 2f, 0f, sampleRegionSize.y / 2f));
while (spawnPoints.Count > 0)
{
int spawnIndex = Random.Range(0, spawnPoints.Count);
Vector3 spawnCenter = spawnPoints[spawnIndex];

bool candidateAccepted = false;
for (int i = 0; i < numSamplesBeforeRejection; i++)
{
float angle = Random.value * Mathf.PI * 2f;
Vector3 dir = new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle));
Vector3 candidate = spawnCenter + dir * Random.Range(2f, 3f) * radius;

if (IsValid(candidate, sampleRegionSize, cellSize, radius, points, grid))
{
grid[Mathf.RoundToInt(candidate.x / cellSize), Mathf.RoundToInt(candidate.z / cellSize)] = points.Count;
candidateAccepted = true;
break;
}
}
if (!candidateAccepted)
{
spawnPoints.RemoveAt(spawnIndex);
}
}

return points;
}
}

using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
public Vector2 sampleRegionSize = new Vector2(3f, 3f);
private List<Vector3> mPoints;

private void OnEnable()
{
}

private void OnDrawGizmos()
{
if (mPoints == null) return;

float cellSize = radius / Mathf.Sqrt(2);

Color cacheColor = Gizmos.color;
Gizmos.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
for (float x = 0; x < sampleRegionSize.x; x += cellSize)
{
for (float z = 0; z < sampleRegionSize.y; z += cellSize)
Gizmos.DrawWireCube(new Vector3(x, 0f, z), new Vector3(cellSize, 0f, cellSize));
}//生成对应采样点的调试方格
Gizmos.color = cacheColor;

for (int i = 0; i < mPoints.Count; i++)
{
Vector3 vector = mPoints[i];

int x = Mathf.FloorToInt(vector.x / cellSize);
int z = Mathf.FloorToInt(vector.z / cellSize);

Gizmos.DrawWireCube(new Vector3(x, 0f, z) * cellSize, new Vector3(cellSize, 0f, cellSize));
//生成当前点所属方格
}
}
}

1.扩展到3D空间，一些鸟的移动轨迹可以直接用这个做路点寻路

2.可以尝试在UV上分布，然后映射到3D空间

3.可以基于这个做三角剖分(https://www.cnblogs.com/hont/p/15310157.html)

posted @ 2022-06-25 20:31  HONT  阅读(1566)  评论(0编辑  收藏  举报