iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 17
1
Modern Web

用 Three.js 來當個創世神系列 第 17

用 Three.js 來當個創世神 (16):專案實作#8 - 粒子特效場景

  • 分享至 

  • xImage
  •  

今天將利用前兩天製作的雪花特效,套用至專案中,除了調整邊界值外,還會試著更換粒子貼圖來達到不一樣場景的效果!

16_front

Photo by Fancycrave on Unsplash


這是本系列第 16 篇,如果還沒看過第 15 篇可以點以下連結前往:

用 Three.js 來當個創世神 (15):粒子系統 - 雪花 Part.2


今日目標

16_demo_1

苦力怕:我住的城巿從不下雪,記憶卻堆滿冷的感覺。

請參考完整原始碼成果展示

鐵人賽寫著寫著轉眼間已經十一月,看著苦力怕的世界下起雪來,才想到離聖誕節也不遠了。啊,剛好應景,老師,音樂下,點播一首 Eason 的「聖誕結」!

今天的目標是來將前兩天做的雪花特效,加到苦力怕的場景中,除了一些邊界參數的微調外,也試著加入幾個有趣的小互動,讀者可以透過右上角的控制面板來「播放背景音樂」及「更換粒子特效場景」,感覺這樣的場景不來首歌真是太可惜了,下面就開始來實作吧!

 

專案實作

基本上今天的內容不太複雜,主要是整合第 12 篇第 15 篇的內容,調整邊界值與粒子數之外,加上一些適合的功能。

1. 整合動畫與雪花場景

// 粒子系統全域變數宣告
const particleCount = 15000
let points
let material
const textureLoader = new THREE.TextureLoader()
const snowTexture = textureLoader.load('./snowflake.png')
// 粒子系統初始化
function createPoints() {
  const geometry = new THREE.Geometry()

  material = new THREE.PointsMaterial({
    size: 5,
    map: snowTexture,
    blending: THREE.AdditiveBlending,
    // depthTest: false, // 遮蔽效果
    transparent: true,
    opacity: 0.5
  })

  const range = 300
  for (let i = 0; i < particleCount; i++) {
    const x = THREE.Math.randInt(-range / 2, range / 2)
    const y = THREE.Math.randInt(0, range * 20)
    const z = THREE.Math.randInt(-range / 2, range / 2)
    const point = new THREE.Vector3(x, y, z)
    point.velocityX = THREE.Math.randFloat(-0.16, 0.16)
    point.velocityY = THREE.Math.randFloat(0.1, 0.3)
    geometry.vertices.push(point)
  }

  points = new THREE.Points(geometry, material)
  scene.add(points)
}
// 粒子系統動畫
function pointsAnimation() {
  points.geometry.vertices.forEach(function(v) {
    if (v.y >= -7) {
      v.x = v.x - v.velocityX
      v.y = v.y - v.velocityY
    }
    if (v.x <= -150 || v.x >= 150) v.velocityX = v.velocityX * -1
  })

  points.geometry.verticesNeedUpdate = true
}

首先將初始化粒子系統的函式加入,並調整邊界值,因為後面粒子動畫的部分要設計成落在地板上就靜止在地面上,形成積雪的感覺,為了不要讓雪太快下完,這邊讓粒子多一點(15000 顆),並讓頂點 y 座標隨機值(降雪起源點邊界)設定高一點(6000),這場雪大概足以下六分鐘左右。

另外這邊有個筆者解不掉的問題,就是前面有提到粒子材質中有個屬性 depthTest,將它關掉可以讓雪花重疊時不會有背景遮擋的違和感,但因為關掉了遮擋的效果就會穿透其他物體,如下圖:

16_q

所以權衡之下暫時還是先開起來,讀者若有更好的解法歡迎留言解答,感恩。

更新:後來找到這篇文章,就試著加上 depthWrite: false 就正常了!詳細的原因應可參考這篇

 

2. 苦力怕隨機走動及移動點光源

另外為了讓苦力怕能在雪景中有散步的效果,將地板加大外,稍微調整了動畫,讓它能隨機走動,將原本回原點的動畫改成隨機移動(見程式碼中 handleNewTweenBackTarget),但轉身的動畫有些地方不太對,但這邊就先不細調動畫了,畢竟主角是粒子特效。

更新:轉身動畫不順暢是因為目前是在 15 秒內,同時移動與轉身,要流暢就需將流程改為先 1 秒轉身,再做 14 秒移動。

然後為了增加一點苦力怕漫步在雪景中的孤寂感,特別在補間動畫的地方加了「移動點光源」,跟隨著苦力怕一起移動。

 

3. 背景音樂

這是個心血來潮的部分,就是覺得這樣的場景、這樣的燈光與這樣欲說還休的表情,就該來一首「聖誕結」,實作上也不難,就是在 HTML 加入 audio tag,並且利用 dat.GUI 來做播放與暫停的功能。

const sound = document.querySelector('audio')
let musicPlayback = false
...
this.togglePlayMusic = function() {
  if (musicPlayback) {
    sound.pause()
    musicPlayback = false
  } else {
    sound.play()
    musicPlayback = true
  }
}

 

4. 雨水材質

16_demo_2

苦力怕:臉上的,揪竟是雨水還是淚水?

因為下雨跟下雪的動畫都是差不多的,最後試著來做個場景切換,將材質的雪花貼圖換成雨滴貼圖:

raindrop圖源

並透過 dat.GUI 切換貼圖及粒子尺寸:

this.changeScene = function() {
  if (sceneType === 'SNOW') {
    material.map = rainTexture
    material.size = 2
    sceneType = 'RAIN'
  } else {
    material.map = snowTexture
    material.size = 5
    sceneType = 'SNOW'
  }
}

 

今日小結

今天將前面專案套上了前兩天做的雪花特效,並試著更換雨滴貼圖就能有另一種效果,也利用 dat.GUI 做了一些有趣的互動,替粒子特效場景做個收尾,明天將來挑戰另一個粒子系統的應用 —— 爆炸,我們明天見!

最後再附上今天的完整原始碼成果展示


上一篇
用 Three.js 來當個創世神 (15):粒子系統 - 雪花 Part.2
下一篇
用 Three.js 來當個創世神 (17):粒子系統 - 爆炸
系列文
用 Three.js 來當個創世神31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言