腾讯地图基于 WebGL实现自定义栅格图层踩坑实录

以下内容转载自totoro的文章《WebGL-Y轴翻转踩坑实录》

作者:totoro

链接:https://blog.totoroxiao.com/webgl-flipY/

来源:https://blog.totoroxiao.com/

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

前言

自定义栅格图层 是指用户可以通过特定软件,将自定义的图像按照上文所述的方式切割为瓦片,并生成图片,然后按照瓦片坐标拼接形成地图的图层。常用于手绘地图、卫星图、地形图等。

案例背景

基于 WebGL 的地图渲染API,实现自定义栅格图层(将地图切分为等大的正方形,并以图片进行拼接渲染)时,为了节省纹理上传的开销,将栅格瓦片集中绘制到一张纹理上,然后绘制时根据瓦片各自的纹理坐标取各自的纹理,大概示意图如下:

image

瓦片根据加载的先后顺序依次排列绘制到大纹理上,占位宽度一致,竖向排列。比如若瓦片大小为256px,那么瓦片1的位置为{x:0, y:0}, 瓦片2的位置为{x:0, y:256}

然后出现了一系列问题:

  1. 瓦片错乱:瓦片1的位置显示了瓦片4的内容;
  2. 瓦片内容倒置。

问题分析

根据调试定位,发现问题的根源在于Y轴翻转。

问题1: Y轴翻转是什么?为什么要翻转?

先看看没有任何处理的情况下如何绘制纹理,我们绘制瓦片的基本顶点模型是一个中心在原点的正方形,对于每个顶点坐标,需要映射到一个纹理坐标(下图左),传给片元着色器,再使用 texture2D() 取纹理像素,这种情况下左上角顶点(-1,1)对应的纹理坐标为(0,0)

image

纹理坐标系与顶点坐标系的Y轴方向不同,进行坐标映射的时候会不方便,所以如果将纹理坐标系的Y轴翻转则能使坐标映射更容易(上图右)。

WebGL 也提供了相应接口实现该功能, WebGLRenderingContext.pixelStorei() 是 WebGL 中用于描述像素存储模式的函数,其中 UNPACK_FLIP_Y_WEBGL 可以用于设置Y轴是否翻转:

// 1表示翻转,0表示不翻转
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

问题2: 为什么Y轴翻转会导致瓦片错乱呢?

如上文所述,首先需要通过 texImage2D 创建一个大纹理,然后使用 texSubImage2D 将瓦片绘制到大纹理上:

// x, y 表示偏移量
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image);

这个接口用于改变纹理中指定子区域的数据,可以类比于 CanvasRenderingContext2D.drawImage() ,我们平常使用drawImage 时都是以左上角为原点进行偏移,所以想象中的大纹理是如下图所示的那样,瓦片1的左上角对应纹理坐标(0, 1),左下角为(0, 0.75),以此类推。

image

但实际上Y轴翻转并不只作用在片元着色器的纹理中,使用 texImage2D 创建大纹理时其像素存储模式就已经确定了,当执行texSubImage2D时也会对image的像素存储位置进行反转,其执行过程是这样:

image

所以实际上大纹理应该长如下这样:

image

所以当使用纹理坐标左上角(0, 1)+左下角(0, 0.75)时,我们取到的是瓦片4的纹理,最终导致了瓦片错乱。

问题3: 为什么瓦片会倒置?

正确取得纹理坐标后,又出现了新的问题:

image

瓦片在屏幕上显示出来是上下颠倒的,且这种情况只出现在chrome/firefox里,因为在这两个浏览器中我们使用了 createImageBitmap 将blob格式的图片转为了位图,而在safari浏览器(不支持 createImageBitmap)中我们将blob格式转为了Image 对象,最终导致了这种差异,所以我们从ImageBitmap 着手去定位问题原因。

ImageBitmap表示位图图像,用于在canvas中绘制图像,相比较于Image 其延迟较低,因为在执行texSubImage2DImage 绘制到纹理上时也会先将其转为ImageBitmap

不论是在 canvas 里绘制2d图像,还是在 WebGL 中创建纹理,当使用图像时浏览器会把图像做一次解码(decode)处理。这个解码也就是把图像的原始格式(比如 jpeg、png 等)统一转换为位图,即每个像素使用 RGB 或 RGBA 来描述。当图片尺寸比较大的时候,解码也会有一定的消耗,而且这个耗时是同步的。——《高性能 WebGL —— 使用 ImageBitmap 提升纹理性能》(http://www.jiazhengblog.com/blog/2019/03/24/3407/)

同时 WebGL 规范里对 ImageBitmap 有一些特殊的描述,当介绍 pixelStorei 的三个参数:UNPACK_FLIP_Y_WEBGLUNPACK_PREMULTIPLY_ALPHA_WEBGL、UNPACK_COLORSPACE_CONVERSION_WEBGL时,明确说明了其对ImageBitmap 无效,只能在创建 ImageBitmap 的时候就进行相应设置:

If the TexImageSource is an ImageBitmap, then these three parameters will be ignored. Instead the equivalent ImageBitmapOptions should be used to create an ImageBitmap with the desired format.

所以可以大胆猜测,pixelStorei 所指定的像素存储模式其实作用于将图像解码转为位图的预处理过程。当我们直接将位图绘制到纹理上时就没有这个预处理过程了,所以UNPACK_FLIP_Y_WEBGL 参数失效了。

小结

  • UNPACK_FLIP_Y_WEBGL 参数用于设置纹理像素存储模式中是否将Y轴翻转,翻不翻取决于你的顶点模型的坐标系方向,适合自己就好。在我们的应用场景里,顶点模型和图像坐标系是反的,所以需要将该参数设为1。
  • 使用 texSubImage2D 上传图片时同样受到UNPACK_FLIP_Y_WEBGL 参数的影响。
  • 如果上传的图像是ImageBitmap对象,则在其创建时可通过 ImageBitmapOptions中的 imageOrientationpremultiplyAlphacolorSpaceConversion 三个参数让其与pixelStorei 中所设置的参数保持一致。

最终使用自定义栅格图层实现手绘图叠加到地图上,完成效果如下:

[站外图片上传中...(image-a05067-1594007455770)]

产品推广

腾讯位置服务已经支持个性化图层使用,如需接入请查看:个性化图层编辑平台,更多示例与开发文档,您也可以官网搜索个性化图层查看!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354