开启掘金成长之旅!这是我参与「掘金日新计划 · 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; }
这样就渲染好了建筑,下次我们渲染道路和道路的流光。