涉及到的知识点
- 平面填充几何体
ShapeGeometry - 包围盒
Box3 - 正投影相机
OrthographicCamera
目标效果
实现步骤
准备好地图数据
// 河南边界轮廓边界经纬度坐标
export default [
[110.3906, 34.585],
[110.8301, 34.6289],
[111.1816, 34.8047],
[111.5332, 34.8486],
[111.7969, 35.0684],
[112.0605, 35.0684],
[112.0605, 35.2881],
[112.7637, 35.2002],
[113.1152, 35.332],
[113.6426, 35.6836],
[113.7305, 36.3428],
[114.873, 36.123],
[114.9609, 36.0791],
[115.1367, 36.2109],
[115.3125, 36.0791],
[115.4883, 36.167],
[115.3125, 35.8154],
[116.1035, 36.0791],
[115.4883, 35.7275],
[115.2246, 35.4199],
[115.0488, 35.376],
[114.7852, 35.0684],
[115.4004, 34.8486],
[115.5762, 34.585],
[116.1914, 34.585],
[116.1914, 34.4092],
[116.543, 34.2773],
[116.6309, 33.9258],
[116.1914, 33.7061],
[116.0156, 33.9697],
[115.6641, 34.0576],
[115.5762, 33.9258],
[115.5762, 33.6621],
[115.4004, 33.5303],
[115.3125, 33.1787],
[114.873, 33.1348],
[114.873, 33.0029],
[115.1367, 32.8711],
[115.2246, 32.6074],
[115.5762, 32.4316],
[115.8398, 32.5195],
[115.9277, 31.7725],
[115.4883, 31.6846],
[115.4004, 31.4209],
[115.2246, 31.4209],
[115.1367, 31.5967],
[114.7852, 31.4648],
[114.6094, 31.5527],
[114.5215, 31.7725],
[114.1699, 31.8604],
[113.9941, 31.7725],
[113.8184, 31.8604],
[113.7305, 32.4316],
[113.4668, 32.2998],
[113.2031, 32.4316],
[112.3242, 32.3438],
[111.5332, 32.6074],
[111.0059, 33.2666],
[111.0059, 33.5303],
[110.6543, 33.8379],
[110.6543, 34.1455],
[110.4785, 34.2334],
[110.3906, 34.585]
]
1. 将地图每个坐标数据包装成二维向量,并放入一个数组中
// 一组二维向量表示一个多边形轮廓坐标
const pointsArr = [];
// data坐标数据转化为Vector2构成的顶点数组
data.forEach(function(e){
const v2 = new THREE.Vector2(e[0],e[1])
pointsArr.push(v2);
})
2. 定义一个平面多边形轮廓,参数为一个二位向量数组
// Shape表示一个平面多边形轮廓,参数是二维向量构成的数组pointsArr
const shape = new THREE.Shape(pointsArr);
3. 填充轮廓,参数为一个平面多边形轮廓
// 多边形shape轮廓作为ShapeGeometry参数,生成一个多边形平面几何体
const geometry = new THREE.ShapeGeometry(shape);
4. 定义材质,并将材质和模型添加到网格模型中
//定义材质
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff, //颜色
side: THREE.DoubleSide //单面还是双面可见
});
//将模型和材质添加进网格对象中
const mesh = new THREE.Mesh(geometry, material);
将mesh添加进场景中
效果图:
可以看到,地图轮廓已经可以呈现出来了,但是并不在视图中心位置并且很小。接下来便是解决这些问题。
将地图渲染到视图中心
使用包围盒(BOX3)得到地图的尺寸,中心点位置等
// 包围盒计算模型对象的大小和位置
//定义一个包围盒
const box3 = new THREE.Box3();
//使用 expandByObject方法计算出mesh的最小包围盒
box3.expandByObject(mesh);
//获取到地图的包围盒尺寸
const size = new THREE.Vector3();
box3.getSize(size);
//获取到地图的中心点坐标
const center = new THREE.Vector3();
box3.getCenter(center);
得到地图的尺寸坐标和中心点坐标之后,去改变相机的参数。
改变相机指向位置 (LookAt)
//x,y 为获取到的地图中心点
const x = 113.51,y = 33.88;
//与观察点x、y一样,地图平行与canvas画布,相机位置垂直于地图
camera.position.set(x, y, 300);
//相机看向地图中心点
camera.lookAt(x, y, 0);
效果:
将辅助坐标系的原点设置为地图中心点
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);
//自定义原点
axesHelper.position.set(x, y, 0);
效果:
设置正投影相机的参数
此处使用的是正投影相机而不是透视投影相机,因为此处不需要模拟人眼效果。
// 正投影相机
const width = window.innerWidth; //canvas画布宽度
const height = window.innerHeight; //canvas画布高度
const k = width / height; //canvas画布宽高比
const s = 2.5;//根据包围盒大小调整s,包围盒y方向尺寸的一半左右
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 8000);
camera.position.set(x, y, 300);//与观察点x、y一样,地图平行与canvas画布
camera.lookAt(x, y, 0);
效果:
总结
整体思路为:
- 先根据地图数据渲染出地图
- 利用包装盒(Box3)得到地图的尺寸,中心位置数据。
- 根据地图的尺寸,中心位置等信息改变
相机的参数和辅助坐标系的原点
注意点
当改变相机的指向坐标时,如果有使用相机空间 orbitControls, 需要设置:
controls.target.set(x, y, 0); //与lookAt参数保持一致
controls.update();//update()函数内会执行camera.lookAt(controls.target)
这样改变camera的lookAt 之后,才会生效!