threejs效果之楼房展示
效果:
基础数据:
楼房每一个楼的底边数据,数据结构为geojson。properties属性中Floor属性为楼房层数,本示例默认所有楼每层等高。
数据示例:

{ "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [ 120.32088059979766, 31.542322389378242 ], ...... [ 120.32088059979766, 31.542322389378242 ] ] ] }, "properties": { "FID": 0, "Id": 0, "Floor": 4 }, "id": 0 }, { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [ [ [ 120.32076281379749, 31.541746604232802 ], ...... [ 120.32076281379749, 31.541746604232802 ] ] ] }, "properties": { "FID": 1, "Id": 0, "Floor": 6 }, "id": 1 } ] }
实现原理:
通过楼的底边数据生成每个楼每层的面、每个面添加边框、以及墙体边框线等要素,拼接为楼群建筑。
先将底边的经纬度转换为米,舍去小数点后数据。
注意事项:
1.threejs生成面、体等为三角面模型,所以本例子的面都为手动绘制
2.基础数据中底边为经纬度点组成。若直接使用经纬度生成楼层面,因为经纬度小数点位数过多会导致threejs经度处理达不到,出现边框被抽稀的效果。
经纬度直接生成模型后,因为经纬度区别在小数点后几位,会导致模型过小。放大后会导致整个模型抖动严重。
代码解析:
1.initContainer() 方法为入口方法,混入vue组件后调用即可。
2.modelBox为threejs的canvas容器
源码:
1.vue组件:

1 <template> 2 <div class="threeModel"> 3 <div id="modelBox"></div> 4 </div> 5 </template> 6 7 <script> 8 import borderData from './人民医院建筑.json' 9 import createModel from './createModel' 10 export default { 11 name: 'threeModel', 12 mixins:[createModel], 13 data() { 14 return { 15 borderData: borderData, 16 } 17 }, 18 mounted() { 19 this.initContainer(this.borderData) 20 } 21 } 22 </script> 23 24 <style scoped lang="scss"> 25 .threeModel { 26 background: url('../../../assets/images/loop.png') no-repeat; 27 background-size: 100% 100%; 28 width: 100%; 29 height: 100%; 30 #modelBox { 31 width: 100%; 32 height: 100%; 33 background: rgba(0, 0, 0, 0.5); 34 } 35 } 36 </style>
2.混入方法:

1 import * as THREE from 'three' 2 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' 3 export default { 4 data(){ 5 return{ 6 _borderData: [], // 楼房基础数据 7 _bounds: [], // 楼房四至 8 _center: [] // 楼房中心点 9 } 10 }, 11 methods: { 12 13 // 创建场景 14 initContainer(borderData) { 15 /*创建场景对象Scene*/ 16 var scene = new THREE.Scene() 17 // 添加楼房楼层平面 18 this.addFloors(scene, borderData) 19 /* 光源设置*/ 20 // 环境光 21 var ambientLight = new THREE.AmbientLight(0xffffff) 22 scene.add(ambientLight) 23 /** 24 * 相机设置 25 */ 26 var width = window.innerWidth //窗口宽度 27 var height = window.innerHeight //窗口高度 28 var k = width / height //窗口宽高比 29 var s = 1500 //三维场景显示范围控制系数,系数越大,显示的范围越大 30 //创建相机对象 31 var camera = new THREE.PerspectiveCamera( 32 45, 33 window.innerWidth / window.innerHeight, 34 0.1, 35 1000000 36 ) 37 camera.position.set(3000,2000,3000) 38 camera.lookAt(scene.position) 39 /** 40 * 创建渲染器对象 41 */ 42 var renderer = new THREE.WebGLRenderer({ 43 antialias: true, 44 alpha: true 45 }) 46 renderer.setSize(window.innerWidth - 300, window.innerHeight) 47 document.getElementById('modelBox').appendChild(renderer.domElement) 48 49 var controls = new OrbitControls(camera, renderer.domElement) 50 controls.target = new THREE.Vector3(0, 0, 0) //控制焦点 51 controls.autoRotate = true;//将自动旋转关闭 52 let clock = new THREE.Clock();//用于更新轨道控制器 53 54 function animate() { 55 renderer.render(scene, camera) 56 57 let delta = clock.getDelta(); 58 controls.update(delta); 59 requestAnimationFrame(animate) 60 } 61 animate() 62 }, 63 64 // 添加楼群 65 addFloors(scene, data) { 66 // 数据处理/基础数据计算 67 this._dataAnalysis(data) 68 this._getModelBounds() 69 this._getModelCenter() 70 this._dataAnalysisAll() 71 // 新建楼房组 72 var group = new THREE.Group(); 73 group.rotation.set(-1.6,0,0); 74 scene.add(group) 75 // 添加楼层 76 this._borderData.forEach(res => { 77 this.addFloor(group,res) 78 // 添加楼房黄色边框墙 79 this.createWall(group, res) 80 }) 81 }, 82 // 添加单个楼 83 addFloor (group, data) { 84 let shape = this.createShape(data.points) 85 let i = 0 86 let addG = setInterval(() => { 87 if (i<data.floor) { 88 // 添加透明层 89 let mesh; 90 if (i === data.floor || i === 0) { 91 // 添加楼层顶部和底部效果 92 // mesh = this.createPolyline(shape, i, 0.9, 'rgb(14,98,173)') 93 } else { 94 mesh = this.createPolyline(shape, i) 95 } 96 group.add(mesh); 97 // 添加楼层边界 98 this.createPolygonline(group, data.points, i) 99 i++ 100 } else{ 101 if(addG){ 102 clearInterval(addG) 103 } 104 } 105 }, 30) 106 }, 107 // 数据转换 108 _dataAnalysis (borderData) { 109 let data = [] 110 borderData.features.forEach(res => { 111 res.geometry.coordinates.forEach(r => { 112 // 将度转换为米 113 r = r.map(re => { 114 return [(re[0]*1112000).toFixed(0)*1, (re[1]*1112000).toFixed(0)*1] 115 }) 116 data.push({ 117 floor: res.properties.Floor, 118 points: r 119 }) 120 }) 121 }) 122 this._borderData = data 123 }, 124 // 获取模型四至 125 _getModelBounds () { 126 // 四至: 上右下左 127 let bounds = [0,0,0,0] 128 // 所有点数组 129 let pointArr = [] 130 // 所有点经度数组 131 let pointLonArr = [] 132 // 所有点纬度数组 133 let pointLatArr = [] 134 // 获取所有点数组 135 this._borderData.forEach(res => { 136 pointArr = pointArr.concat(res.points) 137 }) 138 // 获取经度、纬度数组 139 pointArr.forEach(res => { 140 pointLonArr.push(res[0]) 141 pointLatArr.push(res[1]) 142 }) 143 // 获取四至 144 bounds = [Math.max(...pointLatArr), Math.min(...pointLonArr), Math.min(...pointLatArr), Math.max(...pointLonArr)] 145 this._bounds = bounds 146 }, 147 // 获取模型中心点 148 _getModelCenter () { 149 let center = [(this._bounds[1] + this._bounds[3])/2,(this._bounds[0] + this._bounds[2])/2] 150 this._center = center 151 }, 152 // 数据转换2-将数据移动至原点 153 _dataAnalysisAll(){ 154 this._borderData.forEach(res => { 155 res.points.forEach(re => { 156 re[0] = re[0] - this._center[0] 157 re[1] = re[1] - this._center[1] 158 }) 159 }) 160 }, 161 162 // 创建平面集合 163 createShape (data) { 164 var shape = new THREE.Shape(); 165 data.forEach((e,i) => { 166 if (i === 0) { 167 shape.moveTo( ...e); 168 } else { 169 shape.lineTo( ...e); 170 } 171 }) 172 return shape 173 }, 174 // 创建楼层平面组 175 createPolyoneGroup () { 176 var group = new THREE.Group(); 177 group.rotation.set(-1.6,0,0); 178 // group.position.set(-30,0,30); 179 // group.scale.set(5,5,5); 180 return group 181 }, 182 183 // 创建透明平面 184 createPolyline(shape, h, opacity = 0.1, color='rgb(0, 0,255, 0)'){ 185 var geometry = new THREE.ShapeGeometry( shape ); 186 var cubeMaterial=new THREE.MeshBasicMaterial({ 187 color: color, 188 transparent:true, 189 opacity:opacity, 190 side:THREE.DoubleSide // 强制双面 191 }); 192 var mesh = new THREE.Mesh( geometry, cubeMaterial ); 193 194 mesh.position.z = h*20; 195 // mesh.scale.set(1,1,1); 196 mesh.scale.set(1,1,1); 197 mesh.rotation.set(0,0,0); 198 199 return mesh 200 }, 201 // 创建边缘平面 202 createPolygonline(group, data, h=0){ 203 var material = new THREE.LineBasicMaterial({ 204 color: 'rgba(53,166,255,0.8)', 205 linewidth: 1, 206 side:THREE.DoubleSide // 强制双面 207 }); 208 var geometry = new THREE.Geometry() 209 for (let item of data) { 210 geometry.vertices.push( 211 new THREE.Vector3(...item, h*20) 212 ) 213 } 214 var line = new THREE.Line(geometry, material) 215 // line.scale.set(0.8,0.8,1); 216 // line.position.set(-10,-10,0); 217 group.add(line) 218 }, 219 220 // 创建墙面 221 createWall(group, data){ 222 var material = new THREE.LineBasicMaterial({ 223 color: 0xffc000 224 }); 225 let points = data.points 226 let wallData = [] 227 for (let i=0;i< points.length;i++) { 228 if(i + 1 < points.length){ 229 wallData.push([points[i], points[i+1]]) 230 } 231 } 232 for (let item of wallData) { 233 var geometry = new THREE.Geometry(); 234 geometry.vertices.push( 235 new THREE.Vector3( ...item[1], 0 ), 236 new THREE.Vector3( ...item[1], data.floor*20 ), 237 new THREE.Vector3( ...item[0], data.floor*20 ), 238 new THREE.Vector3( ...item[0], 0 ), 239 new THREE.Vector3( ...item[1], 0 ) 240 ); 241 var line = new THREE.Line( geometry, material ); 242 group.add(line) 243 } 244 }, 245 246 } 247 }
钻研不易,转载请注明出处。。。。。。