最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

three.js開發(fā)3d地圖的實(shí)現(xiàn)示例

 更新時(shí)間:2023年07月23日 12:34:17   作者:evomap  
本文主要介紹了three.js開發(fā)3d地圖的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

公司要做智慧消防樓層可視化,需要用到web3d,開源的引擎中先研究了cesium三維地球,但cesium做樓層感覺是大材小用,而且體驗(yàn)也不好,最終選用的是功能強(qiáng)大、更適合小型場(chǎng)景的three。

three是圖形引擎,而web二維三維地圖都是基于圖形引擎的,所以拿three來(lái)開發(fā)需求簡(jiǎn)單的三維地圖應(yīng)用是沒(méi)什么問(wèn)題的。

1.坐標(biāo)轉(zhuǎn)換

實(shí)際地理坐標(biāo)為經(jīng)度、緯度、高度,而three.js使用的是右手坐標(biāo)系x、y、z,本來(lái)考慮的是將經(jīng)緯度坐標(biāo)轉(zhuǎn)換成墨卡托,再去和three的坐標(biāo)系對(duì)應(yīng)。而實(shí)際項(xiàng)目中,經(jīng)緯度轉(zhuǎn)墨卡托后,墨卡托的值太大,對(duì)應(yīng)到three坐標(biāo)系中,坐標(biāo)距離原點(diǎn)太遠(yuǎn),用戶交互后,會(huì)有精度損失,于是先定義一個(gè)中間點(diǎn),然后將墨卡托的結(jié)果減去這個(gè)中間點(diǎn)的值。(我自己是經(jīng)度對(duì)應(yīng)z軸,緯度對(duì)應(yīng)x軸,高度對(duì)應(yīng)y軸)

function lonlatToMercator(lon,lat,height){
    var z = height ? height:0;
    var x = (lon / 180.0) * 20037508.3427892;
    var y = (Math.PI / 180.0) * lat;
    var tmp = Math.PI / 4.0 + y / 2.0;
    y = 20037508.3427892 * Math.log(Math.tan(tmp)) / Math.PI;
    return {x: x,y: y,z: z};
}
var center = lonlatToMercator(lonVal,latVal,heightVal);
function lonlatToThree(lon,lat,height){
    var z = height? height:0;
    var x = (lon / 180.0) * 20037508.3427892;
    var y = (Math.PI / 180.0) * lat;
    var tmp = Math.PI / 4.0 + y / 2.0;
    y = 20037508.3427892 * Math.log(Math.tan(tmp)) / Math.PI;
    var result = {
        x: x - center.x,
        y: y - center.y,
        z: z -center.z
    };
    return result;
}

2.加載模型

three.js支持多種模型加載,我是用草圖大師建的模型,于是直接轉(zhuǎn)成collada模型,然后使用three的collada模型加載器加載模型。因?yàn)橐蛅hree.js對(duì)應(yīng),而模型默認(rèn)位于x-z軸上,所以要進(jìn)行模型翻轉(zhuǎn)等操作。

3.創(chuàng)建標(biāo)注

three中,創(chuàng)建始終朝向相機(jī)的POI標(biāo)注可以使用Sprite類,也可以使用canvas創(chuàng)建圖標(biāo)+文字類型的圖形作為Sprite的紋理。sprite默認(rèn)是有一個(gè)固定的3d長(zhǎng)度,相機(jī)距離sprite越近,sprite在屏幕上越大,反之越小,過(guò)大或者過(guò)小都會(huì)導(dǎo)致sprite的canvas失真模糊,解決方案是計(jì)算出該點(diǎn)的屏幕像素與3d坐標(biāo)長(zhǎng)度的比值,然后將sprite縮放到一個(gè)合適的3d長(zhǎng)度。

var position = sprite.position;
var canvas = sprite.material.map.image;
if(canvas){
	var poiRect = {w:canvas.width,h:canvas.height};
	var scale = getPoiScale(position,poiRect);
	sprite.scale.set(scale[0],scale[1],1.0);
}
function getPoiScale(position,poiRect){
    if(!position) return;
    var distance = camera.position.distanceTo(position);
    var top = Math.tan(camera.fov / 2 * Math.PI / 180)*distance;    //camera.fov 相機(jī)的拍攝角度
    var meterPerPixel = 2*top/container.clientHeight;
    var scaleX = poiRect.w * meterPerPixel;
    var scaleY = poiRect.h * meterPerPixel;
    return [scaleX,scaleY,1.0];
}

4.標(biāo)注碰撞

創(chuàng)建標(biāo)注之后,放縮時(shí)難免會(huì)出現(xiàn)標(biāo)注相互遮蓋的情況,這樣既影響美觀也會(huì)遮蓋住地圖信息,這里需要檢測(cè)標(biāo)注間的遮蓋,顯示和不顯示一些標(biāo)注。

這里主要是將標(biāo)注點(diǎn)3d坐標(biāo)轉(zhuǎn)成屏幕坐標(biāo),再根據(jù)sprite中canvas的長(zhǎng)度和高度,就可以知道sprite在屏幕的矩形范圍。接下來(lái)就是計(jì)算各個(gè)標(biāo)注點(diǎn)sprite的矩形相交了。

 var sprite1 = {x:x1,y:y1,w:w1,h:h1};	//sprite1左下角x,y,寬度、高度
    var sprite2 = {x:x2,y:y2,w:w2,h:h2};	//sprite2左下角x,y,寬度、高度
  //檢測(cè)兩個(gè)標(biāo)注sprite是否碰撞
    function isPOIRect(sprite1,sprite2){
    	var x1 = sprite1.x,y1=sprite1.y,w1=sprite1.w,h1=sprite1.h;
    	var x2 = sprite2.x,y2=sprite2.y,w1=sprite2.w,h1=sprite2.h;
        if (x1 >= x2 && x1 >= x2 + w2) {
            return false;
        } else if (x1 <= x2 && x1 + w1 <= x2) {
            return false;
        } else if (y1 >= y2 && y1 >= y2 + h2) {
            return false;
        } else if (y1 <= y2 && y1 + h1 <= y2) {
            return false;
        }else{
            return true;
        }
    }

5.加載設(shè)備

創(chuàng)建設(shè)備,我同樣使用的是Sprite類,跟創(chuàng)建標(biāo)注類似,放縮之后,sprite在屏幕上的大小保持不變。

6.設(shè)備點(diǎn)擊

raycaster類用于在3d中被鼠標(biāo)選中的物體,這同樣可以選中sprite對(duì)象,于是用此方法模擬設(shè)備的點(diǎn)擊。其中deviceGroup是保存所有設(shè)備sprite的object3d對(duì)象。

 function onDocumentMouseDown(e) {
        e.preventDefault();
        mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
        //新建一個(gè)三維單位向量 假設(shè)z方向就是0.5
        //根據(jù)照相機(jī),把這個(gè)向量轉(zhuǎn)換到視點(diǎn)坐標(biāo)系
        var vector = new THREE.Vector3(mouse.x, mouse.y,0.5).unproject(camera);
        //在視點(diǎn)坐標(biāo)系中形成射線,射線的起點(diǎn)向量是照相機(jī), 射線的方向向量是照相機(jī)到點(diǎn)擊的點(diǎn),這個(gè)向量應(yīng)該歸一標(biāo)準(zhǔn)化。
        var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
        //射線和模型求交,選中一系列直線
        var intersects = raycaster.intersectObjects([deviceGroup],true);          
        if (intersects.length > 0) {
            var intersected = intersects[0].object;
            if(intersected instanceof THREE.Sprite){
            	//點(diǎn)擊到設(shè)備圖標(biāo)
            }  
        }
    }

7.彈出框

設(shè)備點(diǎn)擊之后,一般都會(huì)以彈出框形式展示設(shè)備的具體信息,這里需要先定義彈出框的樣式,然后將彈出點(diǎn)設(shè)備的三維坐標(biāo)轉(zhuǎn)換成屏幕坐標(biāo),設(shè)置一定的偏移量,再將彈出框放到偏移后的屏幕位置上。然后每次更改相機(jī),重新計(jì)算彈出框的位置。

//three世界坐標(biāo)轉(zhuǎn)為屏幕坐標(biāo)
function threeToScreen(position,camera){
    var worldVector = new THREE.Vector3(
            position.x,
            position.y,
            position.z
    );
    var standardVector = worldVector.project(camera);//世界坐標(biāo)轉(zhuǎn)標(biāo)準(zhǔn)設(shè)備坐標(biāo)
    var a = window.innerWidth / 2;
    var b = window.innerHeight / 2;
    var x = Math.round(standardVector.x * a + a);//標(biāo)準(zhǔn)設(shè)備坐標(biāo)轉(zhuǎn)屏幕坐標(biāo)
    var y = Math.round(-standardVector.y * b + b);//標(biāo)準(zhǔn)設(shè)備坐標(biāo)轉(zhuǎn)屏幕坐標(biāo)
    return {
        x: x,
        y: y
    };
}

8.設(shè)備動(dòng)畫

簡(jiǎn)單設(shè)備動(dòng)畫可以通過(guò)更改設(shè)備的材質(zhì)、大小、位置來(lái)實(shí)現(xiàn),比如通過(guò)定時(shí)更改設(shè)備的材質(zhì)來(lái)實(shí)現(xiàn)設(shè)備圖標(biāo)的閃爍。

項(xiàng)目中要模擬火情,因此花了些時(shí)間網(wǎng)上參考并用粒子系統(tǒng)做了個(gè)火焰動(dòng)畫,這里先用一個(gè)循環(huán)通過(guò)THREE.Vector3對(duì)象創(chuàng)建構(gòu)成火焰的全部的點(diǎn),放到THREE.Geometry對(duì)象的vertices中;再使用canvas創(chuàng)建火焰的紋理圖形,傳給THREE.PointsMaterial對(duì)象(并設(shè)置材質(zhì)透明transparent:true和加法混合THREE.AddictiveBlending),最后以前面的THREE.Geometry和THREE.PointsMaterial創(chuàng)建THREE.Points對(duì)象,完成該火焰粒子系統(tǒng)的初始化。

每個(gè)粒子都有單獨(dú)的坐標(biāo),最后用一定的規(guī)律驅(qū)動(dòng)粒子的移動(dòng)達(dá)到動(dòng)畫的效果。

9.鼠標(biāo)繪制

在3d中,鼠標(biāo)的位置對(duì)應(yīng)到三維坐標(biāo)中是一條射線,因此需要添加繪制平面,點(diǎn)擊時(shí)獲取鼠標(biāo)和繪制平面的交點(diǎn),作為繪制點(diǎn)。繪制時(shí)監(jiān)聽鼠標(biāo)的單擊和移動(dòng)事件。

繪制線時(shí),鼠標(biāo)點(diǎn)擊和移動(dòng)時(shí),直接更改線的geometry中的vertices;繪制面時(shí),不僅僅要更改vertices還要計(jì)算所有頂點(diǎn)組合的三角面(我使用的是Earcut.js),作為geometry的faces,最后創(chuàng)建一個(gè)以這個(gè)geometry為幾何形狀的多邊形mesh。

//positions 三維坐標(biāo)數(shù)組[[x,y,z],[x,y,z],...]
function createPolygon(positions){
	var shapePositons = [];
     for(var i=0;i<positions.length;i++){
	    var position = positions[i];
	    shapePositons.push(new THREE.Vector3(position[0],position[1],position[2]));
    }
	var data = [];
	for(var i=0;i<positions.length;i++){
	    var position = positions[i];
	    data.push(position[0],position[1]);
    }
	var faces = [];
	var triangles = Earcut.triangulate(data);
	if(triangles && triangles.length != 0){
		for(var i=0;i<triangles.length;i++){
			var length = triangles.length;
			if(i%3==0 && i < length-2){
				faces.push(new THREE.Face3(triangles[i],triangles[i+1],triangles[i+2]));
			}
		}
	}
    var geometry = new THREE.BufferGeometry();
    geometry.vertices = shapePositons;
    geometry.faces = faces;
    var mesh = new THREE.Mesh(geometry,material);
    return mesh;
}

到此這篇關(guān)于three.js開發(fā)3d地圖的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)three.js 3d地圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JS打印gridview實(shí)現(xiàn)原理及代碼

    JS打印gridview實(shí)現(xiàn)原理及代碼

    打印gridview對(duì)于一些童鞋們真的是很陌生啊,不過(guò)沒(méi)有關(guān)系,因?yàn)楸疚牡某霈F(xiàn),或讓你茅塞頓開,好了話不多說(shuō),感興趣的朋友可以了解下,或許對(duì)你學(xué)習(xí)js高級(jí)知識(shí)有所幫助
    2013-02-02
  • canvas繪制萬(wàn)花筒效果(代碼分享)

    canvas繪制萬(wàn)花筒效果(代碼分享)

    本文主要分享了canvas繪制萬(wàn)花筒效果的示例代碼。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • 微信小程序用戶授權(quán),以及判斷登錄是否過(guò)期的方法

    微信小程序用戶授權(quán),以及判斷登錄是否過(guò)期的方法

    這篇文章主要介紹了微信小程序用戶授權(quán)及判斷登錄是否過(guò)期,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • JS效率個(gè)人經(jīng)驗(yàn)談(8-15更新),加入range技巧

    JS效率個(gè)人經(jīng)驗(yàn)談(8-15更新),加入range技巧

    JS效率個(gè)人經(jīng)驗(yàn)談(8-15更新),加入range技巧...
    2007-01-01
  • JavaScript+Node.js寫一款markdown解析器

    JavaScript+Node.js寫一款markdown解析器

    這篇文章主要介紹了利用JavaScript和Node.js寫一款markdown解析器,首先編寫getHtml函數(shù),傳入markdown文本字符串,下面更多詳細(xì)內(nèi)容,需要的小伙伴可以參考一下
    2022-02-02
  • H5實(shí)現(xiàn)仿flash效果的實(shí)現(xiàn)代碼

    H5實(shí)現(xiàn)仿flash效果的實(shí)現(xiàn)代碼

    這篇文章主要介紹了H5實(shí)現(xiàn)仿flash效果的實(shí)現(xiàn)代碼的相關(guān)資料,希望通過(guò)本文能幫助到大家,實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-09-09
  • js調(diào)用AJAX時(shí)Get和post的亂碼解決方法

    js調(diào)用AJAX時(shí)Get和post的亂碼解決方法

    在使用"get"時(shí),抓取的頁(yè)面最后加上編碼類型,在使用post時(shí)用vbscript解決了編碼問(wèn)題,具體實(shí)現(xiàn)如下,有類似情況的朋友可以參考下哈
    2013-06-06
  • JS中的事件委托實(shí)例淺析

    JS中的事件委托實(shí)例淺析

    這篇文章主要介紹了JS中的事件委托,結(jié)合實(shí)例形式簡(jiǎn)單分析了javascript事件委托的概念、功能、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2018-03-03
  • 淺析JavaScript中的call、apply和bind方法

    淺析JavaScript中的call、apply和bind方法

    JavaScript中的call、apply和bind方法是用于改變函數(shù)執(zhí)行上下文和預(yù)先設(shè)置參數(shù)的強(qiáng)大工具,它們?cè)诰帉懣删S護(hù)和優(yōu)雅的代碼時(shí)起到了重要的作用,本文將介紹這些方法的原理和使用場(chǎng)景,并展示如何將它們應(yīng)用于你的代碼中,使其更加漂亮
    2023-06-06
  • JS組件系列之Bootstrap table表格組件神器【二、父子表和行列調(diào)序】

    JS組件系列之Bootstrap table表格組件神器【二、父子表和行列調(diào)序】

    本文結(jié)合Bootstrap table的父子表和行列調(diào)序的用法再來(lái)介紹下它稍微高級(jí)點(diǎn)的用法。對(duì)bootstrap表格組件相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05

最新評(píng)論

什邡市| 鹤峰县| 河北区| 林芝县| 醴陵市| 河曲县| 益阳市| 马鞍山市| 泸定县| 资阳市| 霍山县| 蒲江县| 班戈县| 澄迈县| 平顺县| 虎林市| 肥城市| 全椒县| 体育| 贵州省| 黄骅市| 黄大仙区| 资兴市| 英山县| 沾益县| 年辖:市辖区| 麻城市| 宜宾县| 阳信县| 陆河县| 平凉市| 利辛县| 宁武县| 抚松县| 民乐县| 汨罗市| 柏乡县| 高阳县| 巫溪县| 彩票| 华容县|