Three.js 和音频结合
介绍
今天带来的是 Three.js 和音频结合的教程,我们将会学习如何在 Three.js 中使用音频。
先看下效果,随着音乐的节奏,立方体的大小和颜色会发生变化,这就是音频可视化的效果。
- THREE.AudioListener:这是音频监听器的核心类,负责接收和处理来自声音源的音频信号。通常,
THREE.AudioListener
被添加到相机中,这样它就能够根据相机的位置接收声音。 - THREE.Audio:该类代表一个音频对象,可以加载和播放音频文件。音频文件可以是
MP3
或OGG
格式,加载完成后,我们可以控制音频的播放、暂停、音量等。 - THREE.AudioLoader:这是加载音频文件的工具类,它通过
load
方法异步加载音频文件,并返回一个音频缓冲区,这样我们就可以将音频添加到THREE.Audio
中进行播放。 - THREE.AudioAnalyser:该类用于分析音频的频率数据,能够实时读取音频数据并将其转化为可用于可视化的格式。在音频可视化中,
THREE.AudioAnalyser
常常被用来获取音频的频谱数据,并将其应用到 3D 场景中的对象上,从而实现音频可视化效果。
代码实现
创建基础场景
首先,我们通过 THREE.Scene() 创建一个场景实例,然后使用 THREE.AmbientLight 和 THREE.DirectionalLight 创建光源,光源可以照亮场景中的物体。环境光提供均匀的光照,方向光模拟太阳光,确保物体能够清晰可见。
const scene = new THREE.Scene();
// 环境光和方向光
const ambientLight = new THREE.AmbientLight(0x404040);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 10);
scene.add(ambientLight, directionalLight);
加载和播放音频
我们通过 THREE.AudioLoader() 加载音频文件,并创建一个 THREE.Audio 对象来控制音频播放。我们使用 THREE.AudioListener() 来监听音频,并将其附加到相机上,使得音频播放与相机位置关联。音频加载完成后,我们还需要使用 THREE.AudioAnalyser() 来分析音频数据,为后续的可视化效果提供数据支持。
const audioLoader = new THREE.AudioLoader();
audioLoader.load(
fearlessAssets, // 音频文件路径
(buffer) => {
const listener = new THREE.AudioListener();
const audio = new THREE.Audio(listener);
audio.setBuffer(buffer);
audio.setVolume(1);
audio.setLoop(true);
camera.add(listener); // 将 listener 添加到相机中
// 创建音频分析器
const audioAnalyser = new THREE.AudioAnalyser(audio, size);
onLoaded(audio, listener, audioAnalyser); // 传递音频对象和分析器给回调
}
);
创建 3D 模型并应用音频数据
通过 THREE.BoxGeometry() 创建了立方体模型,并使用 THREE.MeshPhongMaterial() 为模型设置材质。接着,我们根据音频的频率数据来调整模型的大小,使其随着音频的节奏产生动态变化。通过 audioAnalyser.getFrequencyData() 获取音频的频率数据,并利用数据调整每个立方体的缩放比例。
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({
color: generateRandomColor(),
emissive: 0x444444,
shininess: 100,
});
const mesh = new THREE.Mesh(geometry, material);
// 根据音频数据调整模型的大小
if (audioAnalyserInstance) {
const data = audioAnalyserInstance.getFrequencyData();
group.children.forEach((mesh, index) => {
const value = data[index] / 255;
mesh.userData.targetScale = 0.3 + value * 0.7;
mesh.scale.y = THREE.MathUtils.lerp(
mesh.scale.y,
mesh.userData.targetScale,
0.5
);
});
}
后期处理效果
为了增强视觉效果,我们使用了 后期处理(Post-processing)技术,特别是 辉光效果。通过 THREE.EffectComposer 管理后期处理效果,并使用 THREE.UnrealBloomPass 实现辉光效果。通过设置辉光的强度、半径和阈值,增强场景中的亮度和光感效果。
const bloomComposer = new EffectComposer(renderer);
bloomComposer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
bloomPass.strength = 1;
bloomPass.radius = 0.75;
bloomPass.threshold = 0;
bloomComposer.addPass(bloomPass);
const outputPass = new OutputPass();
bloomComposer.addPass(outputPass);
射线拾取和互动
为了增加互动性,我们实现了 射线拾取 功能,允许用户点击 3D 模型来播放或暂停音频。通过 THREE.Raycaster 和 THREE.Vector2,我们计算从屏幕点击位置发射的射线与物体的交点,当用户点击模型时,音频会根据当前状态进行播放或暂停。
const rayCaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
const handleClick = (event) => {
pointer.x = ((event.clientX - bounding.left) / bounding.width) * 2 - 1;
pointer.y = -((event.clientY - bounding.top) / bounding.height) * 2 + 1;
rayCaster.setFromCamera(pointer, camera);
const intersects = rayCaster.intersectObjects(objects);
if (intersects.length > 0) {
callback && callback();
}
};
动画循环与性能优化
在 animate 函数中,我们使用 requestAnimationFrame 创建了一个动画循环,确保渲染效果流畅并持续更新。每次渲染时,我们会更新相机控制器并渲染场景,同时保证音频可视化效果实时展示。
function animate() {
raf = requestAnimationFrame(animate);
controls.update();
composer.render();
}
总结
通过灵活运用 THREE.AudioListener、THREE.Audio、THREE.AudioLoader 和 THREE.AudioAnalyser,我们可以非常便捷地处理音频数据,并将其与 3D 场景进行互动。这为实现音频驱动的 3D 可视化、动态效果以及交互体验提供了强有力的支持。
代码
github
https://github.com/calmound/threejs-demo/tree/main/sound (opens in a new tab)
gitee
https://gitee.com/calmound/threejs-demo/tree/main/sound (opens in a new tab)