如何用Three.js寫一個下雨動畫

這篇文章主要講解了“如何用Three.js寫一個下雨動畫”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何用Three.js寫一個下雨動畫”吧!

創(chuàng)新互聯(lián)建站服務(wù)項目包括新和網(wǎng)站建設(shè)、新和網(wǎng)站制作、新和網(wǎng)頁制作以及新和網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,新和網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到新和省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

前置知識

WebGL讓我們能在瀏覽器開發(fā)3D應(yīng)用,然而直接使用WebGL編程還是挺復(fù)雜的,開發(fā)者需要知道WebGL的底層細(xì)節(jié),并且學(xué)習(xí)復(fù)雜的著色語言來獲得WebGL的大部分功能。Three.js提供了一系列很簡單的關(guān)于WebGL特性的JavaScript API,使開發(fā)者可以很方便地創(chuàng)作出好看的3D圖形。在Three.js官網(wǎng),就有很多酷炫3D效果[1]。

使用Three.js開發(fā)3D應(yīng)用,通常要包括渲染器(Renderer)、場景(Scene)、照相機(Camera),以及你在場景中創(chuàng)建的物體,光照。

設(shè)想一下照相的情況,我們需要一個場景(Scene),在這個場景中擺好要拍攝的物體,設(shè)置光照環(huán)境,擺放好照相機(Camera)的位置和朝向,然后就可以拍照了。渲染器(Renderer)可能和攝影師比較像吧,負(fù)責(zé)下命令拍攝,并且生成圖像(照片)。

將下面的代碼的復(fù)制并運行,就可以得到一個很簡單的3D場景。

如何用Three.js寫一個下雨動畫

image.png

<!DOCTYPE html>  <html lang="en">  <head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>room</title>  </head>  <body>   <div id="webgl-output"></div>    <script src="https://unpkg.com/three@0.119.0/build/three.js"></script>    <script>      function init () {        const scene = new THREE.Scene()        const camera = new THREE.PerspectiveCamera(45,           window.innerWidth / window.innerHeight,          0.1,          1000        )        camera.position.set(-30, 40, 30)       camera.lookAt(0,0,0)        scene.add(camera)         const planeGeometry = new THREE.PlaneGeometry(60,20)        const planeMaterial = new THREE.MeshLambertMaterial({          color: 0xAAAAAA        })          const plane = new THREE.Mesh(planeGeometry, planeMaterial)        plane.rotation.x = -Math.PI / 2        plane.position.set(15, 0, 0)        scene.add(plane)        const sphereGeometry = new THREE.SphereGeometry(4, 20, 20)        const sphereMaterial = new THREE.MeshLambertMaterial({          color: 0xffff00        })        const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)        sphere.position.set(20, 4, 2)        scene.add(sphere)        const spotLight = new THREE.SpotLight(0xffffff)        spotLight.position.set(-20, 30, -15)        scene.add(spotLight)        const renderer = new THREE.WebGLRenderer()        renderer.setClearColor(new THREE.Color(0x000000))        renderer.setSize(window.innerWidth, window.innerHeight)        document.getElementById('webgl-output').appendChild(renderer.domElement)        renderer.render(scene, camera)      }     init()  </script>  </body>  </html>

場景(Scene)

THREE.Scene對象是所有不同對象的容器,但這個對象本身沒有很復(fù)雜的操作,我們通常在程序最開始的時候?qū)嵗粋€場景,然后將照相機、物體、光源添加到場景中。

const scene = new THREE.Scene()  scene.add(camera)        //添加照相機  scene.add(plane)         //添加灰色平面  scene.add(sphere)        //添加黃色球體  scene.add(spotLight)     //添加光源

照相機(Camera)

Three.js庫提供了兩種不同的照相機:透視投影照相機和正交投影照相機。

透視投影照相機的效果類似人眼在真實世界中看到的場景,有 "近大遠(yuǎn)小" 的效果,垂直視平面的平行線在遠(yuǎn)方會相交。  正交投影照相機的效果類似我們在數(shù)學(xué)幾何學(xué)課上老師教我們畫的效果,在三維空間內(nèi)平行的線,在屏幕上永遠(yuǎn)不會相交。

我們這里用的是透視投影照相機,就主要討論它,正交投影照相機后面用到再說。

const camera = new THREE.PerspectiveCamera(    45,     window.innerWidth / window.innerHeight,    0.1,    1000  )  camera.position.set(-30, 40, 30)  camera.lookAt(0,0,0)  scene.add(camera)

設(shè)置一個照相機分三步:確定視野范圍, 確定照相機坐標(biāo), 確定照相機聚焦點。

如何用Three.js寫一個下雨動畫

我們在new THREE.PerspectiveCamera的時候確定照相機的視野范圍,對應(yīng)上圖,45是fov,就是視野上下邊緣之間的夾角。window.innerWidth / window.innerHeight是視野水平方向和豎直方向長度的比值,0.1(near)和1000(far)分別是照相機到視景體最近、最遠(yuǎn)的距離,這些參數(shù)決定了要顯示的三維空間的范圍,也就是上圖中的灰色區(qū)域。

camera.position.set(-30, 40, 30)確定了照相機在空間中的坐標(biāo)。

camera.lookAt(0,0,0)確定了照相機聚焦點,該點和照相機坐標(biāo)的連線就是拍攝方向。

上圖中的灰色區(qū)域在屏幕上的顯示效果,也就是將三維空間的坐標(biāo)投影到屏幕二維坐標(biāo)是webgl完成的,我們只需要關(guān)心三維空間的坐標(biāo)。

坐標(biāo)系

與我們之前講到的CSS的3D坐標(biāo)系[2]不同,webgl坐標(biāo)系是右手坐標(biāo)系,X軸向右,Y軸向上,Z軸是指向“自己”的。

伸出右手,讓拇指和食指成"L"形,大拇指向右,食指向上。其余的手指指向自己,這樣就建立了一個右手坐標(biāo)系。

其中,拇指、食指和其余手指分別代表x,y,z軸的正方向

在空間中定位、平移都比較好理解,這里看一下旋轉(zhuǎn)。

有時,我們會這樣設(shè)置物體的旋轉(zhuǎn):object.rotation.x = \-Math.PI / 2,表示的是繞X軸旋轉(zhuǎn)-90度。具體是怎么旋轉(zhuǎn),就要對照上面坐標(biāo)系,展開右手,拇指指向x軸正方向,其余手指的彎曲方向就是旋轉(zhuǎn)的正方向;拇指指向x軸負(fù)方向,其余手指的彎曲方向就是旋轉(zhuǎn)的負(fù)方向。y軸和z軸旋轉(zhuǎn)方向的判斷同理。

物體

在three.js中,創(chuàng)建一個物體需要兩個參數(shù):幾何形狀(Geometry)和 材質(zhì)(Material)。通俗的講,幾何形狀決定物體的形狀,材質(zhì)決定物體表面的顏色、紋理貼圖、對光照的反應(yīng)等等。

//創(chuàng)建一個平面幾何體,參數(shù)是沿X方向的Width和沿Y方向的height  const planeGeometry = new THREE.PlaneGeometry(60,20)    //創(chuàng)建一種材質(zhì),MeshLambertMaterial是一種考慮漫反射而不考慮鏡面反射的材質(zhì)  const planeMaterial = new THREE.MeshLambertMaterial({    color: 0xAAAAAA  })    //根據(jù)幾何形狀和材質(zhì)創(chuàng)建物體  const plane = new THREE.Mesh(planeGeometry, planeMaterial)  //設(shè)置物體的位置和旋轉(zhuǎn),并將物體加到場景(scene)中 plane.rotation.x = -Math.PI / 2  plane.position.set(15, 0, 0)  scene.add(plane)

一些常用的幾何形狀和材質(zhì)可以參考Three.js入門指南[3]

光照

沒有光源,渲染的場景將不可見(除非你使用基礎(chǔ)材質(zhì)或線框材質(zhì),當(dāng)然,在構(gòu)建3D應(yīng)用時,幾乎不怎么用基礎(chǔ)材質(zhì)和線框材質(zhì))。

WebGL本身并不支持光源。如果不使用Three.js,則需要自己寫WebGL著色程序來模擬光源。Three.js讓光源的使用變得簡單。

const spotLight = new THREE.SpotLight(0xffffff)  spotLight.position.set(0, 0, 100)  scene.add(spotLight)

如上所示,我們只需要創(chuàng)建一個光源,并將它加入到場景中就可以了。three.js會根據(jù)光源的類型、位置等信息計算出場景中各個物體的展示效果。

最常用的幾種光源是AmbientLight、PointLight、SpotLight、DirectionalLight。

如何用Three.js寫一個下雨動畫

渲染器(Renderer)

當(dāng)場景中的照相機、物體、光照等準(zhǔn)備就緒,就該渲染器上場了。

在上面那個小例子中,我們是這樣使用渲染器的:

//new 一個渲染器  const renderer = new THREE.WebGLRenderer()  //設(shè)置畫布背景色,也就是畫布中沒有物體的地方的顯示顏色  renderer.setClearColor(new THREE.Color(0x000000))  //設(shè)置畫布大小  renderer.setSize(window.innerWidth, window.innerHeight)  //將畫布元素(即renderer.domElement,它是一個canvas元素)掛載到一個dom節(jié)點  document.getElementById('webgl-output').appendChild(renderer.domElement)  //執(zhí)行渲染操作,參數(shù)是上面定義的場景(scene)和照相機(camera)  renderer.render(scene, camera)

可以看出,使用Three.js開發(fā)3D應(yīng)用,我們只需要關(guān)心場景中物體、照相機、光照等在三維空間中的布局,以及運動,具體怎么渲染都由Three.js去完成。當(dāng)然,懂一些webgl的基本原理會更好,畢竟有一些應(yīng)用會復(fù)雜到three.js的API滿足不了要求。

實現(xiàn)下雨動畫

初始化場景

因為每個3D應(yīng)用的初始化都有scene、camera、render,所以我們把這三者的初始化封裝成一個類Template,后面的應(yīng)用初始化可以通過子類繼承這個類,以便快速搭建框架。

import {    Scene,    PerspectiveCamera,    WebGLRenderer,    Vector3,    Color  } from 'three'  export default class Template {   constructor () {               //各種默認(rèn)選項      this.el = document.body      this.PCamera = {        fov: 45,       aspect: window.innerWidth / window.innerHeight,        near: 1,        far: 1000      }      this.cameraPostion = new Vector3(0, 0, 1)      this.cameraLookAt = new Vector3(0,0,0)      this.rendererColor = new Color(0x000000)      this.rendererWidth = window.innerWidth      this.rendererHeight = window.innerHeight    }    initPerspectiveCamera () {     //初始化相機,這里是透視相機      const camera = new PerspectiveCamera(        this.PCamera.fov,        this.PCamera.aspect,        this.PCamera.near,        this.PCamera.far,      )     camera.position.copy(this.cameraPostion)      camera.lookAt(this.cameraLookAt)      this.camera = camera      this.scene.add(camera)    }    initScene () {                //初始化場景      this.scene = new Scene()     }    initRenderer () {             //初始化渲染器      const renderer = new WebGLRenderer()      renderer.setClearColor(this.rendererColor)      renderer.setSize(this.rendererWidth, this.rendererHeight)     this.el.appendChild(renderer.domElement)      this.renderer = renderer    }    init () {      this.initScene()      this.initPerspectiveCamera()      this.initRenderer()    }  }

在我們的下雨動畫中,創(chuàng)建一個Director類管理動畫,它繼承自Template類??梢钥闯觯龅氖潞芮逦撼跏蓟蚣?、修改父類的默認(rèn)配置、添加物體(云層和雨滴)、添加光照(閃電也是光照形成的)、添加霧化效果、循環(huán)渲染。

//director.js  export default class Director extends Template{    constructor () {      super()      //set params      //camera      this.PCamera.fov = 60       //修改照相機的默認(rèn)視場fov      //init camera/scene/render      this.init()      this.camera.rotation.x = 1.16   //設(shè)置照相機的旋轉(zhuǎn)角度(望向天空)      this.camera.rotation.y = -0.12      this.camera.rotation.z = 0.27      //add object      this.addCloud()                  //添加云層和雨滴      this.addRainDrop()      //add light      this.initLight()                //添加光照,用PointLight模擬閃電      this.addLightning()      //add fog      this.addFog()                   //添加霧,在相機附近視野清晰,距離相機越遠(yuǎn),霧的濃度越高      //animate      this.animate()                 //requestAnimationFrame實現(xiàn)動畫    } }

創(chuàng)建不斷變換的云層

我們首先創(chuàng)建一個平面,將一小朵云做為材質(zhì),得到一個云朵物體。然后將很多云朵物體進(jìn)行疊加,得到一團云。

如何用Three.js寫一個下雨動畫

image.png

//Cloud.js  const texture = new TextureLoader().load('/images/smoke.png')  //加載云朵素材  const cloudGeo = new PlaneBufferGeometry(564, 300)   //創(chuàng)建平面幾何體  const cloudMaterial = new MeshLambertMaterial({   //圖像作為紋理貼圖,生成材質(zhì)    map: texture,    transparent: true  })  export default class Cloud {    constructor () {            const cloud = new Mesh(cloudGeo, cloudMaterial)   //生成云朵物體      cloud.material.opacity = 0.6      this.instance = cloud    }   setPosition (x,y,z) {      this.instance.position.set(x,y,z)   }    setRotation (x,y,z) {      this.instance.rotation.x = x      this.instance.rotation.y = y      this.instance.rotation.z = z    }    animate () {      this.instance.rotation.z -= 0.003            //云朵的運動是不斷繞著z軸旋轉(zhuǎn)    }  }

在Director類中,生成30個云朵物體,隨機設(shè)置它們的位置和旋轉(zhuǎn),形成鋪開和層疊的效果。在循環(huán)渲染時調(diào)用云朵物體的animate方法。

//director.js  addCloud () {    this.clouds = []    for(let i = 0; i < 30; i++){      const cloud = new Cloud()      this.clouds.push(cloud)      cloud.setPosition(Math.random() * 1000 - 460, 600, Math.random() * 500 - 400)      cloud.setRotation(1.16, -0.12, Math.random() * 360)      this.scene.add(cloud.instance)    }  }  animate () {      //cloud move      this.clouds.forEach((cloud) => {  //調(diào)用每個云朵物體的animate方法,形成整個云層的不斷變換效果        cloud.animate()      })      ...      this.renderer.render(this.scene, this.camera)      requestAnimationFrame(this.animate.bind(this))    }

環(huán)境光和閃電

同時使用了AmbientLight和DirectionalLight作為整個場景的穩(wěn)定光源,增強對現(xiàn)實場景的模擬。

//director.js  initLight () {    const ambientLight = new AmbientLight(0x555555)    this.scene.add(ambientLight)    const directionLight = new DirectionalLight(0xffeedd)    directionLight.position.set(0,0,1)    this.scene.add(directionLight)  }

用PointLight模擬閃電,首先是初始一個PointLight。

//director.js  addLightning () {    const lightning = new PointLight(0x062d89, 30, 500, 1.7)    lightning.position.set(200, 300, 100)    this.lightning = lightning    this.scene.add(lightning)  }

在循環(huán)渲染時,不斷隨機改變點光源PointLight的強度(power),形成閃爍的效果,當(dāng)強度較小,即光線暗下來時,"悄悄"改變點光源的位置,這樣就能不突兀使閃電隨機地出現(xiàn)在云層地各個位置。

//director.js  animate () {    ...    //lightning    if(Math.random() > 0.93 || this.lightning.power > 100){      if(this.lightning.power < 100){        this.lightning.position.set(          Math.random() * 400,          300 + Math.random() * 200,          100        )      }      this.lightning.power = 50 + Math.random() * 500    }    this.renderer.render(this.scene, this.camera)    requestAnimationFrame(this.animate.bind(this))  }

創(chuàng)建雨滴

創(chuàng)建雨滴用到的粒子效果。創(chuàng)建一組粒子,直觀的方法是,創(chuàng)建一個粒子物體,然后復(fù)制N個,分別定義它們的位置和旋轉(zhuǎn)。

當(dāng)你使用少量的對象時,這很有效,但是當(dāng)你想使用大量的THREE.Sprite對象時,你會很快遇到性能問題,因為每個對象需要分別由Three.js進(jìn)行管理。

Three.js提供了另一種方式來處理大量的粒子,這需要使用THREE.Points。通過THREE.Points,Three.js不再需要管理大量單個的THREE.Sprite對象,而只需管理THREE.Points實例。

使用THREE.Points,可以非常容易地創(chuàng)建很多細(xì)小的物體,用來模擬雨滴、雪花、煙和其他有趣的效果。

THREE.Points的核心思想,就是先聲明一個幾何體geom,然后確定幾何體各個頂點的位置,這些頂點的位置將會是各個粒子的位置。通過PointsMaterial確定頂點的材質(zhì)material,然后new Points(geom, material),根據(jù)傳入的幾何體和頂點材質(zhì)生成一個粒子系統(tǒng)。

粒子的移動:粒子的位置坐標(biāo)是由一組數(shù)字確定const positions = this.geom.attributes.position.array,這組數(shù)字,每三個數(shù)確定一個坐標(biāo)點(x\y\z),所以要改變粒子的X坐標(biāo),就改變positions[ 3n ] (n是粒子序數(shù));同理,Y坐標(biāo)對應(yīng)的是positions[ 3n+1 ],Z坐標(biāo)對應(yīng)的是positions[ 3n+2 ]。

//RainDrop.js  export default class RainDrop {    constructor () {      const texture = new TextureLoader().load('/images/rain-drop.png')     const material = new PointsMaterial({    //用圖片初始化頂點材質(zhì)        size: 0.8,        map: texture,        transparent: true      })       const positions = []      this.drops = 8000      this.geom = new BufferGeometry()      this.velocityY = []      for(let i = 0; i < this.drops; i++){        positions.push( Math.random() * 400 - 200 )        positions.push( Math.random() * 500 - 250 )        positions.push( Math.random() * 400 - 200 )        this.velocityY.push(0.5 + Math.random() / 2)  //初始化每個粒子的坐標(biāo)和粒子在Y方向的速度     }       //確定各個頂點的位置坐標(biāo)      this.geom.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) )        this.instance = new Points(this.geom, material)  //初始化粒子系統(tǒng)    }    animate () {      const positions = this.geom.attributes.position.array;       for(let i=0; i<this.drops * 3; i+=3){    //改變Y坐標(biāo),加速運動        this.velocityY[i/3] += Math.random() * 0.05        positions[ i + 1 ] -=  this.velocityY[i/3]        if(positions[ i + 1 ] < -200){          positions[ i + 1 ] =  200          this.velocityY[i/3] = 0.5 + Math.random() / 2        }                }      this.instance.rotation.y += 0.002        this.geom.attributes.position.needsUpdate = true    }  }

將雨滴粒子添加到場景中,并在循環(huán)渲染時,調(diào)用RainDrop的animate方法:

//director.js  addRainDrop () {    this.rainDrop = new RainDrop()    this.scene.add(this.rainDrop.instance)  }  animate () {    //rain drop move    this.rainDrop.animate()    ...    this.renderer.render(this.scene, this.camera)    requestAnimationFrame(this.animate.bind(this))  }

感謝各位的閱讀,以上就是“如何用Three.js寫一個下雨動畫”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何用Three.js寫一個下雨動畫這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

本文標(biāo)題:如何用Three.js寫一個下雨動畫
網(wǎng)頁地址:http://www.muchs.cn/article18/iheigp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)響應(yīng)式網(wǎng)站、網(wǎng)站導(dǎo)航、網(wǎng)站營銷、外貿(mào)建站、標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化