Three.js 之 13 Galaxy 銀河效果生成器
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第7天,點選檢視活動詳情
本系列為 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 銀河效果生成器
未完待續
接下來,我們學習一下如何使用粒子,本節將開發一個銀河生成器,使用粒子效果生成各種各樣的銀河效果。
建立粒子
根據上一節建立一個粒子立方體,並加入 debug UI,設定尺寸和數量
```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()
// 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(4, 1.8, 4)
const controls = new OrbitControls(camera, canvas) controls.enableDamping = true controls.zoomSpeed = 0.3
/* * Galaxy / const parameters = { count: 1000, size: 0.02, }
let geometry: THREE.BufferGeometry
let material: THREE.PointsMaterial
let points: THREE.Points
const generatorGalaxy = () => { if (points) { geometry.dispose() material.dispose() scene.remove(points) }
// Geometry geometry = new THREE.BufferGeometry() const position = new Float32Array(parameters.count * 3) for (let i = 0; i < parameters.count; i += 1) { const i3 = i * 3 position[i3] = (Math.random() - 0.5) * 3 position[i3 + 1] = (Math.random() - 0.5) * 3 position[i3 + 2] = (Math.random() - 0.5) * 3 } geometry.setAttribute('position', new THREE.BufferAttribute(position, 3))
// Material material = new THREE.PointsMaterial({ size: parameters.size, sizeAttenuation: true, depthWrite: false, blending: THREE.AdditiveBlending, })
points = new THREE.Points(geometry, material) scene.add(points) }
generatorGalaxy()
// 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)
// Animations const tick = () => { stats.begin()
controls.update()
// Render renderer.render(scene, camera) stats.end() requestAnimationFrame(tick) }
tick()
/* * Debug / const gui = new dat.GUI()
gui.add(controls, 'autoRotate') gui.add(controls, 'autoRotateSpeed', 0.1, 10, 0.01)
gui.add(parameters, 'count', 100, 1000000, 100).onFinishChange(generatorGalaxy) gui.add(parameters, 'size', 0.001, 0.1, 0.001).onFinishChange(generatorGalaxy) ```
形狀
半徑
增加半徑配置引數
js
/**
* Galaxy
*/
const parameters = {
count: 1000,
size: 0.02,
radius: 5,
}
沿著 x 軸半徑內隨機渲染
js
// Geometry
geometry = new THREE.BufferGeometry()
const position = new Float32Array(parameters.count * 3)
for (let i = 0; i < parameters.count; i += 1) {
const i3 = i * 3
const radius = Math.random() * parameters.radius
position[i3] = radius
position[i3 + 1] = 0
position[i3 + 2] = 0
}
geometry.setAttribute('position', new THREE.BufferAttribute(position, 3))
分支
增加配置
js
/**
* Galaxy
*/
const parameters = {
count: 1000,
size: 0.02,
radius: 5,
branches: 3,
}
修改點的位置
js
// Geometry
geometry = new THREE.BufferGeometry()
const position = new Float32Array(parameters.count * 3)
for (let i = 0; i < parameters.count; i += 1) {
const i3 = i * 3
const radius = Math.random() * parameters.radius
const branchesAngle = ((i % parameters.branches) / parameters.branches) * Math.PI * 2
position[i3] = Math.cos(branchesAngle) * radius
position[i3 + 1] = 0
position[i3 + 2] = Math.sin(branchesAngle) * radius
}
geometry.setAttribute('position', new THREE.BufferAttribute(position, 3))
偏轉角度
增加配置
js
/**
* Galaxy
*/
const parameters = {
count: 1000,
size: 0.02,
radius: 5,
branches: 3,
spin: 1,
}
增加偏轉角度
```js // Geometry geometry = new THREE.BufferGeometry() const position = new Float32Array(parameters.count * 3) for (let i = 0; i < parameters.count; i += 1) { const i3 = i * 3 const radius = Math.random() * parameters.radius const branchesAngle = ((i % parameters.branches) / parameters.branches) * Math.PI * 2 const spinAngle = radius * parameters.spin
position[i3] = Math.cos(branchesAngle + spinAngle) * radius
position[i3 + 1] = 0
position[i3 + 2] = Math.sin(branchesAngle + spinAngle) * radius
} geometry.setAttribute('position', new THREE.BufferAttribute(position, 3)) ```
隨機擴散
增加如下程式碼
js
randomness: 0.2,
```js const randomX = (Math.random() - 0.5) * parameters.randomness * radius const randomY = (Math.random() - 0.5) * parameters.randomness * radius const randomZ = (Math.random() - 0.5) * parameters.randomness * radius
position[i3] = Math.cos(branchesAngle + spinAngle) * radius + randomX
position[i3 + 1] = randomY
position[i3 + 2] = Math.sin(branchesAngle + spinAngle) * radius + randomZ
```
但仔細看效果還不是很好,我們藉助冪函式來提升效果
js
randomnessPower: 3,
js
const randomX = Math.random() ** parameters.randomnessPower
* (Math.random() < 0.5 ? 1 : -1)
* parameters.randomness
* radius
const randomY = Math.random() ** parameters.randomnessPower
* (Math.random() < 0.5 ? 1 : -1)
* parameters.randomness
* radius
const randomZ = Math.random() ** parameters.randomnessPower
* (Math.random() < 0.5 ? 1 : -1)
* parameters.randomness
* radius
效果好多了
顏色
為了有更好的效果我們新增漸變色的感覺
js
insideColor: '#ff6030',
outsideColor: '#1b3984',
js
gui.addColor(parameters, 'insideColor').onFinishChange(generatorGalaxy)
gui.addColor(parameters, 'outsideColor').onFinishChange(generatorGalaxy)
我們要為每個頂點設定顏色,所以需要將 vertexColors
設定為 true
js
// Material
material = new THREE.PointsMaterial({
size: parameters.size,
sizeAttenuation: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
vertexColors: true,
})
然後新增顏色屬性,如上一節課學習的那樣
js
const position = new Float32Array(parameters.count * 3)
const colors = new Float32Array(parameters.count * 3)
```js for (let i = 0; i < parameters.count; i += 1) {
...
colors[i3] = 1
colors[i3 + 1] = 0
colors[i3 + 2] = 0
} geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)) ```
接下來設定漸變色
.lerp ( color : Color, alpha : Float ) : this
color - 用於收斂的顏色。 alpha - 介於0到1的數字。
將該顏色的RGB值線性插值到傳入引數的RGB值。alpha引數可以被認為是兩種顏色之間的比例值,其中0是當前顏色和1.0是第一個引數的顏色。
.lerpColors ( color1 : Color, color2 : Color, alpha : Float ) : this
color1 - 開始的顏色。 color2 - 結束收斂的顏色。 alpha - 介於0到1的數字。
```js const mixedColor = colorInside.clone() mixedColor.lerp(colorOutside, radius / parameters.radius)
...
colors[i3] = mixedColor.r
colors[i3 + 1] = mixedColor.g
colors[i3 + 2] = mixedColor.b
```
最終效果如下
線上 demo 連結
可掃碼訪問
小結
本節我們學習瞭如何設定各種debug引數,來調節我們的銀河效果。對粒子特效有了更深入的研究。動畫部分我們會在後續的課程中再詳細學習。