騰訊位置服務(wù)基于 WebGL實(shí)現(xiàn)自定義柵格圖層踩坑實(shí)錄

以下內(nèi)容轉(zhuǎn)載自totoro的文章《WebGL-Y軸翻轉(zhuǎn)踩坑實(shí)錄》

作者:totoro

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

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

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

前言

自定義柵格圖層 是指用戶可以通過特定軟件,將自定義的圖像按照上文所述的方式切割為瓦片,并生成圖片,然后按照瓦片坐標(biāo)拼接形成地圖的圖層。常用于手繪地圖、衛(wèi)星圖、地形圖等。

案例背景

基于 WebGL 的地圖渲染API,實(shí)現(xiàn)自定義柵格圖層(將地圖切分為等大的正方形,并以圖片進(jìn)行拼接渲染)時(shí),為了節(jié)省紋理上傳的開銷,將柵格瓦片集中繪制到一張紋理上,然后繪制時(shí)根據(jù)瓦片各自的紋理坐標(biāo)取各自的紋理,大概示意圖如下:

image

瓦片根據(jù)加載的先后順序依次排列繪制到大紋理上,占位寬度一致,豎向排列。比如若瓦片大小為256px,那么瓦片1的位置為{x:0, y:0}, 瓦片2的位置為{x:0, y:256}。

然后出現(xiàn)了一系列問題:

  1. 瓦片錯(cuò)亂:瓦片1的位置顯示了瓦片4的內(nèi)容;
  2. 瓦片內(nèi)容倒置。

問題分析

根據(jù)調(diào)試定位,發(fā)現(xiàn)問題的根源在于Y軸翻轉(zhuǎn)。

問題1: Y軸翻轉(zhuǎn)是什么?為什么要翻轉(zhuǎn)?

先看看沒有任何處理的情況下如何繪制紋理,我們繪制瓦片的基本頂點(diǎn)模型是一個(gè)中心在原點(diǎn)的正方形,對(duì)于每個(gè)頂點(diǎn)坐標(biāo),需要映射到一個(gè)紋理坐標(biāo)(下圖左),傳給片元著色器,再使用 texture2D() 取紋理像素,這種情況下左上角頂點(diǎn)(-1,1)對(duì)應(yīng)的紋理坐標(biāo)為(0,0)

image

紋理坐標(biāo)系與頂點(diǎn)坐標(biāo)系的Y軸方向不同,進(jìn)行坐標(biāo)映射的時(shí)候會(huì)不方便,所以如果將紋理坐標(biāo)系的Y軸翻轉(zhuǎn)則能使坐標(biāo)映射更容易(上圖右)。

WebGL 也提供了相應(yīng)接口實(shí)現(xiàn)該功能, WebGLRenderingContext.pixelStorei() 是 WebGL 中用于描述像素存儲(chǔ)模式的函數(shù),其中 UNPACK_FLIP_Y_WEBGL 可以用于設(shè)置Y軸是否翻轉(zhuǎn):

// 1表示翻轉(zhuǎn),0表示不翻轉(zhuǎn)
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

問題2: 為什么Y軸翻轉(zhuǎn)會(huì)導(dǎo)致瓦片錯(cuò)亂呢?

如上文所述,首先需要通過 texImage2D 創(chuàng)建一個(gè)大紋理,然后使用 texSubImage2D 將瓦片繪制到大紋理上:

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

這個(gè)接口用于改變紋理中指定子區(qū)域的數(shù)據(jù),可以類比于 CanvasRenderingContext2D.drawImage() ,我們平常使用drawImage 時(shí)都是以左上角為原點(diǎn)進(jìn)行偏移,所以想象中的大紋理是如下圖所示的那樣,瓦片1的左上角對(duì)應(yīng)紋理坐標(biāo)(0, 1),左下角為(0, 0.75),以此類推。

image

但實(shí)際上Y軸翻轉(zhuǎn)并不只作用在片元著色器的紋理中,使用 texImage2D 創(chuàng)建大紋理時(shí)其像素存儲(chǔ)模式就已經(jīng)確定了,當(dāng)執(zhí)行texSubImage2D時(shí)也會(huì)對(duì)image的像素存儲(chǔ)位置進(jìn)行反轉(zhuǎn),其執(zhí)行過程是這樣:

image

所以實(shí)際上大紋理應(yīng)該長(zhǎng)如下這樣:

image

所以當(dāng)使用紋理坐標(biāo)左上角(0, 1)+左下角(0, 0.75)時(shí),我們?nèi)〉降氖峭咂?的紋理,最終導(dǎo)致了瓦片錯(cuò)亂。

問題3: 為什么瓦片會(huì)倒置?

正確取得紋理坐標(biāo)后,又出現(xiàn)了新的問題:

image

瓦片在屏幕上顯示出來是上下顛倒的,且這種情況只出現(xiàn)在chrome/firefox里,因?yàn)樵谶@兩個(gè)瀏覽器中我們使用了 createImageBitmap 將blob格式的圖片轉(zhuǎn)為了位圖,而在safari瀏覽器(不支持 createImageBitmap)中我們將blob格式轉(zhuǎn)為了Image 對(duì)象,最終導(dǎo)致了這種差異,所以我們從ImageBitmap 著手去定位問題原因。

ImageBitmap表示位圖圖像,用于在canvas中繪制圖像,相比較于Image 其延遲較低,因?yàn)樵趫?zhí)行texSubImage2DImage 繪制到紋理上時(shí)也會(huì)先將其轉(zhuǎn)為ImageBitmap

不論是在 canvas 里繪制2d圖像,還是在 WebGL 中創(chuàng)建紋理,當(dāng)使用圖像時(shí)瀏覽器會(huì)把圖像做一次解碼(decode)處理。這個(gè)解碼也就是把圖像的原始格式(比如 jpeg、png 等)統(tǒng)一轉(zhuǎn)換為位圖,即每個(gè)像素使用 RGB 或 RGBA 來描述。當(dāng)圖片尺寸比較大的時(shí)候,解碼也會(huì)有一定的消耗,而且這個(gè)耗時(shí)是同步的?!陡咝阅?WebGL —— 使用 ImageBitmap 提升紋理性能》(http://www.jiazhengblog.com/blog/2019/03/24/3407/)

同時(shí) WebGL 規(guī)范里對(duì) ImageBitmap 有一些特殊的描述,當(dāng)介紹 pixelStorei 的三個(gè)參數(shù):UNPACK_FLIP_Y_WEBGL、UNPACK_PREMULTIPLY_ALPHA_WEBGL、UNPACK_COLORSPACE_CONVERSION_WEBGL時(shí),明確說明了其對(duì)ImageBitmap 無效,只能在創(chuàng)建 ImageBitmap 的時(shí)候就進(jìn)行相應(yīng)設(shè)置:

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.

所以可以大膽猜測(cè),pixelStorei 所指定的像素存儲(chǔ)模式其實(shí)作用于將圖像解碼轉(zhuǎn)為位圖的預(yù)處理過程。當(dāng)我們直接將位圖繪制到紋理上時(shí)就沒有這個(gè)預(yù)處理過程了,所以UNPACK_FLIP_Y_WEBGL 參數(shù)失效了。

小結(jié)

  • UNPACK_FLIP_Y_WEBGL 參數(shù)用于設(shè)置紋理像素存儲(chǔ)模式中是否將Y軸翻轉(zhuǎn),翻不翻取決于你的頂點(diǎn)模型的坐標(biāo)系方向,適合自己就好。在我們的應(yīng)用場(chǎng)景里,頂點(diǎn)模型和圖像坐標(biāo)系是反的,所以需要將該參數(shù)設(shè)為1。
  • 使用 texSubImage2D 上傳圖片時(shí)同樣受到UNPACK_FLIP_Y_WEBGL 參數(shù)的影響。
  • 如果上傳的圖像是ImageBitmap對(duì)象,則在其創(chuàng)建時(shí)可通過 ImageBitmapOptions中的 imageOrientationpremultiplyAlpha、colorSpaceConversion 三個(gè)參數(shù)讓其與pixelStorei 中所設(shè)置的參數(shù)保持一致。

最終使用自定義柵格圖層實(shí)現(xiàn)手繪圖疊加到地圖上,完成效果如下:

image

產(chǎn)品推廣

騰訊位置服務(wù)已經(jīng)支持個(gè)性化圖層使用,如需接入請(qǐng)查看:個(gè)性化圖層編輯平臺(tái),更多示例與開發(fā)文檔,您也可以官網(wǎng)搜索個(gè)性化圖層查看?。。?/p>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容