概述
算法简介
算法链接: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个像素做平滑。
结尾
如果只有一个单位需要计算视野迷雾的话,只要半径不要特别大,一般性能都还能接受,可以实时计算。但如果有多个单位需要计算视野,可以考虑根据起点和半径缓存每次的计算结果,用内存来换性能。因为最终我们是想计算整张地图哪些地方是亮的,那么意味着我们只需要处理每一帧位置有发生改变的单位,将其原本的视野移除,再重新计算视野。这样没动的单位性能就能节省下来
最后
以上就是幸福凉面为你收集整理的视野迷雾算法的全部内容,希望文章能够帮你解决视野迷雾算法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复