threejs效果之樓房展示


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
    }
  ]
}    
View Code

實現原理:

  通過樓的底邊數據生成每個樓每層的面、每個面添加邊框、以及牆體邊框線等要素,拼接為樓群建築。

  先將底邊的經緯度轉換為米,舍去小數點后數據。

注意事項

  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>
View Code

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 }
View Code

 

 

 

鑽研不易,轉載請注明出處。。。。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM