voronoi图代码_在Unity中实时计算Voronoi图

de8793d02a0b966e46d8a19a6c76bcb2.png

Voronoi图是什么

严格的定义参照这里

z文聿:计算几何第四周:维诺图(Voronoi Diagram)​zhuanlan.zhihu.com
zhihu-card-default.svg

简单的讲,在本次实现中,就是给定图像中的一组点,称为种子点,对图像进行分割,使得每个区域中的点与某个种子点距离最短。

85bab0311223ef8a6289e36e890202c9.png
Voronoi图

上图中红色的点就是种子点。然后每个区域都用了不同颜色区分了。

Voronoi图在游戏中有什么应用

你可以用于AI:

How to Use Voronoi Diagrams to Control AI​gamedevelopment.tutsplus.com
6f6daa855c4336a4d7b7a8b5278852db.png

你可以用于地形生成:

火焰猫燐:Minecraft地图生成原理剖析(三)​zhuanlan.zhihu.com
7232f0ca8beffb0d5b4d870dd5484473.png

你可以用于纹理生成:

The Book of Shaders​thebookofshaders.com
3250492e0ed07946ffa57f30e358749d.png

另外,在图像处理,图形学中Voronoi图应用相当广泛。

Voronoi图怎么计算

  • 暴力计算:遍历每个点,对每个种子点计算距离,然后得到最近的种子点。可以,但是太笨了,太耗时了。
  • 构建Delaunay三角网,然后计算Voronoi图。比较复杂,不适合GPU并行。
  • Jump Flooding Algorithm,这个不是严密的计算方法,只是一种近似的计算方法。但是适合GPU计算,所以也是我采用的方法。

Jump Flooding Algorithm大致的思路是这样的:

  • 将种子点不断向四周的8个点“扩散”,让被扩散的点计算与种子点的距离,选取最近的种子点,然后记录下来,直到所有点都被覆盖到。由于这是一个迭代的过程,每个迭代的最优解不一定得到最后的最优解。
  • 扩散的间距逐步缩小,一开始是间距很大很远的8个点,然后每次迭代间距缩小一半,以n*n的图像为例,需要迭代log2(n)次,每次的间距为2^(log2(N) – passIndex – 1),passIndex 指的是第几次迭代,从0开始数。

看下面的图比较好懂:

d13cbd56617f10ac5000e2f351b8d22d.png
Jump Flooding Algorithm

其中k就是扩散间距了。

在Unity中实时计算Voronoi图

也很简单,用Compute Shader进行迭代就可以了。

代码放这里了:

https://github.com/alpacasking/JumpFloodingAlgorithm​github.com

结果如何

以下都是实时录制的效果,具体计算时间没去测,不过在1080P下也能达到70多FPS。

为了方便看效果,特意让种子点随机运动了,然后种子点也是随机生成的。

04cf1ad482415af1d98501fa598db588.gif
Voronoi图

当然,有了Voronoi图,做距离变换(Distance Transform)也就水到渠成了。距离变换在图像处理中也很有用,感兴趣的童鞋自行查找。下面是距离变换的一些效果图,有点恶心。

1c019a0d028be7b43dd139f956d9aae9.gif
距离变换

fab86a86e257c2a740fb31fada0cec0c.gif
距离变换

参考文献:

  • Rong G, Tan T S. Jump flooding in GPU with applications to Voronoi diagram and distance transform[C]//Proceedings of the 2006 symposium on Interactive 3D graphics and games. 2006: 109-116.
  • https://blog.demofox.org/2016/02/29/fast-voronoi-diagrams-and-distance-dield-textures-on-the-gpu-with-the-jump-flooding-algorithm/
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Javascript实现Lloyd算法生成Voronoi代码示例: ```javascript function generateVoronoi(points, maxIterations) { // 初始化Voronoi心点 let voronoi = d3.voronoi().extent([[-1, -1], [width + 1, height + 1]]); let polygons = voronoi.polygons(points); let centers = points.map(p => getCentroid(polygons[p.index])); // 重复maxIterations次Lloyd算法 for (let i = 0; i < maxIterations; i++) { // 对每个心点求其所在的Voronoi多边形 for (let j = 0; j < centers.length; j++) { let polygon = polygons[points[j].index]; // 将心点移动到其所在Voronoi多边形的重心 centers[j] = getCentroid(polygon); } // 重新计算Voronoi polygons = voronoi.polygons(centers); } return polygons; } // 获取多边形的重心 function getCentroid(polygon) { let x = 0, y = 0, n = polygon.length; polygon.forEach(([px, py]) => { x += px; y += py; }); return [x / n, y / n]; } ``` 这份代码使用了D3.js提供的Voronoi库,具体实现步骤为: 1. 初始化Voronoi心点:使用D3.js提供的`d3.voronoi()`函数来生成初始的Voronoi心点数组。 2. 重复maxIterations次Lloyd算法:对于每一次算法迭代,遍历心点数组,对于每个心点求其所在的Voronoi多边形,将心点移动到其所在Voronoi多边形的重心,然后重新计算Voronoi。 3. 最终返回Voronoi的多边形数组。 注意,这份代码使用的`getCentroid()`函数是计算多边形重心的通用实现,您可以根据需要替换为其他的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值