视野迷雾算法

算法简介

算法链接:http://code.google.com/p/libfov/

使用场景:2D格子迷雾算法,以一个点为中心,向外遍历每个格子,当遇到视野阻挡时,阻挡后的格子不可见

优点:比A*快上百倍,对于没有障碍的直线可以由起点直接跳到终点(高效的原因)

复杂度:视野范围内的格子数

相关文件:fov.c、fov.h

它这个算法中有其他几种形状,因为之前工作中,只用到了圆这一种迷雾形状,所以这里只介绍计算圆形迷雾的算法原理。等之后有空了再看看其他形状

算法原理

在_fov_circle函数计算圆形视野时可以看到,它将圆形区域分成8块扇形区域。每块扇形区域计算方式是类似的,这里我们就只讨论一种就可以了。假设我们讨论ppn这块区域,我们用的形状是FOV_SHAPE_CIRCLE。大体思路是,用变量dx从左往右遍历,用变量dy0对一列格子从下往上进行遍历,遍历到dy1(因为是等腰直角三角形,所以如果没有视野阻挡的话,高度和宽度是一样的,也就是说所处第几列它的高就是几,也就是说一开始完全没阻挡时,dy0是0,dy1等于dx)。当遍历完一列没发现视野阻挡时,则继续遍历下一列。假设某一次,我们从下往上遍历时,突然遇到一个视野阻挡,我们可以根据这个阻挡所在位置算出一条线的斜率(dy-0.5)/(dx+0.5),这么做是算出格子中心点到原点的斜率,这时将起始斜率(如果一开始没碰撞那么斜率就是0)和这个有阻挡的斜率递归到下一列,下一列根据这个阻挡斜率算出dy1,这样视野阻挡就限制了后面能“看到”的范围。回到刚才有阻挡的那一列,当在有阻挡的格子之后如果遍历到第一个非阻挡的格子,则要记下它的斜率作为下一列的起始斜率,然后继续往上遍历,直到到达最高处或又遇到视野阻挡,则将该位置的斜率记下,联合刚才的起始斜率一起传到下一列。

这就是这个算法的主要思想,细节的地方是像图片中,ppn和ppy斜线出的格子由ppn去遍历,其他同理,像ppy和pmy公用了一条直线,也只会是其中一个会遍历到这条线上的格子,这样就保证每个格子只会计算一遍。

算法用法

因为有很多功能都用不到,所以很多字段和函数我都直接不用无视了。有两个我们需要关心的结构,fov_private_data_type和fov_settings_type。fov_private_data_type用来记录这次查询所需要用到的数据,例如起点半径以及fov_settings_type和我们项目自己写的类指针(观察者,用于查看和处理每次遍历到的格子)。fov_settings_type则表示每次计算的策略,需要自己写一个opaque和apply方法,用于返回是否视野阻挡,以及记录最终哪些格子可见。fov_settings_type的其他字段我自己只用到了shape和opaque_apply,用于定义视野计算的形状,opaque_apply用来决定我们是否观察和处理算法遍历到的视野阻挡点

战争迷雾渲染

当我们想渲染场景上的战争迷雾时,我们需要根据当前地图大小生成一张等比例的小图。出于性能考虑当时,我们战争迷雾用的格子是64x64,意味着如果地图大小是12800x12800,则需要生成200x200的战争迷雾小图作为RenderTarget简称RT。用迷雾算法标记出RT上哪写点可不可见。最终处理完用这张RT和全屏shader去绘制全屏(想象下画一张贴图贴满整个屏幕,所以其实你看到的迷雾只是你那一屏幕而已),这一步需要在整个场景渲染完做,这个时候可以根据场景的深度信息,算出每个像素的世界坐标,如果是地形的话那么它的深度值会比较小,所以先区分出是地形还是地形外,地形的话则根据深度值算出它所处的世界坐标,然后根据世界坐标转换成RT的UV坐标(直接世界坐标除以世界地图大小就是RT的UV坐标了),这样就能知道该世界坐标对应的像素是亮的还是暗的了。可以直接渲染迷雾颜色。至此战争迷雾其实已经可以在屏幕上看到了,初见形态,但是有个问题就是明亮交接处会有一些生硬,是直接过渡的。如果想平滑过渡的话,可以用高斯模糊的算子或者其他算子做卷积,我这里用的是热浪扭曲中提到的可增长泊松分布去做,效果还是不错的,采样周围的12个像素做平滑。

结尾

如果只有一个单位需要计算视野迷雾的话,只要半径不要特别大,一般性能都还能接受,可以实时计算。但如果有多个单位需要计算视野,可以考虑根据起点和半径缓存每次的计算结果,用内存来换性能。因为最终我们是想计算整张地图哪些地方是亮的,那么意味着我们只需要处理每一帧位置有发生改变的单位,将其原本的视野移除,再重新计算视野。这样没动的单位性能就能节省下来

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值