はじめに
音声ビジュアライザーを作るとき、パーティクルだけでなく、ワイヤーフレームメッシュもかなり相性がよいです。
音量に合わせてメッシュが膨らみ、低域で強く反応し、中域や高域でゆっくり回転すると、それだけで音に反応している感じが出ます。
今回は React Three Fiber を使って、音に反応するワイヤーフレームメッシュを作ります。
作るもの
音声解析で取得した以下の値を使います。
type AudioFeatures = {
volume: number
bass: number
mids: number
highs: number
waveform: Float32Array
}
この値を使って、メッシュの以下を変化させます。
volume: 全体のスケール
bass: 低域による強い膨張
mids: ゆっくりした回転
highs: 細かい回転や発光
基本のメッシュ
まずは React Three Fiber でメッシュを表示します。
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
type AudioFeatures = {
volume: number
bass: number
mids: number
highs: number
waveform: Float32Array
}
type ReactiveMeshProps = {
featuresRef: React.MutableRefObject<AudioFeatures>
}
export function ReactiveMesh({ featuresRef }: ReactiveMeshProps) {
const meshRef = useRef<THREE.Mesh>(null)
return (
<mesh ref={meshRef}>
<icosahedronGeometry args={[1.6, 3]} />
<meshStandardMaterial wireframe />
</mesh>
)
}
ここでは icosahedronGeometry を使っています。
wireframe を true にすると、立体の線だけが表示されます。
useFrameで毎フレーム更新する
音に反応させるには、useFrame の中でメッシュを更新します。
useFrame(() => {
const mesh = meshRef.current
if (!mesh) {
return
}
const { volume, bass, mids, highs } = featuresRef.current
const scale = 1 + volume * 0.3 + bass * 0.45
mesh.scale.lerp(
new THREE.Vector3(scale, scale, scale),
0.12
)
mesh.rotation.x += 0.003 + mids * 0.01
mesh.rotation.y += 0.004 + highs * 0.008
})
volume と bass でスケールを変えています。
mids と highs は回転に使っています。
lerpで急な変化をなめらかにする
音声解析値は急に変化することがあります。
その値をそのままスケールに使うと、見た目がガタガタする場合があります。
そこで lerp を使っています。
mesh.scale.lerp(
new THREE.Vector3(scale, scale, scale),
0.12
)
lerp を使うと、現在の値から目標値に少しずつ近づきます。
音声ビジュアライザーでは、急激すぎる反応を少し抑えると見やすくなります。
色や発光も音に合わせる
マテリアルの色や発光も音に合わせられます。
const material = mesh.material as THREE.MeshStandardMaterial
material.emissiveIntensity = 0.2 + highs * 1.5
highs は細かい音に反応しやすいので、発光感に使うと分かりやすいです。
ただし、強くしすぎると画面がうるさくなるので、少し控えめにするのが扱いやすいです。
完成形
まとめると、以下のようなコンポーネントになります。
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
import * as THREE from 'three'
type AudioFeatures = {
volume: number
bass: number
mids: number
highs: number
waveform: Float32Array
}
type ReactiveMeshProps = {
featuresRef: React.MutableRefObject<AudioFeatures>
}
export function ReactiveMesh({ featuresRef }: ReactiveMeshProps) {
const meshRef = useRef<THREE.Mesh>(null)
const targetScale = new THREE.Vector3()
useFrame(() => {
const mesh = meshRef.current
if (!mesh) {
return
}
const { volume, bass, mids, highs } = featuresRef.current
const scale = 1 + volume * 0.3 + bass * 0.45
targetScale.set(scale, scale, scale)
mesh.scale.lerp(targetScale, 0.12)
mesh.rotation.x += 0.003 + mids * 0.01
mesh.rotation.y += 0.004 + highs * 0.008
const material = mesh.material as THREE.MeshStandardMaterial
material.emissiveIntensity = 0.15 + highs * 1.2
})
return (
<mesh ref={meshRef}>
<icosahedronGeometry args={[1.6, 3]} />
<meshStandardMaterial
wireframe
emissive="#ffffff"
emissiveIntensity={0.2}
/>
</mesh>
)
}
音の役割を見た目に割り当てる
ビジュアライザーでは、音の特徴量ごとに見た目の役割を分けると調整しやすいです。
volume:
全体の大きさ
bass:
低域の衝撃、膨張
mids:
流れ、回転、うねり
highs:
細かい揺れ、発光、ちらつき
全部の値を全部の表現に使うと、何が何に反応しているのか分かりづらくなります。
役割を分けることで、音楽的に見えやすくなります。
派手にしすぎない
音声ビジュアライザーは、係数を上げればすぐ派手になります。
const scale = 1 + bass * 2
ただ、派手にしすぎると、常に大きく動いてしまい、逆に音楽の変化が見えづらくなります。
最初は小さめの値から調整した方がよいです。
const scale = 1 + volume * 0.3 + bass * 0.45
音のピークで少し大きく動くくらいの方が、見ていて疲れにくいです。
まとめ
React Three Fiberを使うと、音声解析値を使って3Dメッシュを簡単に動かせます。
今回作ったワイヤーフレームメッシュでは、
- volumeで全体のスケール
- bassで低域の膨張
- midsで回転
- highsで細かい動きや発光
を制御しました。
音に反応する3D表現を作るときは、まずシンプルなメッシュから始めると調整しやすいです。
パーティクルやシェーダーに進む前の最初のビジュアライザーとして、ワイヤーフレームメッシュはかなり扱いやすいと思いました。