three.js通過canvas實現球體世界平面地圖

概況如下
1、SphereGeometry實現自轉的地球;
2、THREE.CatmullRomCurve3實現球體線條地圖點確定;
3、THREE.Math.degToRadMath.sinMath.cos實現地圖經緯度與三位座標x,y,z之間的轉換;
4、MeshLine用於繪製線條;
5、canvas用於繪製球體世界地圖貼圖,通過THREE.CanvasTexture引入。

效果圖如下:
在這裏插入圖片描述
預覽地址:three.js通過canvas實現球體世界平面地圖

初始化場景、相機、渲染器,設置相機位置,初始化光源,光源採用HemisphereLight,設置光源位置爲場景中心位置,並將光源加入場景中。

// 初始化場景
var scene = new THREE.Scene();
// 初始化相機,第一個參數爲攝像機視錐體垂直視野角度,第二個參數爲攝像機視錐體長寬比,
// 第三個參數爲攝像機視錐體近端面,第四個參數爲攝像機視錐體遠端面
var camera = new THREE.PerspectiveCamera(20, dom.clientWidth / dom.clientHeight, 1, 100000);
// 設置相機位置,對應參數分別表示x,y,z位置
camera.position.set(0, 0, 200);
var renderer = new THREE.WebGLRenderer({
	  alpha: true,
	  antialias: true
});
// 設置光照
scene.add(new THREE.HemisphereLight('#ffffff', '#ffffff', 1));

設置場景窗口尺寸,並且初始化控制器,窗口尺寸默認與瀏覽器窗口尺寸保持一致,最後將渲染器加載到dom中。

// 設置窗口尺寸,第一個參數爲寬度,第二個參數爲高度
renderer.setSize(dom.clientWidth, dom.clientHeight);
// 初始化控制器
var orbitcontrols = new THREE.OrbitControls(camera,renderer.domElement);
// 將渲染器加載到dom中
dom.appendChild(renderer.domElement);</pre>
						</div>

通過canvas定義地球材質。

// canvas畫地圖函數,因爲性能問題,線條不再canvas中實現,w表示寬度,h表示高度,worldPos表示世界地圖經緯度信息
var createCanvas = function (w, h, worldPos) {
	var canvas = document.createElement('canvas');
    canvas.width = w;
    canvas.height = h;
    var context = canvas.getContext('2d');
    var centerX = w / 2;
    var centerY = h / 2;
    var average = w / 360;
    // 繪製背景顏色
    context.fillStyle = earthBallColor;
    context.fillRect(0, 0, w, h);
    // canvas中繪製地圖方法
    function canvasLineFun (childrenPosition) {
    	context.fillStyle = earthBallPlaneColor;
    	context.moveTo(centerX + childrenPosition[0][0] * average, centerY - childrenPosition[0][1] * average);
    	childrenPosition.forEach(function (posItem) {
    		context.lineTo(centerX + posItem[0] * average, centerY - posItem[1] * average);
    	})
    	context.closePath();
    	context.fill();
    }
    worldPos.forEach(function (item) {
    	canvasLineFun(item);
    })
    return canvas;
}

定義地球及其材質,地球通過SphereGeometry來實現,通過THREE.CanvasTexture來引入canvas創建的貼圖。

// 創建地球
earthBall = new THREE.Mesh(new THREE.SphereGeometry(earthBallSize, 50, 50), new THREE.MeshBasicMaterial({
    map: new THREE.CanvasTexture(createCanvas(2048, 1024, worldGeometry)),
    side: THREE.FrontSide
}));
scene.add(earthBall);

標記地點經緯度座標與三維x,y,z座標轉換方法。

// 經緯度轉換函數,longitude表示經度,latitude表示唯獨,radius表示球體半徑
var getPosition = function (longitude, latitude, radius) {
	// 將經度,緯度轉換爲rad座標
	var lg = THREE.Math.degToRad(longitude);
	var lt = THREE.Math.degToRad(latitude);
	var temp = radius * Math.cos(lt);
	// 獲取x,y,z座標
	var x = temp * Math.sin(lg);
	var y = radius * Math.sin(lt);
	var z = temp * Math.cos(lg);
	return {
		x: x,
		y: y,
		z: z
	}
}

繪製世界地圖線條方法

// 繪製世界地圖線條函數
var drawWorldLine = function (pos, identify) {
	var posArray = [];
	pos.forEach(function (item) {
		var pointPosition = getPosition(item[0] + 90, item[1], earthBallSize);
		posArray.push(new THREE.Vector3(pointPosition.x, pointPosition.y, pointPosition.z));
	})
	// 繪製的線條需要關閉,第二個參數默認爲false,表示不關閉
	var curve = new THREE.CatmullRomCurve3(posArray, true);
	var points = curve.getPoints(500);
	var geometry = new THREE.Geometry().setFromPoints(points);
	// 定義線條
	var line = new MeshLine();
	line.setGeometry(geometry);
	// 定義線條材質
	var material = new MeshLineMaterial({
		color: worldLineColor,
		lineWidth: worldLineWidth
	})
	// 繪製地圖
    lineGeometryObj['lineGeometry' + identify] = new THREE.Mesh(line.geometry, material);
	// 將地圖加入場景
    scene.add(lineGeometryObj['lineGeometry' + identify])
}

獲取世界地圖經緯度信息及計算繪製球體地圖參數方法。

// 獲取世界經緯度信息函數
var getWorldGeometry = function () {
	$.ajax({ 
     	type : "GET", //提交方式 
     	url : "./code/world.json",
     	async: false,
     	success : function(response) {//返回數據根據結果進行相應的處理 
     		worldGeometry = [];
     		// 繪製世界地圖
	    	response.features.forEach(function (worldItem, worldItemIndex) {
	    		var length = worldItem.geometry.coordinates.length;
	    		var multipleBool = length > 1 ? true : false;
	    		worldItem.geometry.coordinates.forEach(function (worldChildItem, worldChildItemIndex) {
	    			if (multipleBool) {
	    				// 值界可以使用的經緯度信息
	    				if (worldChildItem.length && worldChildItem[0].length == 2) {
	    					worldGeometry.push(worldChildItem);
	    				}
	    				// 需要轉換纔可以使用的經緯度信息
	    				if (worldChildItem.length && worldChildItem[0].length > 2) {
	    					worldChildItem.forEach(function (countryItem, countryItenIndex) {
	    						worldGeometry.push(countryItem);
	    					})
	    				}
	    			} else {
	    				var countryPos = null;
	    				if (worldChildItem.length > 1) {
	    					countryPos = worldChildItem;
	    				} else {
	    					countryPos = worldChildItem[0];
	    				}
	    				if (countryPos) {
	    					worldGeometry.push(countryPos);
	    				}
	    			}
	    		})
	    	})
     	} 
    })
}

球體地圖線條通過position值來實現位置的確認,動畫使用requestAnimationFrame來實現。

// 執行函數
var render = function () {
	scene.rotation.y -= 0.01;
    renderer.render(scene, camera);
    orbitcontrols.update();
    requestAnimationFrame(render);
}
發佈了19 篇原創文章 · 獲贊 4 · 訪問量 4294
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章