日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
用Three.js寫一個(gè)下雨動(dòng)畫

 最近看了《Three.js開發(fā)指南》,深刻地意識(shí)到光看不練跟沒看差不多,所以就練習(xí)寫了這個(gè)小動(dòng)畫。

創(chuàng)新互聯(lián)建站主要從事成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)瓊結(jié),十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):028-86922220

項(xiàng)目地址:

https://github.com/alasolala/threejs-tutorial.git

前置知識(shí)

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)、照相機(jī)(Camera),以及你在場景中創(chuàng)建的物體,光照。

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

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

image.png

 
 
 
 
  1.  
  2.  
  3.  
  4.    
  5.    
  6.    
  7.   room 
  8.  
  9.   
 
  •    
  •    
  •  
  • 場景(Scene)

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

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

    照相機(jī)(Camera)

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

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

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

     
     
     
     
    1. const camera = new THREE.PerspectiveCamera( 
    2.   45,  
    3.   window.innerWidth / window.innerHeight, 
    4.   0.1, 
    5.   1000 
    6. camera.position.set(-30, 40, 30) 
    7. camera.lookAt(0,0,0) 
    8. scene.add(camera) 

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

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

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

    camera.lookAt(0,0,0)確定了照相機(jī)聚焦點(diǎn),該點(diǎn)和照相機(jī)坐標(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"形,大拇指向右,食指向上。其余的手指指向自己,這樣就建立了一個(gè)右手坐標(biāo)系。

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

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

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

    物體

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

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

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

    光照

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

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

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

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

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

    渲染器(Renderer)

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

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

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

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

    實(shí)現(xiàn)下雨動(dòng)畫

    初始化場景

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

     
     
     
     
    1. import { 
    2.   Scene, 
    3.   PerspectiveCamera, 
    4.   WebGLRenderer, 
    5.   Vector3, 
    6.   Color 
    7. } from 'three' 
    8. export default class Template {
    9.   constructor () {               //各種默認(rèn)選項(xiàng) 
    10.     this.el = document.body 
    11.     this.PCamera = { 
    12.       fov: 45,
    13.       aspect: window.innerWidth / window.innerHeight, 
    14.       near: 1, 
    15.       far: 1000 
    16.     } 
    17.     this.cameraPostion = new Vector3(0, 0, 1) 
    18.     this.cameraLookAt = new Vector3(0,0,0) 
    19.     this.rendererColor = new Color(0x000000) 
    20.     this.rendererWidth = window.innerWidth 
    21.     this.rendererHeight = window.innerHeight 
    22.   } 
    23.   initPerspectiveCamera () {     //初始化相機(jī),這里是透視相機(jī) 
    24.     const camera = new PerspectiveCamera( 
    25.       this.PCamera.fov, 
    26.       this.PCamera.aspect, 
    27.       this.PCamera.near, 
    28.       this.PCamera.far, 
    29.     )
    30.     camera.position.copy(this.cameraPostion) 
    31.     camera.lookAt(this.cameraLookAt) 
    32.     this.camera = camera 
    33.     this.scene.add(camera) 
    34.   } 
    35.   initScene () {                //初始化場景 
    36.     this.scene = new Scene()  
    37.   } 
    38.   initRenderer () {             //初始化渲染器 
    39.     const renderer = new WebGLRenderer() 
    40.     renderer.setClearColor(this.rendererColor) 
    41.     renderer.setSize(this.rendererWidth, this.rendererHeight)
    42.     this.el.appendChild(renderer.domElement) 
    43.     this.renderer = renderer 
    44.   } 
    45.   init () { 
    46.     this.initScene() 
    47.     this.initPerspectiveCamera() 
    48.     this.initRenderer() 
    49.   } 
    50. }

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

     
     
     
     
    1. //director.js 
    2. export default class Director extends Template{ 
    3.   constructor () { 
    4.     super() 
    5.     //set params 
    6.     //camera 
    7.     this.PCamera.fov = 60       //修改照相機(jī)的默認(rèn)視場fov 
    8.     //init camera/scene/render 
    9.     this.init() 
    10.     this.camera.rotation.x = 1.16   //設(shè)置照相機(jī)的旋轉(zhuǎn)角度(望向天空) 
    11.     this.camera.rotation.y = -0.12 
    12.     this.camera.rotation.z = 0.27 
    13.     //add object 
    14.     this.addCloud()                  //添加云層和雨滴 
    15.     this.addRainDrop() 
    16.     //add light 
    17.     this.initLight()                //添加光照,用PointLight模擬閃電 
    18.     this.addLightning()
    19.      //add fog 
    20.     this.addFog()                   //添加霧,在相機(jī)附近視野清晰,距離相機(jī)越遠(yuǎn),霧的濃度越高 
    21.     //animate 
    22.     this.animate()                 //requestAnimationFrame實(shí)現(xiàn)動(dòng)畫 
    23.   }
    24. }

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

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

    image.png

     
     
     
     
    1. //Cloud.js 
    2. const texture = new TextureLoader().load('/images/smoke.png')  //加載云朵素材 
    3. const cloudGeo = new PlaneBufferGeometry(564, 300)   //創(chuàng)建平面幾何體 
    4. const cloudMaterial = new MeshLambertMaterial({   //圖像作為紋理貼圖,生成材質(zhì) 
    5.   map: texture, 
    6.   transparent: true 
    7. }) 
    8. export default class Cloud { 
    9.   constructor () {       
    10.     const cloud = new Mesh(cloudGeo, cloudMaterial)   //生成云朵物體 
    11.     cloud.material.opacity = 0.6 
    12.     this.instance = cloud 
    13.   }
    14.   setPosition (x,y,z) { 
    15.     this.instance.position.set(x,y,z)
    16.   } 
    17.   setRotation (x,y,z) { 
    18.     this.instance.rotation.x = x 
    19.     this.instance.rotation.y = y 
    20.     this.instance.rotation.z = z 
    21.   } 
    22.   animate () { 
    23.     this.instance.rotation.z -= 0.003            //云朵的運(yùn)動(dòng)是不斷繞著z軸旋轉(zhuǎn) 
    24.   } 
    25. }

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

     
     
     
     
    1. //director.js 
    2. addCloud () { 
    3.   this.clouds = [] 
    4.   for(let i = 0; i < 30; i++){ 
    5.     const cloud = new Cloud() 
    6.     this.clouds.push(cloud) 
    7.     cloud.setPosition(Math.random() * 1000 - 460, 600, Math.random() * 500 - 400) 
    8.     cloud.setRotation(1.16, -0.12, Math.random() * 360) 
    9.     this.scene.add(cloud.instance) 
    10.   } 
    11. animate () { 
    12.     //cloud move 
    13.     this.clouds.forEach((cloud) => {  //調(diào)用每個(gè)云朵物體的animate方法,形成整個(gè)云層的不斷變換效果 
    14.       cloud.animate() 
    15.     }) 
    16.     ... 
    17.     this.renderer.render(this.scene, this.camera) 
    18.     requestAnimationFrame(this.animate.bind(this)) 
    19.   }

    環(huán)境光和閃電

    同時(shí)使用了AmbientLight和DirectionalLight作為整個(gè)場景的穩(wěn)定光源,增強(qiáng)對(duì)現(xiàn)實(shí)場景的模擬。

     
     
     
     
    1. //director.js 
    2. initLight () { 
    3.   const ambientLight = new AmbientLight(0x555555) 
    4.   this.scene.add(ambientLight) 
    5.   const directionLight = new DirectionalLight(0xffeedd) 
    6.   directionLight.position.set(0,0,1) 
    7.   this.scene.add(directionLight) 
    8. }

    用PointLight模擬閃電,首先是初始一個(gè)PointLight。

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

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

     
     
     
     
    1. //director.js 
    2. animate () { 
    3.   ... 
    4.   //lightning 
    5.   if(Math.random() > 0.93 || this.lightning.power > 100){ 
    6.     if(this.lightning.power < 100){ 
    7.       this.lightning.position.set( 
    8.         Math.random() * 400, 
    9.         300 + Math.random() * 200, 
    10.         100 
    11.       ) 
    12.     } 
    13.     this.lightning.power = 50 + Math.random() * 500 
    14.   } 
    15.   this.renderer.render(this.scene, this.camera) 
    16.   requestAnimationFrame(this.animate.bind(this)) 
    17. }

    創(chuàng)建雨滴

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

    當(dāng)你使用少量的對(duì)象時(shí),這很有效,但是當(dāng)你想使用大量的THREE.Sprite對(duì)象時(shí),你會(huì)很快遇到性能問題,因?yàn)槊總€(gè)對(duì)象需要分別由Three.js進(jìn)行管理。

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

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

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

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

     
     
     
     
    1. //RainDrop.js 
    2. export default class RainDrop { 
    3.   constructor () { 
    4.     const texture = new TextureLoader().load('/images/rain-drop.png')
    5.     const material = new PointsMaterial({    //用圖片初始化頂點(diǎn)材質(zhì) 
    6.       size: 0.8, 
    7.       map: texture, 
    8.       transparent: true 
    9.     })  
    10.     const positions = [] 
    11.     this.drops = 8000 
    12.     this.geom = new BufferGeometry() 
    13.     this.velocityY = [] 
    14.     for(let i = 0; i < this.drops; i++){ 
    15.       positions.push( Math.random() * 400 - 200 ) 
    16.       positions.push( Math.random() * 500 - 250 ) 
    17.       positions.push( Math.random() * 400 - 200 ) 
    18.       this.velocityY.push(0.5 + Math.random() / 2)  //初始化每個(gè)粒子的坐標(biāo)和粒子在Y方向的速度
    19.     } 
    20.      //確定各個(gè)頂點(diǎn)的位置坐標(biāo) 
    21.     this.geom.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) )   
    22.     this.instance = new Points(this.geom, material)  //初始化粒子系統(tǒng) 
    23.   } 
    24.   animate () { 
    25.     const positions = this.geom.attributes.position.array; 
    26.      for(let i=0; i
    27.       this.velocityY[i/3] += Math.random() * 0.05 
    28.       positions[ i + 1 ] -=  this.velocityY[i/3] 
    29.       if(positions[ i + 1 ] < -200){ 
    30.         positions[ i + 1 ] =  200 
    31.         this.velocityY[i/3] = 0.5 + Math.random() / 2 
    32.       }           
    33.     } 
    34.     this.instance.rotation.y += 0.002  
    35.      this.geom.attributes.position.needsUpdate = true 
    36.   } 
    37. }

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

     
     
     
     
    1. //director.js 
    2. addRainDrop () { 
    3.   this.rainDrop = new RainDrop() 
    4.   this.scene.add(this.rainDrop.instance) 
    5. animate () { 
    6.   //rain drop move 
    7.   this.rainDrop.animate() 
    8.   ... 
    9.   this.renderer.render(this.scene, this.camera) 
    10.   requestAnimationFrame(this.animate.bind(this)) 

    分享題目:用Three.js寫一個(gè)下雨動(dòng)畫
    文章URL:http://www.dlmjj.cn/article/dhsdiho.html