摘要:畢業(yè)兩年,一直在地圖相關的公司工作,雖然不是出身,但是也對地圖有些耳濡目染最近在看的東西,就拿做了一個關于的三維數(shù)據(jù)渲染的練手。
畢業(yè)兩年,一直在地圖相關的公司工作,雖然不是 GIS 出身,但是也對地圖有些耳濡目染;最近在看 WebGl 的東西,就拿 MapboxGL 做了一個關于 WebGL 的三維數(shù)據(jù)渲染的 DEMO 練手。
首先大致看了一下 MapboxGL 的 GLGS 到圖層的一個結構:
大體就是先做 WebGl 的 Shader 代碼放進 Painter(WebGL 的 Context 就在這個對象里面) 里面,然后通過 Source 層去加載處理需要的數(shù)據(jù)(包括矢量和柵格數(shù)據(jù)),把數(shù)據(jù)通過 Tile 對象傳進 Render 里面,去做一些 WebGL 的數(shù)據(jù)處理和渲染,然后扔進 Tile 里面?zhèn)魅氲?Layer 層,最后就是一些樣式和事件的管理。
// 序列化瓦片地址,將數(shù)據(jù)瓦片的 xyz 坐標計算出來 let url = normalizeURL( tile.coord.url(this.tiles, null, this.scheme), this.url, this.tileSize ); ... // 用 MapboxGl 封裝的獲取二進制數(shù)據(jù)格式的 Ajax 請求拿到二進制數(shù)據(jù) tile.request = ajax.getArrayBuffer(url, done.bind(this)); ... // 將數(shù)據(jù)進行轉(zhuǎn)碼處理成 JS 對象,并傳遞給 tile tile.pixelObj = pixelObj; // 處理好的數(shù)據(jù) ...
const divisions = 257; let vertexPositionData = new Float32Array(divisions * divisions * 3); const pixels = pixelObj.pixels[0]; if (coord.vertexPositionData) { // 做了緩存優(yōu)化 console.log("緩存", "coord"); vertexPositionData = coord.vertexPositionData; } else { console.time("vertex"); // 全數(shù)據(jù)量 for (let i = 0; i < divisions; ++i) { for (let j = 0; j < divisions; ++j) { const bufferLength = (i * divisions + j) * 3; let dem = parseInt(pixels[bufferLength / 3]); if (!dem || dem === -3) { // 對于無效數(shù)據(jù)給一個默認值(PS: DEM 高程數(shù)據(jù)質(zhì)量不高 ) dem = -1000; } vertexPositionData[bufferLength] = j * SCALE; vertexPositionData[bufferLength + 1] = i * SCALE * 1; vertexPositionData[bufferLength + 2] = dem; } } // 計算數(shù)據(jù)處理的耗時,優(yōu)化的時候要用 console.timeEnd("vertex"); coord.vertexPositionData = vertexPositionData; } const indexData = getIndex(divisions); const FSIZE = vertexPositionData.BYTES_PER_ELEMENT; const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertexPositionData, gl.STATIC_DRAW); const aPosiLoc = gl.getAttribLocation(gl.program, "a_Position"); gl.vertexAttribPointer(aPosiLoc, 3, gl.FLOAT, false, FSIZE * 3, 0); gl.enableVertexAttribArray(aPosiLoc); // 設置索引 const indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); // https://stackoverflow.com/questions/28324162/webgl-element-array-buffers-not-working gl.getExtension("OES_element_index_uint"); gl.drawElements(gl.TRIANGLES, indexData.length, gl.UNSIGNED_INT, 0); ... // 生成索引,WebGL 的渲染有兩種方式,一種是 drawElements,一種是 drawArray,我們這里采用第一種 function getIndex(divisions) { if (drawLerc3D.indexData) { return drawLerc3D.indexData; } console.time("獲取索引"); const indexData = []; // 這個是全數(shù)據(jù)量渲染 // for (let row = 0; row < divisions - 1; ++row) { // for (let i = 0; i < divisions; ++i) { // const base = row * divisions + i; // if (i < divisions - 1) { // indexData.push(base); // indexData.push(base + 1); // indexData.push(base + divisions); // indexData.push(base + 1); // indexData.push(base + divisions); // indexData.push(base + divisions + 1); // } // } // } // 這是一半數(shù)據(jù)(PS: 這是為了優(yōu)化,犧牲一些精度) for (let row = 0; row < divisions - 2; row += 2) { for (let i = 0; i < divisions; i += 2) { const base = row * divisions + i; if (i < divisions - 2) { indexData.push(base); indexData.push(base + 2); indexData.push(base + divisions * 2); indexData.push(base + 2); indexData.push(base + divisions * 2); indexData.push(base + divisions * 2 + 2); } } } console.timeEnd("獲取索引"); drawLerc3D.indexData = new Uint32Array(indexData); return drawLerc3D.indexData; }
vertex shader
// 視角矩陣 uniform mat4 u_matrix; // 頂點位置數(shù)據(jù) attribute vec3 a_Position; // 紋理數(shù)據(jù),貼圖衛(wèi)星影像 attribute vec2 a_texCoord; varying vec2 v_texCoord; // 高程數(shù)據(jù) varying float dem; void main(){ dem = a_Position.z; gl_Position = u_matrix * vec4(a_Position.x, a_Position.y, dem * 32.0, 1.0); v_texCoord = a_texCoord; }
fragment shader
// precision lowp float; // uniform float u_brightness_low; // uniform float u_brightness_high; // 顏色 // varying vec3 v_Color; varying float dem; // 紋理 uniform sampler2D u_image; varying vec2 v_texCoord; // 根據(jù)不同高程取不同顏色 vec4 getColor() { // 顏色數(shù)組 const int COLORS_SIZE = 11; vec3 colors[COLORS_SIZE]; // 對 dem 進行歸一化 float n_dem = -2.0 * (dem / 6000.0 - 0.5); const float MINDEM = -1.0; const float MAXDEM = 1.0; const float STEP = (MAXDEM - MINDEM) / float(COLORS_SIZE - 1); int index = int(ceil((n_dem - MINDEM) / STEP)); colors[10] = vec3(0.3686274509803922,0.30980392156862746,0.6352941176470588); colors[9] = vec3(0.19607843137254902,0.5333333333333333,0.7411764705882353); colors[8] = vec3(0.4, 0.7607843137254902,0.6470588235294118); colors[7] = vec3(0.6705882352941176,0.8666666666666667,0.6431372549019608); colors[6] = vec3(0.9019607843137255,0.9607843137254902,0.596078431372549); colors[5] = vec3(1.0, 1.0, 0.7490196078431373); colors[4] = vec3(0.996078431372549,0.8784313725490196,0.5450980392156862); colors[3] = vec3(0.9921568627450981,0.6823529411764706,0.3803921568627451); colors[2] = vec3(0.9568627450980393,0.42745098039215684,0.2627450980392157); colors[1] = vec3(0.8352941176470589,0.24313725490196078,0.30980392156862746); colors[0] = vec3(0.6196078431372549,0.00392156862745098,0.25882352941176473); if(index > 10){ return vec4(0.3, 0.3, 0.9, 0.5); } if(index < 0){ index = 0; } for (int i = 0; i < COLORS_SIZE; i++) { if (i == index) return vec4(colors[i], 1.0); } } void main(){ // 用顏色渲染 DEM 數(shù)據(jù),和紋理二選一 gl_FragColor = getColor(); // 用紋理(衛(wèi)星影像)渲染效果 gl_FragColor = texture2D(u_image, v_texCoord / 256.0 / 32.0); }
map.addSource("DEMImgSource", { //高程數(shù)據(jù) "type": "DEM3D", "tiles": [ "http://xxx.xxx.xxx.xxx/{x}/{y}/{z}", ], "tileSize": 512, // 谷歌瓦片地址,用來渲染紋理貼圖 "rasterUrl": "http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}", // 高德的 // "rasterUrl": "https://webst04.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}" }); map.addLayer({ // layer "id": "DEMlayer", "type": "DEM3D", "source": "DEMImgSource" });最終的渲染效果(顏色渲染):
因為數(shù)據(jù)量實在是太大(一般整張3D屏幕渲染需要40張瓦片,每張都有256*256個數(shù)據(jù)點),一開始沒有做優(yōu)化的時候非常卡,根本無法進行地圖拖動和縮放,后來將數(shù)據(jù)進行緩存,頂點信息進行精簡,瓦片大小進行放大(一屏幕只需要20張數(shù)據(jù)片渲染)得到的效果就很不錯了,拖動和縮放基本比較流暢,體驗和正常地圖差別不大。
不得不說好像還是顏色渲染的視覺效果更(yao)好(yan)一(jian)些(huo)~
對于 WebGL 方向上的探索一些大公司也有一些成果:
高德 Loca:https://lbs.amap.com/api/java...
百度 Echarts: http://echarts.baidu.com/exam...
UBER: https://deck.gl/
等等,所以對于 WebGL 的前景個人覺得在數(shù)據(jù)可視化、高精地圖(無人駕駛)等方面還是有很多價值的~
第一次寫文章,很多地方可能沒有解釋清楚,歡迎拍磚~
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/52917.html
摘要:畢業(yè)兩年,一直在地圖相關的公司工作,雖然不是出身,但是也對地圖有些耳濡目染最近在看的東西,就拿做了一個關于的三維數(shù)據(jù)渲染的練手。 畢業(yè)兩年,一直在地圖相關的公司工作,雖然不是 GIS 出身,但是也對地圖有些耳濡目染;最近在看 WebGl 的東西,就拿 MapboxGL 做了一個關于 WebGL 的三維數(shù)據(jù)渲染的 DEMO 練手。 首先大致看了一下 MapboxGL 的 GLGS 到圖層...
摘要:畢業(yè)兩年,一直在地圖相關的公司工作,雖然不是出身,但是也對地圖有些耳濡目染最近在看的東西,就拿做了一個關于的三維數(shù)據(jù)渲染的練手。 畢業(yè)兩年,一直在地圖相關的公司工作,雖然不是 GIS 出身,但是也對地圖有些耳濡目染;最近在看 WebGl 的東西,就拿 MapboxGL 做了一個關于 WebGL 的三維數(shù)據(jù)渲染的 DEMO 練手。 首先大致看了一下 MapboxGL 的 GLGS 到圖層...
摘要:是一套庫,用來渲染地球,區(qū)域地圖,和多種要素,不需要安裝任何插件就能在支持最新標準的瀏覽器上運行,支持硬件加速,非常適合動態(tài)數(shù)據(jù)在圖層上的展示,是一個跨平臺,開源,非常有前途的表現(xiàn)層庫。 Cesiumjs 是一套javascript庫,用來渲染3D地球,2D區(qū)域地圖,和多種GIS要素,不需要安裝任何插件就能在支持最新HTML5標準的瀏覽器上運行,支持WebGL硬件加速,非常適合動態(tài)數(shù)據(jù)...
閱讀 2178·2021-11-24 09:38
閱讀 3242·2021-11-08 13:27
閱讀 3083·2021-09-10 10:51
閱讀 3143·2019-08-29 12:20
閱讀 663·2019-08-28 18:28
閱讀 3459·2019-08-26 11:53
閱讀 2706·2019-08-26 11:46
閱讀 1515·2019-08-26 10:56