前言
搞定地图画线之后,接下来就是绘制面和体了:
面作为地图渲染的基本元素之一,在地图中可以代表各种形式的区域,例如海面、绿地等。面数据通常以离散点串形式存储,因此渲染时最关注的是如何将其展现为闭合的图形。
体可以理解为带有高度的面,在地图中代表各种建筑,通常是由其顶部面数据和高度数据处理得到。
本文记录了绘制面和体的流程以及解决闪烁问题的方案。
绘制多边形区域面
面数据通常以离散点串形式存储,面的绘制与线的绘制原理类似。渲染的基本单位是三角形,线是通过扩展线宽构造三角形后渲染,而面是通过将多边形拆分为多个三角形后渲染。
拆分为三角形的过程被称为三角剖分,常用的三角剖分算法是耳切法(Ear Clipping),比较成熟的方案是Mapbox的earcut,对于有 [公式] 个顶点的多边形,其时间复杂度为 [公式] ,值得注意的是,三角剖分的解可能是不唯一的,任何一种剖分方式都能够渲染得到面,但细小的三角形更容易使面中的同一像素绘制多次,造成过度绘制(Overdraw),因此根据多边形特征做一些剖分次序的调整可以作为一个优化点。
剖分完成的多边形区域,在指定了每一个顶点的颜色之后,就能绘制得到纯色的面。和道路线的Z-fighting问题类似,区域面也需要处理同一高度叠加显示的问题。同时,二维的道路线和区域面整体也处于同一个高度上,因此也需要统一考虑层级关系,将所有的道路线置于区域面之上。统一处理完成就可以得到二维的地图底板了。
绘制多边形建筑体
二维地图底板完成后,就轮到地图上的楼块建筑了。为了减少数据量,通常的存储方式是顶面点串和其对应的拔起高度,在渲染时增加顶点构成闭合体。
顶面渲染流程和闭合区域面一致,侧面则是根据楼高进行绘制,在每两个相邻顶点间渲染一个矩形从而构成闭合体的侧面,为了减少绘制次数通常只绘制朝向外侧的侧面,底面在正常视角下看不到,也可以酌情选择是否绘制。
建筑体的渲染只比区域面多了拔高产生的侧面,逻辑比较简单,处理得到所有三角形数据后,配置好顶点颜色即可完成渲染。
奇怪的建筑体Z-fighting问题
理论上来说,建筑体数据的顶面通常不会重合,因此在拔起渲染后不会出现Z-Fighting问题,但奇怪的是,渲染后仍然发现一些体存在侧面闪烁问题。通过全链路的排查,才查出是多边形数据的问题。
三角剖分在使用时有一个前置条件:使用对象必须为简单多边形,即多边形中的任何两条边仅可以在顶点处相交。下图(a)多边形为满足定义的简单多边形,图(b)多边形边01和23在非顶点处相交,因此是非简单多边形。
对于非简单多边形,使用三角剖分只能得出较为满意的结果,但不能保证其正确性。从下图四个顶点构成的非简单多边形的三角剖分结果可以看到,多边形渲染时会丢失顶点并且产生错误的三角形,无法还原数据真实情况。
按照这种想法对现有数据进行了边的相交检测,确实存在一小部分的多边形不是简单多边形。而体元素的立面拔起是按照原始数据在每一组相邻顶点间绘制矩形,因此会产生问题。以上述的非简单多边形(b)为例,边12拔起生成矩形1245,边23拔起生成矩形2364,两个侧面矩形在面1245上完全重合,当外立面贴上不同的纹理后就会产生Z-Fighting现象。同时,因为外立面仅仅绘制朝向外侧一面,面1245在对侧查看时会消失,产生非常诡异的效果。
针对这个问题,比较容易想到的解决方法主要是以下三个:
1、直接过滤,简单粗暴。
2、根据多边形计算外接矩形,减少细节
3、根据三角剖分结果剔除多余顶点,重新生成简单多边形
以上三个方案对于多边形的细节保留由少到多,但并不是完全还原真实数据。尤其对于一些复杂建筑,某一个面的错误会导致最终拼装得到的渲染结果错误。因此比较理想的方式是修复非简单多边形,将其分解为多个简单多边形,分别渲染还原细节。
简单多边形的判定与修复
根据简单多边形的定义,很容易想到采用暴力解法进行判定:一个 边形有 条边,每条边只需要和其他的 条不相邻边判断是否相交即可达到目的,其时间复杂度为 ,对于仅需要进行一遍数据清洗的静态数据来说已经足够。但对于需要实时处理的动态数据来说,其需要遍历所有组合,尤其对于可能仅存在少量相交点的情况,冗余计算太多,因此可以引入时间复杂度更低的相交判定算法进行处理。
对于一个非简单多边形,在分解为多个简单多边形后,绘制所有面积不为0的图形就可以了。这种方案可以最大限度还原原始数据,并且规避闪烁问题。
小结
解决了数据造成的闪烁问题后,就可以在建筑的侧面和顶面使用纯色或者纹理贴图进行装饰,搭配天空盒和一些纯色元素去装饰,已经可以简略模仿出城市的效果。但在当前的建筑拔起渲染方式下,只能通过贴图的形式去表达建筑细节,如果需要更精细的表达效果,例如玻璃窗体结构、楼顶设施等,需要增加额外的三角形去进行呈现。
作者:程序员阿Tu链接:https://zhuanlan.zhihu.com/p/...
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。