成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

我是如何用 Three.js 在三維世界建房子的(詳細教程)

開發 前端
Three.js 還是挺好玩的,業務上可能主要用于可視化、游戲,但工作之余也可以用它來做些有趣的東西。

[[439071]]

這兩天用 Three.js 畫了一個 3D 的房子,放了一個床進去,可以用鼠標和鍵盤控制移動,有種 3D 游戲的即視感。

這篇文章就來講下實現原理。

代碼地址:https://github.com/QuarkGluonPlasma/threejs-exercize

思路分析

我們先不著急寫代碼,先來分析下思路。

這樣一個房子,其實也是由幾個幾何體堆起來的:

具體有這么些幾何體:

地板就是個平面,用 PlaneGeometry(平面幾何體) 就可以畫,貼上個紋理貼圖就行。

兩個側面的墻,是一個不規則的形狀,這個可以用 ExtrudeGeometry(擠壓幾何體),它支持用畫筆畫一個 2D 的路徑,然后加厚變成 3D 的。

同理,后面的墻也很簡單,可以是 BoxGeometry(立方體)來畫,也可以是 ExtrudeGeometry(擠壓結合體)先畫個形狀,然后變成 3D 的。

前面的墻稍微復雜些,它也是不規則的,可以用 ExtrudeGeometry(擠壓幾何體)來畫出形狀,然后變成 3D 的,只不過它多了兩個洞,需要畫兩個洞加到形狀里面去。

門框、窗框也是形狀里扣個洞,用 ExtrudeGeometry 變成 3D 的。

那房頂呢?房頂也沒什么特殊的,只是立方體旋轉一定的角度就行,用 BoxGeometry(立方體) 就可以畫。

接下來,給墻和房頂、地板貼上不同的圖,設置好不同的位置,就可以組裝成一個房子了。

那么床呢?

Three.js 提供了很多的幾何體,可以畫一些簡單的物體,但復雜的物體就很難畫出來了,這類物體一般會用專業的 3D 建模軟件來畫,導出 FPX 或者 OBJ 格式的文件由 Three.js 加載并渲染出來。

我們在網上找一個床的 3D 模型,我找了一個 FBX 格式的,然后用 Three.js 的 FBXLoader 加載就行。

還剩下一個草地,這個也是一個平面,用 PlaneGeometry(平面幾何體)畫,只不過就是長寬比較大,看不到盡頭而已。

看起來還有霧?

沒錯,確實設置了霧(Fog),Three.js 在場景中設置霧的效果,指定顏色和霧的遠近范圍就行。為了有種模糊的感覺,我就在場景中加入了霧。

全部的物體都畫完了,接下來就可以在 3D 場景中漫游了,通過鼠標和鍵盤可以改變方向和前后左右移動,這種交互使用 FirstPersonControls(第一人稱控制器) 來實現。

一般我們常用的是 OrbitsControls(軌道控制器),它支持圍繞物體轉動相機,就像衛星一樣。但我們這里不是想繞著轉,而是想鍵盤和鼠標控制的前后左右的隨意移動。

我們簡單小結下:

Three.js 是在三維的坐標系中添加各種物體,組裝成不同的 3D 場景。其中簡單的物體可以畫,復雜的物體會用建模軟件畫,然后加載到場景中。我們可以用不同的控制器來控制相機移動,達到不同的交互效果,比如軌道控制器、第一人稱控制器等。

房子的墻、地板、房頂都可以用 BoxGeometry(立方體)、ExtrudeGeometry(擠壓幾何體)畫出來,但是床這種復雜的就不行了,會直接加載模型文件。

通過 FistPersonControls(第一人稱控制器)來控制交互,就能達到 3D 游戲的那種感覺。

思路理清了,接下來我們具體寫下代碼:

代碼實現

先畫草地,也就是一個大的平面,貼上草地的貼圖。

三維的物體(Mesh) 是由幾何體(Geometry),加上材質(Material)構成的。我們創建平面幾何體(PlaneGeometry),長和寬制定一個很大的值,比如 10000,然后加載草地的圖片作為紋理(Texture),構成材質。之后就可以創建出草地了。

  1. function createGrass() { 
  2.     const geometry = new THREE.PlaneGeometry( 10000, 10000); 
  3.  
  4.     const texture = new THREE.TextureLoader().load('img/grass.jpg'); 
  5.     texture.wrapS = THREE.RepeatWrapping; 
  6.     texture.wrapT = THREE.RepeatWrapping; 
  7.     texture.repeat.set( 100, 100 ); 
  8.  
  9.     const grassMaterial = new THREE.MeshBasicMaterial({map: texture}); 
  10.  
  11.     const grass = new THREE.Mesh( geometry, grassMaterial ); 
  12.  
  13.     grass.rotation.x = -0.5 * Math.PI; 
  14.  
  15.     scene.add( grass ); 

紋理貼圖要設置兩個方向都重復,重復的次數是 100 次。

然后草地的平面要旋轉一下。

加點霧,讓天際模糊一些:

  1. scene.fog = new THREE.Fog(0xffffff, 10, 1500); 

分別指定顏色為白色,霧的遠近范圍為 10 到 1500。

接下來是創建房子,房子由地板、兩側的墻、前面的墻、后面的墻、門框窗框、房頂、床構成,要分別創建每一部分,我們把它們放到單獨的 Group(分組)里。

  1. const house = new THREE.Group(); 
  2.  
  3. function createHouse() { 
  4.     createFloor(); 
  5.  
  6.     const sideWall = createSideWall(); 
  7.     const sideWall2 = createSideWall(); 
  8.     sideWall2.position.z = 300; 
  9.  
  10.     createFrontWall(); 
  11.     createBackWall(); 
  12.  
  13.     const roof = createRoof(); 
  14.     const roof2 = createRoof(); 
  15.     roof2.rotation.x = Math.PI / 2; 
  16.     roof2.rotation.y = Math.PI / 4 * 0.6; 
  17.     roof2.position.y = 130; 
  18.     roof2.position.x = -50; 
  19.     roof2.position.z = 155; 
  20.  
  21.     createWindow(); 
  22.     createDoor(); 
  23.  
  24.     createBed(); 

創建地板也是平面幾何體(PlaneGeometry),貼上木材的圖就行,然后設置下位置:

  1. function createFloor() { 
  2.     const geometry = new THREE.PlaneGeometry( 200, 300); 
  3.  
  4.     const texture = new THREE.TextureLoader().load('img/wood.jpg'); 
  5.     texture.wrapS = THREE.RepeatWrapping; 
  6.     texture.wrapT = THREE.RepeatWrapping; 
  7.     texture.repeat.set( 2, 2 ); 
  8.  
  9.     const material = new THREE.MeshBasicMaterial({map: texture}); 
  10.      
  11.     const floor = new THREE.Mesh( geometry, material ); 
  12.  
  13.     floor.rotation.x = -0.5 * Math.PI; 
  14.     floor.position.y = 1; 
  15.     floor.position.z = 150; 
  16.  
  17.     house.add(floor); 

創建側面的墻,要用 ExtrudeGeometry(擠壓幾何體)來畫,也就是先畫出一個 2D 的形狀,然后擠壓成 3D。還要貼上墻的紋理貼圖。

  1. function createSideWall() { 
  2.     const shape = new THREE.Shape(); 
  3.     shape.moveTo(-100, 0); 
  4.     shape.lineTo(100, 0); 
  5.     shape.lineTo(100,100); 
  6.     shape.lineTo(0,150); 
  7.     shape.lineTo(-100,100); 
  8.     shape.lineTo(-100,0); 
  9.  
  10.     const extrudeGeometry = new THREE.ExtrudeGeometry( shape ); 
  11.  
  12.     const texture = new THREE.TextureLoader().load('./img/wall.jpg'); 
  13.     texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  14.     texture.repeat.set( 0.01, 0.005 ); 
  15.  
  16.     var material = new THREE.MeshBasicMaterial( {map: texture} ); 
  17.  
  18.     const sideWall = new THREE.Mesh( extrudeGeometry, material ) ; 
  19.  
  20.     house.add(sideWall); 
  21.  
  22.     return sideWall; 

兩個側墻只是位置不同,修改下 z 軸位置就行:

  1. const sideWall = createSideWall(); 
  2. const sideWall2 = createSideWall(); 
  3. sideWall2.position.z = 300; 

對了,如果對位置拿不準,可以在場景中加個坐標系輔助工具(AxisHelper)。

  1. const axisHelper = new THREE.AxisHelper(2000); 
  2. scene.add(axisHelper); 

然后是后面的墻,這個形狀簡單一些,就是個矩形:

  1. function createBackWall() { 
  2.     const shape = new THREE.Shape(); 
  3.     shape.moveTo(-150, 0) 
  4.     shape.lineTo(150, 0) 
  5.     shape.lineTo(150,100) 
  6.     shape.lineTo(-150,100); 
  7.  
  8.     const extrudeGeometry = new THREE.ExtrudeGeometry( shape )  
  9.  
  10.     const texture = new THREE.TextureLoader().load('./img/wall.jpg'); 
  11.     texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  12.     texture.repeat.set( 0.01, 0.005 ); 
  13.  
  14.     const material = new THREE.MeshBasicMaterial({map: texture}); 
  15.  
  16.     const backWall = new THREE.Mesh( extrudeGeometry, material) ; 
  17.  
  18.     backWall.position.z = 150; 
  19.     backWall.position.x = -100; 
  20.     backWall.rotation.y = Math.PI * 0.5; 
  21.  
  22.     house.add(backWall); 

接下來是前面的墻,這個除了要畫出形狀外,還要摳出兩個洞:

  1. function createFrontWall() { 
  2.     const shape = new THREE.Shape(); 
  3.     shape.moveTo(-150, 0); 
  4.     shape.lineTo(150, 0); 
  5.     shape.lineTo(150,100); 
  6.     shape.lineTo(-150,100); 
  7.     shape.lineTo(-150,0); 
  8.  
  9.     const window = new THREE.Path(); 
  10.     window.moveTo(30,30) 
  11.     window.lineTo(80, 30) 
  12.     window.lineTo(80, 80) 
  13.     window.lineTo(30, 80); 
  14.     window.lineTo(30, 30); 
  15.     shape.holes.push(window); 
  16.  
  17.     const door = new THREE.Path(); 
  18.     door.moveTo(-30, 0) 
  19.     door.lineTo(-30, 80) 
  20.     door.lineTo(-80, 80) 
  21.     door.lineTo(-80, 0); 
  22.     door.lineTo(-30, 0); 
  23.     shape.holes.push(door); 
  24.  
  25.     const extrudeGeometry = new THREE.ExtrudeGeometry( shape )  
  26.  
  27.     const texture = new THREE.TextureLoader().load('./img/wall.jpg'); 
  28.     texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  29.     texture.repeat.set( 0.01, 0.005 ); 
  30.  
  31.     const material = new THREE.MeshBasicMaterial({map: texture} ); 
  32.  
  33.     const frontWall = new THREE.Mesh( extrudeGeometry, material ) ; 
  34.  
  35.     frontWall.position.z = 150; 
  36.     frontWall.position.x = 100; 
  37.     frontWall.rotation.y = Math.PI * 0.5; 
  38.  
  39.     house.add(frontWall); 

只是形狀上多了兩個洞,畫起來復雜些,其余的紋理、材質,還有位置等設置方式都一樣。

門窗也是畫一個形狀,摳一個洞,然后加點厚度變成 3D 的:

  1. function createWindow() { 
  2.     const shape = new THREE.Shape(); 
  3.     shape.moveTo(0, 0); 
  4.     shape.lineTo(0, 50) 
  5.     shape.lineTo(50,50) 
  6.     shape.lineTo(50,0); 
  7.     shape.lineTo(0, 0); 
  8.  
  9.     const hole = new THREE.Path(); 
  10.     hole.moveTo(5,5) 
  11.     hole.lineTo(5, 45) 
  12.     hole.lineTo(45, 45) 
  13.     hole.lineTo(45, 5); 
  14.     hole.lineTo(5, 5); 
  15.     shape.holes.push(hole); 
  16.  
  17.     const extrudeGeometry = new THREE.ExtrudeGeometry(shape); 
  18.  
  19.     var extrudeMaterial = new THREE.MeshBasicMaterial({ color: 'silver' }); 
  20.  
  21.     var window = new THREE.Mesh( extrudeGeometry, extrudeMaterial ) ; 
  22.     window.rotation.y = Math.PI / 2; 
  23.     window.position.y = 30; 
  24.     window.position.x = 100; 
  25.     window.position.z = 120; 
  26.  
  27.     house.add(window); 
  28.  
  29.     return window; 

顏色設置為銀白色。

門框也是一樣:

  1. function createDoor() { 
  2.     const shape = new THREE.Shape(); 
  3.     shape.moveTo(0, 0); 
  4.     shape.lineTo(0, 80); 
  5.     shape.lineTo(50,80); 
  6.     shape.lineTo(50,0); 
  7.     shape.lineTo(0, 0); 
  8.  
  9.     const hole = new THREE.Path(); 
  10.     hole.moveTo(5,5); 
  11.     hole.lineTo(5, 75); 
  12.     hole.lineTo(45, 75); 
  13.     hole.lineTo(45, 5); 
  14.     hole.lineTo(5, 5); 
  15.     shape.holes.push(hole); 
  16.  
  17.     const extrudeGeometry = new THREE.ExtrudeGeometry( shape ); 
  18.  
  19.     const material = new THREE.MeshBasicMaterial( { color: 'silver' } ); 
  20.  
  21.     const door = new THREE.Mesh( extrudeGeometry, material ) ; 
  22.  
  23.     door.rotation.y = Math.PI / 2; 
  24.     door.position.y = 0; 
  25.     door.position.x = 100; 
  26.     door.position.z = 230; 
  27.  
  28.     house.add(door); 

接下來是房頂,就是兩個立方體(BoxGeometry),做下旋轉:

  1. const roof = createRoof(); 
  2.  
  3. const roof2 = createRoof(); 
  4. roof2.rotation.x = Math.PI / 2; 
  5. roof2.rotation.y = Math.PI / 4 * 0.6; 
  6. roof2.position.y = 130; 
  7. roof2.position.x = -50; 
  8. roof2.position.z = 155; 

房頂的六個面的材質不同,一個面放瓦片的貼圖,其余的面設置成灰色就行,模擬水泥的效果。其中,瓦片的紋理要做下旋轉,設置下兩個方向的重復次數。

  1. function createRoof() { 
  2.     const geometry = new THREE.BoxGeometry( 120, 320, 10 ); 
  3.  
  4.     const texture = new THREE.TextureLoader().load('./img/tile.jpg'); 
  5.     texture.wrapS = texture.wrapT = THREE.RepeatWrapping; 
  6.     texture.repeat.set( 5, 1); 
  7.     texture.rotation = Math.PI / 2; 
  8.     const textureMaterial = new THREE.MeshBasicMaterial({ map: texture}); 
  9.  
  10.     const colorMaterial = new THREE.MeshBasicMaterial({ color: 'grey' }); 
  11.  
  12.     const materials = [ 
  13.         colorMaterial, 
  14.         colorMaterial, 
  15.         colorMaterial, 
  16.         colorMaterial, 
  17.         colorMaterial, 
  18.         textureMaterial 
  19.     ]; 
  20.  
  21.     const roof = new THREE.Mesh( geometry, materials ); 
  22.  
  23.     house.add(roof); 
  24.  
  25.     roof.rotation.x = Math.PI / 2; 
  26.     roof.rotation.y = - Math.PI / 4 * 0.6; 
  27.     roof.position.y = 130; 
  28.     roof.position.x = 50; 
  29.     roof.position.z = 155; 
  30.  
  31.     return roof; 

接下來的床就簡單了,因為不用自己畫,直接加載一個已有的模型就行,這種復雜的模型一般都是專業建模軟件畫的。

  1. function createBed() { 
  2.     var loader = new THREE.FBXLoader(); 
  3.     loader.load('./obj/bed.fbx'function ( object ) { 
  4.         object.position.x = 40; 
  5.         object.position.z = 80; 
  6.         object.position.y = 20; 
  7.  
  8.         house.add( object ); 
  9.     } ); 

再就是燈光設置為環境光,也就是每個方向的光照強度都一樣。

  1. const light = new THREE.AmbientLight(0xCCCCCC); 
  2. scene.add(light); 

創建相機,使用透視相機,也就是近大遠小的那種透視效果:

  1. const width = window.innerWidth; 
  2. const height = window.innerHeight; 
  3. const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000); 

指定看的角度為 60 度,寬高比,遠近范圍 0.1 到 1000。

創建渲染器,并用 requestAnimationFrame 一幀幀渲染就行了:

  1. const renderer = new THREE.WebGLRenderer(); 
  2. function render() { 
  3.     renderer.render(scene, camera); 
  4.     requestAnimationFrame(render) 

接下來還要支持在 3D 場景中漫游,這個也不用自己做,Three.js 貼心的提供了很多控制器,各自有不同的交互效果,其中有個第一人稱控制器(FirstPersonControls),就是玩游戲時那種交互,通過 W、S、A、D 鍵控制前后左右,通過鼠標控制方向。

  1. const controls = new THREE.FirstPersonControls(camera); 
  2. controls.lookSpeed = 0.05; 
  3. controls.movementSpeed = 100; 
  4. controls.lookVertical = false

我們指定了轉換方向的速度 lookSpeed,移動的速度 movementSpeed,禁止了縱向的轉動。

然后每一幀都要更新一下看到的畫面,通過時鐘 Clock 獲取到過去了多久,然后更新下控制器。

  1. const clock = new THREE.Clock(); 
  2.  
  3. function render() { 
  4.     const delta = clock.getDelta(); 
  5.     controls.update(delta); 
  6.  
  7.     renderer.render(scene, camera); 
  8.     requestAnimationFrame(render) 

總結

本文寫了 Three.js 畫 3D 房子的實現原理。

Three.js 通過場景 Scene 管理各種物體,物體之間可以分組。物體由幾何體(Geometry)和材質(Material)兩部分構成,房子就是由立方體(BoxGeometry)、擠壓幾何體(ExtrudeGeometry)等各種幾何體構成的,設置不同的貼圖紋理,還有位置、旋轉角度。

其中比較特殊的是 ExtrudeGeometry(擠壓幾何體),它是通過在二維平面畫一個形狀,然后“擠壓”成 三維的形式,形狀中還可以扣個洞。

房子中放了一張床,這種復雜的物體用 Three.js 手畫就比較難了,這種一般都是由專業建模軟件,比如 blender 來畫好,然后用 Three.js 加載并渲染的。

視角的改變其實就是相機位置和朝向的改變,Three.js 提供了各種控制器,比如 OrbitsControls(軌道控制器)、FirstPersonControls(第一人稱控制器)等。

我們這里要的通過鍵盤控制前后左右,通過鼠標控制轉向的交互就可以用 FirstPersonControls。

Three.js 還是挺好玩的,業務上可能主要用于可視化、游戲,但工作之余也可以用它來做些有趣的東西。

 

責任編輯:姜華 來源: 神光的編程秘籍
相關推薦

2019-11-29 09:30:37

Three.js3D前端

2022-07-15 13:09:33

Three.js前端

2023-07-13 10:48:22

web 3DThree.jsBlender

2017-05-08 11:41:37

WebGLThree.js

2021-11-22 06:14:45

Three.js3D 渲染花瓣雨

2009-11-10 12:48:17

VB.NET三維模型

2024-07-18 06:58:36

2021-04-23 16:40:49

Three.js前端代碼

2025-05-15 08:45:00

開源前端手勢

2021-12-14 11:44:37

可視化Three.js 信息

2021-12-03 07:27:30

全景瀏覽Three.js

2022-01-16 19:23:25

Three.js粒子動畫群星送福

2025-03-13 10:54:18

2013-05-03 16:50:22

三維實景

2025-06-17 08:15:00

VTK.jsThree.js3D

2017-05-02 13:38:51

CSS繪制形狀

2022-03-07 09:20:00

JavaScripThree.jsNFT

2020-11-23 07:43:18

JS

2022-07-08 10:39:09

Three.js元宇宙VR

2021-03-22 11:10:09

Redis架構MQ
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 97伊人 | 成人在线中文字幕 | 欧美2区| 日韩视频在线免费观看 | 在线国产一区二区 | 中文字幕人成乱码在线观看 | 欧美在线视频一区 | 国产精品一区二区福利视频 | 成人一区二区三区在线观看 | 久久久精彩视频 | 午夜国产精品视频 | 久久国产亚洲精品 | 国产精品区二区三区日本 | 欧美性生活网 | 一区二区中文 | 免费午夜视频 | a在线免费观看视频 | av男人的天堂在线 | 91麻豆精品国产91久久久更新资源速度超快 | 国产精品久久免费观看 | 在线一级片| 视频一区二区在线观看 | 福利视频一区二区 | 亚洲久在线| jdav视频在线观看免费 | www.黄色片视频 | 欧美精品国产一区二区 | 国产福利小视频 | 99国产精品久久久久老师 | 久久久久一区二区三区 | www.色53色.com | 日韩一区精品 | 亚洲美女视频 | 一级黄色毛片免费 | 免费观看黄 | 国产精品欧美一区二区 | 日韩在线国产 | 久久99精品久久久久久国产越南 | 成人国产午夜在线观看 | 在线看av的网址 | 日韩欧美在线播放 |