泊松盘采样(Poisson Disk Sampling)生成均匀随机点
当需要生成随机点且要求随机点自然均匀的分布时,使用泊松盘采样就较为适合。
但该方法与统计学上的概念关联不大,这个只相当于点在面积上服从泊松分布,
而实现这个结果有很多做法。
最终效果:

圆形为含半径的点,圆形的中心代表生成点
B站有一个不错的搬运教程(Bridson方法):
https://www.bilibili.com/video/BV1KV411x7LM
另外Bridson文章里说蓝噪声(BlueNoise)也基于此方法生成
我做了些修改,代码如下:
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))
{
points.Add(candidate);
spawnPoints.Add(candidate);
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 float radius = 0.3f;
public Vector2 sampleRegionSize = new Vector2(3f, 3f);
private List<Vector3> mPoints; private void OnEnable()
{
mPoints = PossonDiscSampling.GeneratePoints(radius, sampleRegionSize);
} 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]; Gizmos.DrawWireSphere(vector, radius); 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)
泊松盘采样(Poisson Disk Sampling)生成均匀随机点的更多相关文章
- Golang生成区间随机整数
package main import ( "fmt" "math/rand" "time" ) func main() { rand.Se ...
- C#使用 RNGCryptoServiceProvider 生成强随机字符串
为了生成更加可靠的随机数,微软在System.Security.Cryptography命名空间下提供一个名为system.Security.Cryptography.RNGCryptoService ...
- .NET使用Bogus生成大量随机数据
.NET如何生成大量随机数据 在演示Demo.数据库脱敏.性能测试中,有时需要生成大量随机数据.Bogus就是.NET中优秀的高性能.合理.支持多语言的随机数据生成库. Bogus的Github链接: ...
- .NET使用Bogus生成大量随机数据(转载)
原文地址:https://www.cnblogs.com/sdflysha/p/20190821-generate-lorem-data.html 在演示Demo.数据库脱敏.性能测试中,有时需要生成 ...
- 如何生成均匀随机数 C++
#include <iostream> #include <fstream> #include <cstdlib> #include <ctime> u ...
- Python Random模块生成伪随机数字
This module implements pseudo-random number generators for various distributions. 对于整数,有一个范围的均匀选择: 对 ...
- 【代码实现】PHP生成各种随机验证码
原文地址:http://www.phpthinking.com/archives/531 验证码在WEB应用中很重要,通经常使用来防止用户恶意提交表单,如恶意注冊和登录.论坛恶意灌水等.本文将通过实例 ...
- 按条件生成j随机json包:randomjson
前端开发中,在做前后端分离的时候,经常需要手写json数据,有2个问题特别揪心: 1,数据是写死的,不能按一定的条件随机生成长度不一,内容不一的数据 2,写数组的时候,如果有很多条,需要一条一条地写, ...
- [实例]JAVA生成字母+随机数字并生成文件
package com.ishow.control.code; import java.io.*; import java.text.SimpleDateFormat; import java.uti ...
- Python_生成大量随机信息
#coding=utf-8 import random import string import codecs ''' 演示如何使用Python标准库random来生成随机数据,这在需要 ''' #常 ...
随机推荐
- Csharp线程
CSharpe线程 目录 CSharpe线程 C#如何操作线程 Thread 1. Thread如何开启一个线程呢? 2. Thread中常见的API 3. thread的扩展封装 threadpoo ...
- MybatisPlus的那些坑
1.实体类属性会被错误解析,需要加上注解@TableField @TableField("front_of_id_card") //身份证正面 private String fro ...
- #博弈论#HDU 2516 取石子游戏
题目 \(n\)个石子,两人轮流取.先取者第1次可以取任意多个, 但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍. 取完者胜.先取者负输出"Second win".先取 ...
- 【FAQ】运动健康服务云侧数据常见问题及解答
目录 Q1:v2接口相比于v1接口传参及返回的数据格式有变化吗?是否可以直接将v1接口改成v2接口? Q2:如何获取采集健康数据的穿戴设备信息? Q3:如何处理非华为手机产生的步数调用采样明细接口查询 ...
- 报名启动丨HMS Core. Sparkle应用创新论坛
图形领域将迎来哪些技术新进展?时下热门的3D.AR/VR和数字人等创新技术又会给应用开发带来哪些新的启发?以上问题,将在7月15日HMS Core. Sparkle应用创新论坛揭晓,赶快点击链接报名参 ...
- ODBC批量merge中出现主键冲突的分析
ODBC 批量 merge 中出现主键冲突的分析 一. 文档概括 客户某个 merge 语句运行时,发生主键冲突报错. 经分析,其原因如下: 由于 merge 语句中,ON 里的判断条件(谓词)中存在 ...
- 【直播预告】HarmonyOS极客松赋能直播第三期:一次开发多端部署与ArkTS卡片开发
- 动态规划(三)——线性dp
一.概念 具有线性阶段划分的动态规划算法叫作线性动态规划(简称线性DP).若状态包含多个维度,则每个维度都是线性划分的阶段,也属于线性DP,如下图所示: 二.线性dp的三大经典例题 1.LIS问题:求 ...
- 前端js解析识别图片二维码
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- Kafka 集群副本数量调整
Kafka 创建时未指定多个副本或者副本数量过少,都可以在后期手动添加,另外如果副本过多也可以减少,当前调整基于 Kafka 的版本是 2.5.1,但是估计 2.1 ~ 2.5 应该都是兼容的. 下面 ...