今天將延續昨天的光源主題,在專案中實作光影效果,並透過移動點光源的動畫,讓光影效果的內容更有趣!
Photo by Kristine Weilert on Unsplash
這是本系列第 10 篇,如果還沒看過第 09 篇可以點以下連結前往:
用 Three.js 來當個創世神 (09):光源(Light)
昨天學習了四種基本光源的內容,並透過簡單的練習理解實際的用法後,今天的目標就是替場景加上適當的光源讓畫面更豐富並且產生影子的效果,為了讓畫面更有趣一些,還加上了一個移動點光源:
要讓場景有光影效果,首先要對場景中的各個元素進行投影的設定:
renderer.shadowMap.enabled = true // 設定需渲染陰影效果
renderer.shadowMap.type = 2 // THREE.PCFSoftShadowMap
首先最重要的是要在渲染器設定的地方將 renderer.shadowMap.enabled
把陰影效果啟用,不然後面都不會出現陰影。
而另外一個 renderer.shadowMap.type
是設定陰影貼圖的種類,總共有三種可以設定:
可以簡單理解為陰影的毛邊優化,調整成 THREE.PCFSoftShadowMap
影子會看起來比較圓滑。
plane.receiveShadow = true
這邊地板終於派上用場了,記得要在地板處將 receiveShadow
這個屬性打開才會接收其他元素投影的效果。
// 苦力怕投影設定,利用 traverse 遍歷各個子元件設定陰影
this.creeper.traverse(function(object) {
if (object instanceof THREE.Mesh) {
object.castShadow = true
object.receiveShadow = true
}
})
最後是我們的主角苦力怕也需要設定能夠有投影的效果,因此這邊在苦力怕物件中也加上以上設定開啟投影效果。
這邊有個 traverse
方法,這是 THREE.Scene
中提供一個用來遍歷目標物件(creeper)及其所有後代(head、body、feet)的方法,透過傳入的 function,可以對苦力怕底下的所有子元件都設定陰影效果。
// 設置環境光提供輔助柔和白光
let ambientLight = new THREE.AmbientLight(0x404040)
scene.add(ambientLight)
// 設置聚光燈幫忙照亮物體
let spotLight = new THREE.SpotLight(0xf0f0f0)
spotLight.position.set(-10, 30, 20)
spotLight.castShadow = true
scene.add(spotLight)
加上光源的方法跟加昨天概念相同,這邊就不多作介紹。
只是簡單幾行添加光源與陰影有點太無趣了,所以再加了一個移動的點光源來體驗一下光影互動的效果。
// 移動點光源
pointLight = new THREE.PointLight(0xccffcc, 1, 100) // 顏色, 強度, 距離
pointLight.castShadow = true // 投影
scene.add(pointLight)
// 小球體模擬點光源實體
const sphereLightGeo = new THREE.SphereGeometry(0.3)
const sphereLightMat = new THREE.MeshBasicMaterial({ color: 0xccffcc })
sphereLightMesh = new THREE.Mesh(sphereLightGeo, sphereLightMat)
sphereLightMesh.castShadow = true
sphereLightMesh.position.y = 16
scene.add(sphereLightMesh)
先在 Init()
中宣告點光源與模擬光源實體的小球體到場景中,再來要讓小球移動的話就需要在 render()
中加上動畫效果:
// 點光源繞 Y 軸旋轉動畫
function pointLightAnimation() {
if (rotateAngle > 2 * Math.PI) {
rotateAngle = 0 // 超過 360 度後歸零
} else {
rotateAngle += 0.03 // 遞增角度
}
// 光源延橢圓軌道繞 Y 軸旋轉
sphereLightMesh.position.x = 8 * Math.cos(rotateAngle)
sphereLightMesh.position.z = 4 * Math.sin(rotateAngle)
// 點光源位置與球體同步
pointLight.position.copy(sphereLightMesh.position)
}
function render() {
...
pointLightAnimation() // update
requestAnimationFrame(render)
renderer.render(scene, camera)
}
由於是繞 y 軸旋轉,所以要讓小球體的 (x, z)
座標隨著遞增的角度產生繞行圓心的效果,這個座標就會是 (r * cos(θ), r * sin(θ))
,而角度會隨時間遞增並且在超過 360 度後歸零重算,另外這邊讓 x 與 z 所繞的半徑不同,看起來就會有橢圓軌道的效果。
今天從昨天學到的基礎光源,利用專案實作學習如何應用這幾種光源,並且還練習使用點光源做出有趣的動畫效果,相信各位是意猶未盡,明天將打鐵趁熱,延續動畫的主題,讓苦力怕動起來,我們明天見!