Three.js 之 13 Galaxy 銀河效果生成器

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第7天,點選檢視活動詳情

本系列為 Three.js journey 教程學習筆記。包含以下內容

未完待續

接下來,我們學習一下如何使用粒子,本節將開發一個銀河生成器,使用粒子效果生成各種各樣的銀河效果。

建立粒子

根據上一節建立一個粒子立方體,並加入 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 連結

可掃碼訪問

demo 原始碼

小結

本節我們學習瞭如何設定各種debug引數,來調節我們的銀河效果。對粒子特效有了更深入的研究。動畫部分我們會在後續的課程中再詳細學習。