当前位置:网站首页>Poisson disk sampling for procedural placement
Poisson disk sampling for procedural placement
2022-06-12 06:02:00 【Senior brother Dai Dai】
Procedural object placement (Procedural Placement)
In the open world game , Lots of little things ( Item box , Impurity , The grass , Cask ) It takes a lot of work to place , It is unrealistic to place manually , In order to save work , The engineer explored Various algorithms for programmatically placing objects .

In the game, the position of an object in the world can be represented by a point , The distribution of all objects constitutes a density map (density map)

For now , There are three algorithms for generating object density maps ( Do not take the noise map into account ), Uniform random (Uniofrm Random), Shake the grid (Jitter Grid), Poisson hard disk sampling (Possion Disk Sample)

Uniform random (Uniofrm Random)
The uniform random algorithm is simple , Ideas : The sampling points are obtained by measuring the width and height of the density map .
Code implementation
void GenerateUniformRandom(int width, int height, int newPointsCount, vector<Point2D>& sampleList)
{
sampleList.clear();
for (int i = 0; i < newPointsCount; ++i)
{
sampleList.push_back(Point2D(Randf(width), Randf(height)));
}
}Results show
width = 128, height = 128, sampleCount = 1400

Shake the grid (Jitter Grid)
Dithering grid is to divide the whole picture according to the fixed grid size , It's classified as N x M Lattice of , Then random points are generated in different grids at random
Grid division
Random in the grid

Code implementation
void GenerateJitterGrid(int width, int height, int newPointsCount, float gridSize, vector<Point2D>& sampleList)
{
sampleList.clear();
int GridNumX = (float)width / gridSize;
int GridNumY = (float)height / gridSize;
TRandomVector<Point2DInt> sampleGrids;
for (int x = 0; x < GridNumX; ++x)
{
for (int y = 0; y < GridNumY; ++y)
{
sampleGrids.push_back(Point2DInt(x, y));
}
}
int num = min(sampleGrids.Size(), newPointsCount);
while (sampleList.size() < num)
{
Point2DInt gridPos = sampleGrids.pop();
float LeftTopX = (float)gridPos.x * gridSize;
float LeftTopY = (float)gridPos.y * gridSize;
float RightBottomX = (float)(gridPos.x + 1) * gridSize;
float RightBottomY = (float)(gridPos.y + 1) * gridSize;
float randomX = RandRangef(LeftTopX, RightBottomX);
float randomY = RandRangef(LeftTopY, RightBottomY);
sampleList.push_back(Point2D(randomX, randomY));
}
}
Results show
width = 128, height = 128, gridSize = 6.0, sampleCount = 1400

Poisson hard disk sampling (Possion Disk Sample)
There are many kinds of Poisson hard sampling algorithms , At present, this paper introduces a kind of : Random first point from the whole graph , And then start at this point and do multiple random divergences “ Random offset of distance and angle ”, Get a batch of sampling points , Then these sampling points are added to the random array for recursive random divergence , Finally, the most sufficient samples are generated

Random first point , Add to a random array
TRandomVector<Point2D> processList;
Point2D firstPoint = Point2D(Randf(width), Randf(height));
processList.push_back(firstPoint);
sampleList.push_back(firstPoint);Random offset of angle and distance from the initial point , Offset distance = random(minDistance, random(1.0,2.0) * minDistance), Offset angle = random(0, 2 * PI)
Point2D point = processList.pop();
for (int i = 0; i < newPointsCount; ++i)
{
Point2D newPoint = GenerateRandomPointAround(point, minDistance);
if (IsInRect(newPoint, width, height) && !IsNeighbourhood(grid, newPoint, minDistance, cellSize, 2))
{
processList.push_back(newPoint);
sampleList.push_back(newPoint);
grid.Push(newPoint);
}
}
Point2D GenerateRandomPointAround(const Point2D& point, float minDistance)
{
float r1 = Randf(1.0);
float r2 = Randf(1.0);
//random radius
float radius = minDistance * (r1 + 1.0);
//random angle
float angle = 2.0 * PI * r2;
float newX = point.x + radius * cos(angle);
float newY = point.y + radius * sin(angle);
return Point2D(newX, newY);
}
Note that the distance between the newly generated points and the surrounding sampling points must be greater than minDisatnce

bool IsNeighbourhood(Grid2D& grid, const Point2D& point, float minDistance, float cellSize, int NearSeachCellSize = 2)
{
Point2DInt gridPoint = ImageToGrid(point, cellSize);
vector<Point2DInt> aroundCells = SquareAroundPoint(grid, gridPoint, 2);
for (auto& cell : aroundCells)
{
const vector<Point2D> &cellPoints = grid.data[cell];
for (auto& otherPoint : cellPoints)
{
if (point.Distance(otherPoint) < minDistance)
return true;
}
}
return false;
}Algorithm example diagram

Results show
width = 128, height = 128, minDisance = 3.0, sampleCount = 1400

Improved Poisson hard disk sampling ------ More reasonable offset distance
In the game , The distribution density of the same object in different areas is different , For example, the distribution density of iron ore in cities is small , The distribution density in the field is large . In other words, if the layout of the iron ore uses Poisson hard disk sampling , The random offset distance in the urban area is generally large , The offset distance of the field area is generally small . Therefore, the distance of random divergence should be flexible , Not fixed Offset distance = random(minDistance, random(1.0,2.0) * minDistance),
Better strategy : Specify the maximum distance of the object maxDistance And the minimum distance minDisatnce, Based on density pixel value value, Calculate the dynamic minimum distance when generating new sampling points :newMinDistance = minDistance + (maxDistance - minDistance) * (1.0 - value)
New generation point algorithm
Point2D GenerateRandomPointAroundDensity(const Point2D& point, float minDistance, float maxDistance, float cellSize, FIBITMAP* densityMap, float& newMinDistance)
{
Point2DInt cellPos = ImageToGrid(point, cellSize);
RGBQUAD color;
FreeImage_GetPixelColor(densityMap, int(point.x), int(point.y), &color);
float r1 = Randf(1.0);
float r2 = Randf(1.0);
//random radius
newMinDistance = minDistance + (maxDistance - minDistance) * (1.0 - (float)color.rgbRed / 255.0f);
float radius = (r1 + 1.0) * newMinDistance;
//random angle
float angle = 2.0 * PI * r2;
float newX = point.x + radius * cos(angle);
float newY = point.y + radius * sin(angle);
return Point2D(newX, newY);
}void GeneratePoissonRandomDensity(int width, int height, float minDistance, float maxDistance, int newPointsCount, vector<Point2D>& sampleList, FIBITMAP* densityMap)
{
sampleList.clear();
float cellSize = maxDistance / sqrt(2.0);
Grid2D grid = Grid2D(ceilf((float)width / cellSize), ceilf((float)height / cellSize), cellSize);
TRandomVector<Point2D> processList;
Point2D firstPoint = Point2D(Randf(width), Randf(height));
processList.push_back(firstPoint);
sampleList.push_back(firstPoint);
while (!processList.isEmpty() && sampleList.size() < newPointsCount)
{
Point2D point = processList.pop();
for (int i = 0; i < newPointsCount; ++i)
{
float newMinDistance;
Point2D newPoint = GenerateRandomPointAroundDensity(point, minDistance, maxDistance, cellSize, densityMap, newMinDistance);
if (IsInRect(newPoint, width, height) && !IsNeighbourhood(grid, newPoint, newMinDistance, cellSize, 4))
{
processList.push_back(newPoint);
sampleList.push_back(newPoint);
grid.Push(newPoint);
}
}
}
}Density distribution

Results show
width = 128, height = 128, minDisance = 3.0, minDisance = 8.0, sampleCount = 1400

Project code link
https://download.csdn.net/download/qq_29523119/18985787
Reference
【1】http://devmag.org.za/2009/05/03/poisson-disk-sampling/
边栏推荐
- jpg格式与xml格式文件分离到不同的文件夹
- 项目管理与统筹
- Why doesn't the database use binary tree, red black tree, B tree and hash table? Instead, a b+ tree is used
- Error the main class com xxx. yyy. Application
- Tabulation skills and matrix processing skills
- Project and build Publishing
- Front desk display LED number (number type on calculator)
- MySQL notes
- Divide a folder image into training set and test set
- nrf52832--官方例程ble_app_uart添加led特性,实现电脑uart和手机app控制开发板led开和关
猜你喜欢

Why don't databases use hash tables?

Halcon 3D 1 Reading 3D data

Un mois de DDD hépatique.

数据集成框架SeaTunnel学习笔记

Login authentication filter

Conversion of Halcon 3D depth map to 3D image
![[untitled]](/img/75/599c5b13dd483fad50f73ddb431989.jpg)
[untitled]

姿态估计之2D人体姿态估计 - PifPaf:Composite Fields for Human Pose Estimation

Chapter 8 - structure

Redis memory obsolescence strategy
随机推荐
将一个文件夹图片分成训练集和测试集
Individual application for ov type SSL certificate
Tabulation skills and matrix processing skills
Project technical structure
468. verifying the IP address
The application could not be installed: INSTALL_FAILED_TEST_ONLY
为什么数据库不使用二叉树、红黑树、B树、Hash表? 而是使用了B+树
CCF noi2022 quota allocation scheme
TCP and UDP introduction
项目管理与统筹
Halcon uses points to fit a plane
数据集成框架SeaTunnel学习笔记
Leetcode-646. Longest number pair chain
Jackson - how to convert the array string with only one map object to list < map >
Leetcode-1663. Minimum string with given value
MySQL notes
Error the main class com xxx. yyy. Application
Nrf52832 services et fonctionnalités personnalisés
Leetcode-717. 1-bit and 2-bit characters (O (1) solution)
从传统网络IO 到 IO多路复用
