程序化物件放置(procedural placement)之泊松硬盘采样(poisson disk sampling)
程序化物件放置(Procedural Placement)
在开放世界游戏中, 很多小物件(物品箱子, 杂物, 草,木桶)的摆放是很耗费工作量的, 靠人工手动摆放是不现实的,为了节省工作量, 工程师探索了 程序化摆放物件的各种算法.
在游戏中物件在世界中的位置可以用一个点来表示, 所有物件的分布就构成了一张密度图(density map)
目前而言,物件密度图生成算法有三种(不把噪声图考虑其中), 统一随机(Uniofrm Random), 抖动格子(Jitter Grid), 泊松硬盘采样(Possion Disk Sample)
统一随机(Uniofrm Random)
统一随机算法很简单, 思路: 在密度图的宽度和高度中进行进行就得到采样点.
代码实现
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)));}
}
结果显示
width = 128, height = 128, sampleCount = 1400
抖动格子(Jitter Grid)
抖动格子是先按照固定的格子大小将整个图片进行划分, 划为N x M的格子, 然后在随机在不同的格子内进行随机点的生成
格子划分
格子内随机
代码实现
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));}
}
结果显示
width = 128, height = 128, gridSize = 6.0, sampleCount = 1400
泊松硬盘采样(Possion Disk Sample)
泊松硬采样的算法实现有多种, 目前介绍一种比较容易实现的: 从整个图中随机第一个点, 然后从这个点开始进行多次随机发散 “距离和角度随机偏移”,得到一批采样点, 然后这批采样点加入随机数组中进行递归的随机发散行为, 最终生成最够的采样
随机第一个点,加入随机数组中
TRandomVector<Point2D> processList;Point2D firstPoint = Point2D(Randf(width), Randf(height));processList.push_back(firstPoint);sampleList.push_back(firstPoint);
从初始点进行角度和距离的随机偏移, 偏移距离 = random(minDistance, random(1.0,2.0) * minDistance), 偏移角度 = 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 radiusfloat radius = minDistance * (r1 + 1.0);//random anglefloat angle = 2.0 * PI * r2;float newX = point.x + radius * cos(angle);float newY = point.y + radius * sin(angle);return Point2D(newX, newY);
}
注意新生成的点还得判断其和周围已经生成的采样点的距离都大于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;
}
算法示例图
结果显示
width = 128, height = 128, minDisance = 3.0, sampleCount = 1400
改进泊松硬盘采样------更合理的偏移距离
在游戏中,同个物件在不同的区域分布密度是不一样的,比如说铁矿石出现在城市的分布密度小,在野外出现的分布密度大。换句来说假如铁矿石的布置用的是泊松硬盘采样,则在城市的区域随机偏移距离总体比较大,野外区域的偏移距离总体比较小。 因此随机发散的距离应该是灵活变化,而非固定的 偏移距离 = random(minDistance, random(1.0,2.0) * minDistance),
更好的策略:规定物件的最大距离maxDistance和最小距离minDisatnce,根据密度图像素值value, 在生成新的采样点的时候计算动态的最小距离:newMinDistance = minDistance + (maxDistance - minDistance) * (1.0 - value)
新的生成点算法
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 radiusnewMinDistance = minDistance + (maxDistance - minDistance) * (1.0 - (float)color.rgbRed / 255.0f);float radius = (r1 + 1.0) * newMinDistance;//random anglefloat 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);}}}
}
密度分布图
结果显示
width = 128, height = 128, minDisance = 3.0, minDisance = 8.0, sampleCount = 1400
项目代码链接
https://download.csdn.net/download/qq_29523119/18985787
资料参考
【1】http://devmag.org.za/2009/05/03/poisson-disk-sampling/
程序化物件放置(procedural placement)之泊松硬盘采样(poisson disk sampling)相关推荐
- 泊松圆盘采样(Poisson Disk Sampling)代码实现
欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击. 参考资料 https://www.jianshu.com/p/4556ccad58d9 https://blog.csdn.ne ...
- 【项目记录】过程内容生成(PCG)与快速泊松碟采样算法实现Unity中的物件摆放
背景 在当今游戏开发过程中,创建一个内容丰富的虚拟世界一直是一个十分费时的工作.游戏制作者们希望在更短的时间内在游戏中增加更加多样的内容,过程内容生成技术为有限时间内制作复杂的虚拟世界提供了一种解 ...
- 【GDC翻译】“地平线零之曙光”中基于GPU的程序化实时放置系统
原视频:GDC Vault - GPU-Based Run-Time Procedural Placement in 'Horizon: Zero Dawn' PDF:https://www.gdcv ...
- R语言构建xgboost模型:使用xgboost构建泊松回归(poisson regression)模型
R语言构建xgboost模型:使用xgboost构建泊松回归(poisson regression)模型 目录 R语言构建xgboost模型:使用xgboost构建泊松回归(poisson regre ...
- Python使用sklearn构建广义线性模型:泊松回归(Poisson regression)实战
Python使用sklearn构建广义线性模型:泊松回归(Poisson regression)实战 目录 Python使用sklearn构建广义线性模型:泊松回归(Poisson regressio ...
- R语言广义线性模型泊松回归(Poisson Regression)模型
R语言广义线性模型泊松回归(Poisson Regression)模型 试想一下,你现在就站在一个人流密集的马路旁,打算收集闯红灯的人群情况(?).首先,利用秒表和计数器,一分钟过去了,有5个人闯红灯 ...
- 泊松回归(Poisson regression)、COX回归、分类器变回归器、回归算法注意事项、多重共线性问题
泊松回归(Poisson regression).COX回归.分类器变回归器.回归算法注意事项.多重共线性问题 目录
- python、turtle实现泊松盘采样
泊松盘采样算法参考 https://www.jasondavies.com/poisson-disc/ 算法核心要点 以上一次采样点为中心,随机取圆环内点进行采样 采样时需要判断inner范围内是否已 ...
- 内存缓存(from memory cache)和硬盘缓存(from disk cache) 的区别
引言 ?命中强制缓存时,资源会显示 from memory cache or from disk cache 两者的区别 内存缓存(from memory cache) 内存缓存具有两个特点,分别是快 ...
最新文章
- Python:数据提取之JSON与JsonPATH
- HDU2925(约瑟夫环问题)
- android 生成签名命令
- 5G NGC — UDM 统一数据管理
- Codeforces 235C
- 更深入地了解Java 8 Date and Time API
- 【本地差分隐私与随机响应代码实现】差分隐私代码实现系列(十三)
- Silverlight4 ColorPicker控件
- redis 端口_「建议收藏」手把手教你搭建redis集群
- java实现浏览器_利用Java实现网页浏览器
- Google永久允许使用Flash 100%详细简单+解决
- Metasploit之——社会工程学工具包
- gift to me by myself on 2012's new year
- Vue + Spring Boot 项目实战
- linux 移动硬盘 mac,Macbook pro使用原生EFI在移动硬盘安装Windows+Fedora双系统
- AdxMenu真的不错!我写了个中文的使用说明如下,希望大家用得着
- 边缘计算七大核心技术
- Web服务器群集——公有CA构建阿里云服务器HTTPS
- iPhone出现绿屏问题怎么修复?可以尝试这些解决方案
- 知乎9.7万人围观热帖:“同事被富婆包养了,我哪里不如他?”
热门文章
- 营销思维篇:透漏22个我赚钱的小秘密
- 程序猿生存定律-六个程序猿的故事(2)
- Mac 不能写入移动硬盘的解决方案
- matlab中区分fplot和plot,以及作图时sin(1./x)需要点除而不是除
- 下载了Mysql for excel却不在excel里显示
- 利用CMS漏洞渗透并获取某服务器权限
- 基于FPGA的DDS 信号发生器(三)
- Android音乐播放器开发(3)—注册
- ueditor 图片上传 java_Spring+Vue整合UEditor富文本实现图片附件上传的方法
- Windows11 安装教程(ultraiso制作启动盘)