Three.js 之 9 Light 光
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第3天,點選檢視活動詳情
本系列為 Three.js journey 教程學習筆記。包含以下內容
- Three.js 之 1 Animation 動畫
- Three.js 之 2 Camera 相機
- Three.js 之 3 畫布與全屏
- Three.js 之 4 Geometry 幾何體
- Three.js 之 5 debug UI
- Three.js 之 6 Texture 紋理
- Three.js 之 7 Materials 材質
- Three.js 之 8 炫酷的 3D Text
- Three.js 之 9 Light 光
- Three.js 之 10 Shadow 投影
- Three.js 之 11 Haunted House 恐怖鬼屋
- Three.js 之 12 Particles 粒子效果
- Three.js 之 13 Galaxy 銀河效果生成器
未完待續
lights 光
我們之前學習了簡單的新增光源到場景中。接下來就詳細講講各種各樣的光源、引數以及如何使用。
建立一組幾何體
先建立一組幾何體用於接受光照。使用 MeshStandardMaterial
因為最能真實反饋光效。並將粗糙度設定為 0.4 觀察反射情況。並增加一點環境光 AmbientLight 否則會看不到物體。效果如下
若看不清楚,可以只觀察其 wireframe
完整程式碼如下
```js import * as THREE from 'three' import './style.css' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import * as dat from 'lil-gui' import stats from '../common/stats' import { listenResize, dbClkfullScreen } from '../common/utils'
// Canvas const canvas = document.querySelector('#mainCanvas') as HTMLCanvasElement
// Scene const scene = new THREE.Scene()
/* * Objects / // Material const material = new THREE.MeshStandardMaterial() material.metalness = 0 material.roughness = 0.4
// Objects const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32), material) sphere.position.set(-1.5, 0, 0)
const cube = new THREE.Mesh(new THREE.BoxGeometry(0.75, 0.75, 0.75), material)
const torus = new THREE.Mesh(new THREE.TorusGeometry(0.3, 0.2, 32, 64), material) torus.position.set(1.5, 0, 0)
const plane = new THREE.Mesh(new THREE.PlaneGeometry(5, 5), material) plane.rotation.set(-Math.PI / 2, 0, 0) plane.position.set(0, -0.65, 0)
scene.add(sphere, cube, torus, plane)
/* * Lights / const ambientLight = new THREE.AmbientLight('#ffffff', 0.5) scene.add(ambientLight)
// Size const sizes = { width: window.innerWidth, height: window.innerHeight, }
// Camera const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100) camera.position.set(1, 1, 2)
const controls = new OrbitControls(camera, canvas) controls.enableDamping = true // controls.autoRotate = true // controls.enabled = false
// Renderer const renderer = new THREE.WebGLRenderer({ canvas, }) renderer.setSize(sizes.width, sizes.height) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
listenResize(sizes, camera, renderer) dbClkfullScreen(document.body)
// Clock const clock = new THREE.Clock()
// Animations const tick = () => { stats.begin()
const elapsedTime = clock.getElapsedTime()
// Update Objects sphere.rotation.y = 0.1 * elapsedTime cube.rotation.y = 0.1 * elapsedTime torus.rotation.y = 0.1 * elapsedTime
sphere.rotation.x = 0.15 * elapsedTime cube.rotation.x = 0.15 * elapsedTime torus.rotation.x = 0.15 * elapsedTime
controls.update() // Render renderer.render(scene, camera) stats.end() requestAnimationFrame(tick) }
tick()
/* * Debug / const gui = new dat.GUI()
gui.add(material, 'metalness').min(0).max(1).step(0.0001) gui.add(material, 'roughness').min(0).max(1).step(0.0001) gui.add(material, 'wireframe') ```
AmbientLight 環境光
環境光會均勻的照亮場景中的所有物體。
環境光不能用來投射陰影,因為它沒有方向。
AmbientLight 繼承自 Light,因此具有 Light 的公共屬性
Object3D → Light → AmbientLight
因此在建構函式的宣告變數也可以直接在其示例上修改,如下
```js const ambientLight = new THREE.AmbientLight(0xffffff, 0.5) scene.add(ambientLight)
// Equals const ambientLight = new THREE.AmbientLight() ambientLight.color = new THREE.Color(0xffffff) ambientLight.intensity = 0.5 scene.add(ambientLight) ```
可以在 gui 中增加一行觀察環境光強度
js
gui.add(ambientLight, 'intensity').min(0).max(1).step(0.001)
在現實世界中,如果使用光照射一個物體,物體的背面不是全黑的,這是因為會有牆面或其他物體反光。但是在 Three.js 中,由於效能問題,沒有反光的特性,所以可以使用微弱的環境光 AmbientLight 來模擬這種反光。
DirectionalLight 平行光
平行光是沿著特定方向發射的光。這種光的表現像是無限遠,從它發出的光線都是平行的。常常用平行光來模擬太陽光 的效果; 太陽足夠遠,因此我們可以認為太陽的位置是無限遠,所以我們認為從太陽發出的光線也都是平行的。平行光可以投射陰影。
我們在 demo 上增加平行光
js
const directionalLight = new THREE.DirectionalLight('#ffffaa', 0.5)
scene.add(directionalLight)
效果如下
預設平行光是從頂部直射的,我們可以使用 position 屬性設定位置
js
const directionalLight = new THREE.DirectionalLight('#ffffaa', 0.5)
directionalLight.position.set(1, 0.25, 0)
scene.add(directionalLight)
線上 demo 連結
可掃碼訪問
可以看到光來自右側。
我們暫時不考慮光線的傳播距離,預設是來自無窮遠,併發散到無窮遠
HemisphereLight 半球光
光源直接放置於場景之上,類似環境光 AmbientLight,但光照顏色從天空光線顏色漸變到地面光線顏色。
半球光不能投射陰影。
js
const hemisphereLight = new THREE.HemisphereLight('#B71C1C', '#004D40', 0.6)
scene.add(hemisphereLight)
線上 demo 連結
可掃碼訪問
PointLight 點光源
從一個點向各個方向發射的光源。一個常見的例子是模擬一個燈泡發出的光。該光源可以投射陰影。
其特點是光源無線小,光線向各個方向傳播。 - 第一個引數 color 是顏色 - intensity 是強度。 - distance 這個距離表示從光源到光照強度為0的位置。當設定為0時,光永遠不會消失(距離無窮大)。預設值 0. - decay 沿著光照距離的衰退量。預設值 1。 在 physically correct 模式中,decay = 2。
js
PointLight( color : Integer, intensity : Float, distance : Number, decay : Float )
js
const pointLight = new THREE.PointLight(0xff9000, 0.5)
pointLight.position.set(1, 1, 1)
scene.add(pointLight)
gui 增加調節 distance 和 decay 的程式碼如下
js
pointLightFolder.add(pointLight, 'distance', 0, 100, 0.00001)
pointLightFolder.add(pointLight, 'decay', 0, 10, 0.00001)
RectAreaLight 平面光光源
平面光光源從一個矩形平面上均勻地發射光線。這種光源可以用來模擬像明亮的窗戶或者條狀燈光光源。它混合了平行光與發散光。
不支援陰影。只支援 MeshStandardMaterial 和 MeshPhysicalMaterial 兩種材質。
js
RectAreaLight( color : Integer, intensity : Float, width : Float, height : Float )
- color - (可選引數) 十六進位制數字表示的光照顏色。預設值為 0xffffff (白色)
- intensity - (可選引數) 光源強度/亮度 。預設值為 1。
- width - (可選引數) 光源寬度。預設值為 10。
- height - (可選引數) 光源高度。預設值為 10。
js
const rectAreaLight = new THREE.RectAreaLight(0x4e00ff, 10, 1, 1)
rectAreaLight.position.set(-1.5, 0, 1.5)
rectAreaLight.lookAt(new THREE.Vector3())
scene.add(rectAreaLight)
效果如下
開關 helper 效果如下
線上 demo 連結
可掃碼訪問
SpotLight 聚光燈
光線從一個點沿一個方向射出,隨著光線照射的變遠,光線圓錐體的尺寸也逐漸增大。該光源可以投射陰影。
建構函式
js
SpotLight( color : Integer, intensity : Float, distance : Float, angle : Radians, penumbra : Float, decay : Float )
- color - (可選引數) 十六進位制光照顏色。 預設值 0xffffff (白色)。
- intensity - (可選引數) 光照強度。 預設值 1。
- distance - 從光源發出光的最大距離,其強度根據光源的距離線性衰減。
- angle - 光線散射角度,最大為Math.PI/2。
- penumbra - 聚光錐的半影衰減百分比。在0和1之間的值。預設為0。
- decay - 沿著光照距離的衰減量。
js
const spotLight = new THREE.SpotLight(0x78ff00, 0.5, 10, Math.PI * 0.1, 0.25, 1)
spotLight.position.set(0, 2, 3)
scene.add(spotLight)
intensity 與 distance 調節
angle 調節
penumbra(半影衰減) 調節
decay 調節
效能考慮
光照效果很好,但是會非常消耗效能。GPU 會對其進行大量計算。
Minimal cost:
- AmbientLight
- HemisphereLight
Moderate cost:
- DirectionalLight
- PointLight
High cost:
- SpotLight
- RectAreaLight
所以要儘量少的新增燈光,就會帶來更好的效能。想要少的燈光,但又想有很好的光效該怎麼辦呢?可以考慮 Baking 光照的方案。
Baking 烘焙光照
原理是將光照烘焙到貼圖紋理(Texture)中,這個過程可以在 3D 建模軟體中實現。但不足的是,不能移動光線,因為根本沒有光,都是再貼圖紋理中。後續會深入學習。
小結
本節學習了 Three.js 內建的所有光效,並學習了其 Helper 的使用。瞭解了其效能的排序,以及光照烘焙的方案。下一節將學習投影。