Threejs实现音乐节奏表演

简介: 这篇文章详细介绍了如何使用Three.js结合音频频谱分析来创建一个随着音乐节奏变化的3D视觉表演,提供了实现音乐可视化效果的具体步骤和代码示例。

昨天看到一个网站是2维的柱形图随着音乐节奏起伏,我突然想到二维形状可以起伏,三维应该也可以,最终原理应该都是将音乐频谱分解为数据,通过数据的切换不断地改变图形的形状来实现,因为找了分析音乐频谱的源码,解析完,再通过threejs的动画实现了3D场景下的模型随着音乐的节奏而变化,下面做详细步骤的说明:

这只是做个简单的demo,主要是有了音乐的数据,后面的效果可以任意发挥,首先需要新建一个场景,还是之前的老样子,创建场景,相机,灯光,渲染器等,代码如下:

initScene(){
      scene = new THREE.Scene();
    },
    initCamera(){
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      this.camera.position.set(200,400,200);
    },
    initLight(){
      //添加两个平行光
      const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight1.position.set(300,300,600)
      scene.add(directionalLight1);
      const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight2.position.set(600,200,600)
      scene.add(directionalLight2);
    },
initFloor(){
      let floorGeometry = new THREE.BoxGeometry( 200,1,200);
      let floorMaterial = new THREE.MeshBasicMaterial({color:'#03133E'});
      let floor = new THREE.Mesh( floorGeometry, floorMaterial );
      floor.position.set(0,2,0)
      scene.add(floor)
    },
initRenderer(){
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.container = document.getElementById("container")
      this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
      this.renderer.setClearColor('#AAAAAA', 1.0);
      this.container.appendChild(this.renderer.domElement);
    },
    initControl(){
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.maxPolarAngle = Math.PI / 2.2;      // // 最大角度
    },
initAnimate() {
      requestAnimationFrame(this.initAnimate);
      this.renderer.render(scene, this.camera);
}

然后在地板上放小方块,如果直接循环放置会放在同一个位置重叠,因为这里要在循环里控制每个小方块之间的距离,大概意思就是每个小方块长宽高为10,每个小方块间隔1,且每铺满11个小方块为一行后就再换一行重头平铺,每个方块的高度设置为0,水平位置为循环中计算出来的位置,最终全部平铺开来,每个都加入到场景中等会用音乐的节奏来控制小方块的跳动,但是经过测试太多的小方块会导致卡顿,(因为模型和修改模型位置和材质都会消耗性能,且因为方块独立跳动没办法进行合并),这里选择用121个小方块,做成11*11的方阵。

initBox(){
      let x = -60; let z = -60;
      for (let i = 0; i < 121; i++) {
        if(i%11 === 0){
          x = x + 11
          z = -60
        }
        z = z + 11
        const geometry = new THREE.BoxGeometry(10, 10, 10);
        const material = new THREE.MeshPhongMaterial({
          color: 0x00ff00
        });
        let boxMesh = new THREE.Mesh(geometry, material);
        this.boxMeshs.push(boxMesh)
        this.boxMeshs[i].position.set(x, 0, z);
        scene.add(this.boxMeshs[i]);
      }
    },

然后就需要去加载音乐了,音乐是设置在html中的audio标签,通过js获取标签元素来获取到连接的音乐资源,通过加载到的音乐分析,并获取他的音乐节奏,这部分我直接贴出代码,就不做详细解释了,如果有问题可以私信我。

initRound(){
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      let audioCtx = new AudioContext();
      if(!this.source){
        this.source = audioCtx.createMediaElementSource(this.myAudio);//通过audio标签获取播放源
        this.analyser = audioCtx.createAnalyser(); // 创建AnalyserNode 用于分析音频频谱的节点
        this.source.connect(this.analyser);
        this.analyser.connect(audioCtx.destination);
        let bufferLength = this.analyser.frequencyBinCount;
        this.dataArray = new Uint8Array(bufferLength);
      }
    },

此时的场景如下:

Threejs实现音乐节奏表演_Threejs

下面就是关键的步骤,通过不断更新的音乐节奏来更新小方块的位置,这里是在渲染动画里去实现,在循环中不断通过this.analyser.getByteFrequencyData(this.dataArray);获取到新的音乐数据,然后根据dataArray的值设置方块的位置,这里是直接设置方块的所在高度,为了更好看的效果还加了颜色设置,通过不同高度切换不同的RGB颜色,并赋值给对应的小方块材质,

initAnimate() {
      requestAnimationFrame(this.initAnimate);
      this.renderer.render(scene, this.camera);
      if(this.analyser){
        this.analyser.getByteFrequencyData(this.dataArray);
        for (let i = 0; i < this.dataArray.length-1; i++) {
          if(this.boxMeshs[i]){
            let x = this.boxMeshs[i].position.x
            let z = this.boxMeshs[i].position.z
            this.boxMeshs[i].position.set(x,this.dataArray[i],z)
            this.boxMeshs[i].geometry.parameters.width = this.dataArray[i]
            this.boxMeshs[i].material.color = new THREE.Color(0.8,this.dataArray[i],this.dataArray[i]);
            let r =0, g = 0, b  = 0;
            if(this.dataArray[i]<85){
              r = g = b = this.dataArray[i] * 3
            }else if(this.dataArray[i]<170){
              r = g = (this.dataArray[i] - 85) * 2
              b = 255
            }else{
              r = (this.dataArray[i] - 170) * 3
              g = b = 255
            }
            this.boxMeshs[i].material.color = new THREE.Color('rgb('+r+', '+g+', '+b+')')
          }
        }
      }
    },

基本的代码就这些了,下面附上完整代码

<template>
  <div>
    <div style="position: absolute;top:10px;left:40%;z-index: 100">
      <audio id='myAudio' controls>
        <source src="/static/video/music.mp3" type="audio/mp3">
      </audio>
    </div>
    <div id="container"></div>
  </div>
</template>

<script>
import * as THREE from 'three'
import {OrbitControls} from "three/addons/controls/OrbitControls";

let scene;
export default {
  name: "agv-single",
  data() {
    return{
      camera:null,
      cameraCurve:null,
      renderer:null,
      container:null,
      controls:null,

      myAudio:null,
      analyser:null,
      dataArray:[],
      boxMeshs:[],
      source:null,
    }
  },
  methods:{
    initScene(){
      scene = new THREE.Scene();
    },
    initCamera(){
      this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
      this.camera.position.set(200,400,200);
    },
    initLight(){
      //添加两个平行光
      const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight1.position.set(300,300,600)
      scene.add(directionalLight1);
      const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1.5);
      directionalLight2.position.set(600,200,600)
      scene.add(directionalLight2);
    },
    initFloor(){
      let floorGeometry = new THREE.BoxGeometry( 200,1,200);
      let floorMaterial = new THREE.MeshBasicMaterial({color:'#03133E'});
      let floor = new THREE.Mesh( floorGeometry, floorMaterial );
      floor.position.set(0,2,0)
      scene.add(floor)
    },
    initRound(){
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      let audioCtx = new AudioContext();
      if(!this.source){
        this.source = audioCtx.createMediaElementSource(this.myAudio);//通过audio标签获取播放源
        this.analyser = audioCtx.createAnalyser(); // 创建AnalyserNode 用于分析音频频谱的节点
        this.source.connect(this.analyser);
        this.analyser.connect(audioCtx.destination);
        let bufferLength = this.analyser.frequencyBinCount;
        this.dataArray = new Uint8Array(bufferLength);
      }
    },
    initBox(){
      let x = -60; let z = -60;
      for (let i = 0; i < 121; i++) {
        if(i%11 === 0){
          x = x + 11
          z = -60
        }
        z = z + 11
        const geometry = new THREE.BoxGeometry(10, 10, 10);
        const material = new THREE.MeshPhongMaterial({
          color: 0x00ff00
        });
        let boxMesh = new THREE.Mesh(geometry, material);
        this.boxMeshs.push(boxMesh)
        this.boxMeshs[i].position.set(x, 0, z);
        scene.add(this.boxMeshs[i]);
      }
    },
    initRenderer(){
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.container = document.getElementById("container")
      this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
      this.renderer.setClearColor('#AAAAAA', 1.0);
      this.container.appendChild(this.renderer.domElement);
    },
    initControl(){
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.maxPolarAngle = Math.PI / 2.2;      // // 最大角度
    },
    initAnimate() {
      requestAnimationFrame(this.initAnimate);
      this.renderer.render(scene, this.camera);
      if(this.analyser){
        this.analyser.getByteFrequencyData(this.dataArray);
        for (let i = 0; i < this.dataArray.length-1; i++) {
          if(this.boxMeshs[i]){
            let x = this.boxMeshs[i].position.x
            let z = this.boxMeshs[i].position.z
            this.boxMeshs[i].position.set(x,this.dataArray[i],z)
            this.boxMeshs[i].geometry.parameters.width = this.dataArray[i]
            this.boxMeshs[i].material.color = new THREE.Color(0.8,this.dataArray[i],this.dataArray[i]);
            let r =0, g = 0, b  = 0;
            if(this.dataArray[i]<85){
              r = g = b = this.dataArray[i] * 3
            }else if(this.dataArray[i]<170){
              r = g = (this.dataArray[i] - 85) * 2
              b = 255
            }else{
              r = (this.dataArray[i] - 170) * 3
              g = b = 255
            }
            this.boxMeshs[i].material.color = new THREE.Color('rgb('+r+', '+g+', '+b+')')
          }
        }
      }
    },
    initPage(){
      this.initScene();
      this.initCamera();
      this.initLight();
      this.initFloor();
      this.initRenderer();
      this.initControl();
      this.initBox();
      this.initAnimate();
    }
  },
  mounted() {
    this.myAudio = document.querySelector('#myAudio');
    this.myAudio.addEventListener('play', () => {
      this.initRound();
    })
    this.initPage()
  }
}
</script>

<style scoped>
#container{
  position: absolute;
  width:100%;
  height:100%;
  overflow: hidden;
}

</style>

效果图如下

Threejs实现音乐节奏表演_数据_02

相关文章
|
4月前
|
前端开发
HTML+CSS动画实现动感3D卡片墙:现代Web设计的视觉盛宴
HTML+CSS动画实现动感3D卡片墙:现代Web设计的视觉盛宴
|
4月前
|
前端开发
CSS动画新境界:打造逼真的水罐摇晃动效!
CSS动画新境界:打造逼真的水罐摇晃动效!
|
6月前
|
图形学 开发者
【unity小技巧】两种办法解决FPS游戏枪或者人物穿墙穿模问题
【unity小技巧】两种办法解决FPS游戏枪或者人物穿墙穿模问题
271 0
|
Java API Android开发
Compose 动画艺术探索之灵动岛
Compose 动画艺术探索之灵动岛
258 0
Compose 动画艺术探索之灵动岛
|
前端开发 JavaScript 程序员
「《奇迹再现》专属音乐播放器🎵」致以跃动的心与阳光
「《奇迹再现》专属音乐播放器🎵」致以跃动的心与阳光
244 0
|
算法 图形学
还在被电影中吧爆炸的画面震撼?那你一定不要错过这款Unity的爆炸插件
Exploder是一个Unity3D插件,可以爆炸任何有网格的游戏对象。在游戏对象上tag设置为“Exploder”,就可以看到爆炸了!
|
Linux
4步教你打造好莱坞科幻特效
4步教你打造好莱坞科幻特效
173 0
4步教你打造好莱坞科幻特效
|
机器学习/深度学习 传感器 人工智能
堪比科幻大片!优酷特效广告、互动视频技术大揭秘
大家都看过科幻电影吧,像《头号玩家》、《美国队长》、《银河护卫队》,这些科幻电影中都出现过AR/VR的镜头。以《头号玩家》为例,主角来到博物馆,能够实时的、多角度地去浏览资料,这里就用到了volumetric video技术,它是一种VR技术,就是在被摄物周围放一圈摄像头,采集的视频经过合成加工就可以无缝地切换观看了。与电影中的拍摄特技不同,随着5G和AI的加速落地,在视频生产和播放环节,越来越多的融入AR、VR的相关技术。可以说今天的科幻电影是明天的科学事实。
1607 0
堪比科幻大片!优酷特效广告、互动视频技术大揭秘
|
机器学习/深度学习 人工智能 算法
算法音乐往事:二次元女神“初音未来”诞生记
音乐的诞生甚至早于语言,人类对于音乐的探索却从未停止。从最初的“音乐骰子”到如今火遍二次元的宅男女神“初音未来”,算法与音乐之间的故事,才刚刚开始。
2291 0
下一篇
DataWorks