开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天
设置渲染的原点
在上一篇中我们讲到如何将经纬度和渲染坐标进行转换,我们都知道经纬度[0,0] 的位置是在非洲几内亚湾,而我们渲染的其实是中国的城市,为了简化,我们自己进行一次转化:将我们需要渲染的经纬度的一个点设置为渲染的原点,然后在后续开发中的遇到的经纬度,先转化为渲染坐标,然后在和原点进行一次距离换算。
//选定杭州的某个点的经纬度
const data_center=[120.17219180819616,30.247646581986757];
//计算中心点的位置,也就是原点的相对坐标,getMercator是上节中经纬度转化方法
const com_center = getMercator(data_center);
//开发的经纬度和渲染的相对坐标计算
const getThreePosition=(positonArry)=> {
const local_position = this.getMercator(positonArry);
local_position.x -= this.com_center.x;
local_position.y -= this.com_center.y;
return local_position;
}
建筑数据准备
杭州数据源:
gw.alipayobjects.com/os/rmsporta…
这是我找到的杭州建筑数据,部分数据格式如下:
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "floor": 2 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 120.196545181928684, 30.254913414850879 ], [ 120.196587916040812, 30.254913555556769 ], [ 120.1965879187985, 30.254946779300354 ], [ 120.196679873650595, 30.254946619826882 ], [ 120.196679871643028, 30.254922432441454 ], [ 120.196878098908684, 30.25492768203145 ], [ 120.196883037303849, 30.254760614569687 ], [ 120.19671699669172, 30.254756741591784 ], [ 120.196717003790695, 30.254779696221956 ], [ 120.19661499594217, 30.25477458790802 ], [ 120.196609908044962, 30.254876578931778 ], [ 120.196545178852986, 30.254876360053494 ], [ 120.196545181928684, 30.254913414850879 ] ] ] ] } },
{ "type": "Feature", "properties": { "floor": 6 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 120.196969105954409, 30.25492744908205 ], [ 120.197119195586595, 30.254927461032878 ], [ 120.197114224173092, 30.254978570322315 ], [ 120.197275099142189, 30.254983553591565 ], [ 120.197275091435102, 30.254890679812995 ], [ 120.197130115077073, 30.254890579232381 ], [ 120.197130110842593, 30.254839554065057 ], [ 120.19696909866822, 30.254839656033688 ], [ 120.196969105954409, 30.25492744908205 ] ] ] ] } }]}
简单的解释下这个数据结构,所有的数据在features中,里面是一个个建筑的信息,properties.floor的值是楼层数也就是建筑物的高度,coordinates[0][0]里面的就是建筑物的几个平面顶点的经纬度,是个N边形。
开始渲染建筑
拿到数据以后我们先写个方法把建筑信息取出来
//data_buildInfos刚刚拿到的json数据,
data_buildInfos.features.forEach((items, i) => {
//buildSet渲染的方法(顶点位置,楼层数)
buildSet(items.geometry.coordinates[0][0], items.properties.floor);
});
然后编写渲染的代码(项目用的是vue)
buildSet(positionArry, floor) {
// 假设1层楼高度为10
const floorHeight = 10 * floor;
const cubeGeometry = new THREE.Geometry();
const vertices = [];
const faces = [];
const buttomShape = new THREE.Shape();
positionArry.forEach((item, i) => {
// 获取相对原点的位置
const local_position = this.getThreePosition(item);
// three画shape的方法,画建筑物的底
if (i === 0) {
buttomShape.moveTo(local_position.x, local_position.y);
} else {
buttomShape.lineTo(local_position.x, local_position.y);
}
// 顶点存起来
vertices.push(new THREE.Vector3(local_position.x, 0, local_position.y));
vertices.push(new THREE.Vector3(local_position.x, floorHeight, local_position.y));
// 画建筑物侧面的三角形,2个三角形就是一个四边形
if (i === positionArry.length - 1) {
faces.push(new THREE.Face3(2 * i, 0, 1));
faces.push(new THREE.Face3(2 * i, 1, 2 * i + 1));
} else {
faces.push(new THREE.Face3(2 * i, 2 * i + 2, 2 * i + 3));
faces.push(new THREE.Face3(2 * i, 2 * i + 3, 2 * i + 1));
}
});
cubeGeometry.vertices = vertices;
cubeGeometry.faces = faces;
// 生成法向量,用来贴图
this.assignUVs(cubeGeometry);
cubeGeometry.computeFaceNormals();
// 贴图
const local_texture = new THREE.TextureLoader().load('./images/casement.png');
const material12 = new THREE.MeshPhongMaterial({
map: local_texture,
side: THREE.DoubleSide,
});
const mesh1 = new THREE.Mesh(cubeGeometry, material12);
mesh1.castShadow = true;
this.scene.add(mesh1);
// 允许重复
local_texture.wrapS = THREE.RepeatWrapping;
local_texture.wrapT = THREE.RepeatWrapping;
local_texture.repeat.set(2, Math.ceil(0.5 * floor));
const material1 = new THREE.MeshBasicMaterial({ color: '#333', side: THREE.DoubleSide });
const geometry = new THREE.ShapeGeometry(buttomShape);
const mesh = new THREE.Mesh(geometry, material1);
mesh.rotateX(Math.PI * 0.5);
const meshed = mesh.clone();
this.scene.add(mesh);
meshed.position.y = floorHeight;
this.scene.add(meshed);
local_texture.needsUpdate = true;
}
assignUVs(geometry) {
const faces = geometry.faces;
geometry.faceVertexUvs[0] = [];
for (let i = 0; i < faces.length; i++) {
if (i % 2 === 0) {
geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 0),
new THREE.Vector2(1, 0),
new THREE.Vector2(1, 1),
]);
} else {
geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 0),
new THREE.Vector2(1, 1),
new THREE.Vector2(0, 1),
]);
}
}
geometry.uvsNeedUpdate = true;
}
这样就渲染好了建筑,下次我们渲染道路和道路的流光。