three实现三维音频声音可视化频谱动画效果代码

代码语言:html

所属分类:多媒体

代码描述:three实现三维音频声音可视化频谱动画效果代码

代码标签: three 三维 音频 声音 可视化 频谱 动画

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  

  
  
  
<style>
@import url('https://fonts.googleapis.com/css?family=Architects+Daughter|Orbitron');

* {
    margin: 0;
    padding: 0;
}

html, body {
    overflow: hidden;
    font-family:'Orbitron',sans-serif;
    font-size:14px; 
}

/* dat.gui align to left */
.lil-gui.autoPlace{ 
    right:auto !important;
    left:15px !important;
 }



.Experience {
    position:relative;
    width:100vw;
    height:100vh;
}

.Experience > .Experience_Canvas {
    top:0;
    left:0;
    position:absolute;
    outline: none;
    filter:grayscale(0%);
    transition:filter 1s;
}

.Experience[loading = "true"] > .Experience_Canvas {
    filter:grayscale(90%);
}


.Experience_Panel {
    position:relative;
    border:1px solid #000022;
    background-color: rgba(30, 30, 90, 0.75);
    color:#FFFFFF;
    border-radius: 6px;
    box-shadow:  5px  5px 5px rgba(0,0,0, 0.5);
}

.Experience > .Experience_Loading {
    padding:0.6em;
/*    width:4em;*/
    margin:auto;
    top: calc(50vh - 10px);
    height: 18px;
    display:none;
    font-family:'Orbitron',sans-serif;
    font-size:max(1.5vw, 1.5vh); 
}

.Experience[loading = "true"] > .Experience_Loading {
    display:table;
}

/* Group of controls (fps, button logo, button full screen) */
.Experience_Controls {
    position:fixed;
    bottom :0;
    right:0;
    z-index:10000;
}

.Experience_Static, .Experience_Control {
    user-select: none;
    position:relative;
    width:48px; /*max(3.2vw, 3.2vh);*/
    height:48px; /*max(3.2vw, 3.2vh);*/
    margin-bottom:max(0.5vw, 0.5vh);
    margin-right:max(0.5vw, 0.5vh);
    padding:6px;
/*    padding:max(0.4vw, 0.4vh);*/
    text-align: center;
    border:2px solid transparent;
    display: block;
    margin-left: auto;
    transition:border 0.4s;
}

.Experience_FPS {
    font-size:24px;
}

.Experience_TxtFPS {
    font-size:14px;
}


.Experience_Control:hover {
    border:2px solid rgb(234, 80, 78);
    cursor:pointer;
}

.Experience_Control > img {
    transition:transform 0.4s;
    width:48px;
    height:48px;
    position:relative;    
}

.Experience_Control:hover > img {
    transform: scale(1.5, 1.5);
}

#restoreScreen {
    display:none;
}

#Logo {
    display: block;
    transition: width 0.33s ease-in;
}

/* Logo svg need to be fixed on the right, because of the letter logo animation */
#Logo img {
    position:absolute;
    right: 6px;
}

#Logo > * {
    pointer-events: none;
}

#Logo:hover {
    width:280px;
}

#Logo > #LogoText {
    position:absolute;
    bottom:14px;
    margin-top:3px;
    transition:0.4s;
    width:230px; /* 12 caracters * 400px */                 
    font-size:24px;
}

#Logo > #LogoText > span {
    transition:0.33s cubic-bezier(.17,-0.42,0,1.15);
    opacity:0;
    position: relative;
    display: inline-block;
    color:#FFF;
}            

#Logo:hover > #LogoText > span {                
    position:relative;
    opacity:1;
    left:230px;
    animation: LogoAnimation 0.33s 0.33s cubic-bezier(.17,-0.42,0,1.15);
    animation-fill-mode:forwards;
}


/* Color rojo del 33 */
#Logo > #LogoText > span:nth-child(11), #Logo > #LogoText > span:nth-child(12) {
    color: rgb(234, 80, 78);
}            


#Logo:hover > #LogoText > span:nth-child(1)  {    animation-delay: 0.55s;     }
#Logo:hover > #LogoText > span:nth-child(2)  {    animation-delay: 0.50s;     }
#Logo:hover > #LogoText > span:nth-child(3)  {    animation-delay: 0.45s;     }
#Logo:hover > #LogoText > span:nth-child(4)  {    animation-delay: 0.40s;     }
#Logo:hover > #LogoText > span:nth-child(5)  {    animation-delay: 0.35s;     }
#Logo:hover > #LogoText > span:nth-child(6)  {    animation-delay: 0.30s;     }
#Logo:hover > #LogoText > span:nth-child(7)  {    animation-delay: 0.25s;     }
#Logo:hover > #LogoText > span:nth-child(8)  {    animation-delay: 0.20s;     }
#Logo:hover > #LogoText > span:nth-child(9)  {    animation-delay: 0.15s;     }
#Logo:hover > #LogoText > span:nth-child(10) {    animation-delay: 0.10s;     }
#Logo:hover > #LogoText > span:nth-child(11) {    animation-delay: 0.05s;     }
#Logo:hover > #LogoText > span:nth-child(12) {    animation-delay: 0.0s;      }

@keyframes LogoAnimation {
    0%   { left:230px;  }
    100% { left:0px;  }
}  

/* 
 * Audio controls
 */
.Experience_AudioControls {
    user-select: none;
    position:fixed;
    display:flex;
    flex-flow: row;
    flex-direction:column;
/*    flex-wrap: wrap;*/
    bottom:1vh;
/*    height:80px;*/
    width:75vw;
    left:12.5vw;
/*    padding:0 0 min(1vw, 1vh) 0 0;*/
    pointer-events: none;
}

.Experience_AudioControls label, .Experience_AudioControls button {
    font-size:14px !important;
    font-family:'Orbitron',sans-serif;
}

.Experience_AC_Play {
/*    margin:max(0.5vw, 0.5vh);*/
    flex-basis:32px;
}

    

.Experience_AC_Play > button {
    width:62px;
    height:62px;
    border-radius: 100px;
    background-color: rgba(30, 30, 150, 1.0);
    cursor:pointer;
    color:#fff;
    transition:background-color 0.5s;
    pointer-events:all;
}

.Experience_AC_Play > button:hover {
    background-color: rgb(49, 49, 255);
}

.Experience_AC_Songs {
    flex: 3 0 10%; 
/*    margin:max(0.5vw, 0.5vh);*/
    position:relative;
/*    top:6px;*/
    
/*    flex-basis:176px;*/
flex-basis:32px;
}

.Experience_AC_Songs > select {
    position:relative;
    left:5px;
    height: 22px;
    pointer-events:all;
}

.Experience_AC_Volume {
    flex-basis:32px;
/*    margin:max(0.5vw, 0.5vh);*/
    
/*    flex-basis:150px;*/
}

.Experience_AC_Volume > input {
    position:relative;
    left:5px;
    top:5px;
    pointer-events:all;
}

.Experience_AC_Time {
    flex-basis:32px;
/*    margin:max(0.5vw, 0.5vh);*/
}

.Experience_AC_Time > input {
    position:relative;
    left:5px;
    top:5px;
    width: calc(100% - 10px);
    pointer-events:all;
}

.Experience td {
    color:#ffffff;
    text-shadow: 1px 1px #000000;
}




/* Pay & Pause buttons */
.Experience_Play, .Experience_Pause {
    position: fixed;
    margin-bottom: max(0.5vw, 0.5vh);
    margin-left: max(0.5vw, 0.5vh);
    bottom:0;
}

/* when play is true, play button is enabled, and pause button is disabled, when is false is the oposite */
.Experience_Play[play="false"], .Experience_Pause[play="true"] {
    display:none;
}
 
.Experience_SongInfo {
    position:fixed;
    margin-bottom: max(0.5vw, 0.5vh);
    margin-left: max(0.5vw, 0.5vh);    
    bottom:70px;
    padding:12px;
    left:-500px;
    transition:left 0.4s cubic-bezier(.17,-0.42,0,1.15);
}

.Experience_SongInfo[visible="false"] {
    left:0px;
    animation: SongInfoAnimation 0.4s 5s cubic-bezier(.17,-0.42,0,1.15);
    animation-fill-mode:forwards;
}

.Experience_SongInfo td {
    padding: 4px;
    color:#CCCCCC;
}

.Experience_SongInfo a, .Experience_SongInfo a:visited {
    color:#FFFFFF;
}

@keyframes SongInfoAnimation {
    0%   { opacity:1; left:0px;     }
    100% { opacity:0; left:-300px;  }
}
</style>

  
  
</head>

<body >
  <!-- import THREE.js cdn -->
<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/es-module-shims.1.6.2.js"></script>
<script type="importmap">
  {
  "imports": {      
      "three": "//repo.bfw.wiki/bfwrepo/js/module/three/build/151/three.module.js",
      "three/addons/": "//repo.bfw.wiki/bfwrepo/js/module/three/examples/151/jsm/"
    }
  }
</script>


<!-- Bars vertex shader-->
<script id="BarsVertexShader" type="x-shader/x-vertex">
uniform sampler2D uAudioTexture;  // Linear texture (1024 * 1) to get audio values
uniform float     uAudioStrength; // Strength multiplyer 
attribute float   aId;            // Its an unique ID for each bar (1.0 / totalBars * actualBar)
varying vec2      vUv;

void main() {
    
    vec4 modelPosition      = modelMatrix       * vec4(position , 1.0);
    // If the point is from superior part
    vec4 data = texture2D(uAudioTexture, vec2(aId, 0.0));
    if (uv.y > 0.1) {
        // Add the red channel intensity to the Y of the model
        modelPosition.y +=  data.r * uAudioStrength;
    }
    else {
        modelPosition.y -=  data.r * uAudioStrength;
    }
 
    vec4 viewPosition       = viewMatrix        * modelPosition;
    vec4 projectionPosition = projectionMatrix  * viewPosition;

    gl_Position = projectionPosition;

    vUv = uv;
}
</script>

<!-- Bars fragment shader-->
<script id="BarsFragmentShader" type="x-shader/x-fragment">
varying vec2 vUv;

void main() {
    float dist = length(vUv - vec2(0.5));


    // Fill the bar with red
    gl_FragColor = vec4(0.75 + dist, 1.0 - dist, 0.0, 1.0);
}
</script>


<!-- Bars Depth vertex shader-->
<script id="BarsDepthVertexShader" type="x-shader/x-vertex">
uniform sampler2D uAudioTexture;  // Linear texture (1024 * 1) to get audio values
uniform float     uAudioStrength; // Strength multiplyer 
attribute float   aId;            // Its an unique ID for each bar (1.0 / totalBars * actualBar)


#include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
varying vec2 vHighPrecisionZW;
void main() {
	#include <uv_vertex>
	#include <skinbase_vertex>
	#ifdef USE_DISPLACEMENTMAP
		#include <beginnormal_vertex>
		#include <morphnormal_vertex>
		#include <skinnormal_vertex>
	#endif
	#include <begin_vertex>

//vec4 audioValue = vec4(1.0);
    vec4 audioValue = texture2D(uAudioTexture, vec2(aId, 0.0));

    if (uv.y > 0.5) {
        // Add the red channel intensity to the Y of the model
        transformed.y +=  audioValue.r * uAudioStrength;
    }
    else {
        transformed.y -=  audioValue.r * uAudioStrength;
    }
    

	#include <morphtarget_vertex>
	#include <skinning_vertex>
	#include <displacementmap_vertex>
	#include <project_vertex>
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>
	vHighPrecisionZW = gl_Position.zw;
}
</script>



<!-- Floor Standard vertex shader-->
<script id="FloorStandardVertexShader" type="x-shader/x-vertex">
uniform sampler2D uAudioTexture;
//uniform float uTime;
uniform float uAudioStrength;
varying vec2  vUv;



/* 
 * Three.js globals
 */
#define STANDARD
varying vec3 vViewPosition;
#ifdef USE_TRANSMISSION
	varying vec3 vWorldPosition;
#endif
#include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <normal_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

/* 
 * Three.js main
 */
void main() {
    #include <uv_vertex>
    #include <color_vertex>
    #include <morphcolor_vertex>
    #include <beginnormal_vertex>
    #include <morphnormal_vertex>
    #include <skinbase_vertex>
    #include <skinnormal_vertex>
    #include <defaultnormal_vertex>
    #include <normal_vertex>
    #include <begin_vertex>

    // Audio value on Y axis
    vec4 textureColor = texture2D(uAudioTexture, uv);
    transformed.z += textureColor.r * uAudioStrength;

    vUv = uv;

    /*
     * Three.js main end
     */


    #include <morphtarget_vertex>
    #include <skinning_vertex>
    #include <displacementmap_vertex>
    #include <project_vertex>
    #include <logdepthbuf_vertex>
    #include <clipping_planes_vertex>
    vViewPosition = - mvPosition.xyz;
    #include <worldpos_vertex>
    #include <shadowmap_vertex>
    #include <fog_vertex>
  #ifdef USE_TRANSMISSION
    vWorldPosition = worldPosition.xyz;
  #endif
}
</script>






<!-- Floor Depth vertex shader-->
<script id="FloorDepthVertexShader" type="x-shader/x-vertex">
uniform sampler2D uAudioTexture;
uniform float     uAudioStrength;


#include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
varying vec2 vHighPrecisionZW;
void main() {
	#include <uv_vertex>
	#include <skinbase_vertex>
	#ifdef USE_DISPLACEMENTMAP
		#include <beginnormal_vertex>
		#include <morphnormal_vertex>
		#include <skinnormal_vertex>
	#endif
	#include <begin_vertex>

//vec4 audioValue = vec4(1.0);
    vec4 audioValue = texture2D(uAudioTexture, uv);

    if (uv.y > 0.5) {
        // Add the red channel intensity to the Y of the model
        transformed.y +=  audioValue.r * uAudioStrength;
    }
    else {
        transformed.y -=  audioValue.r * uAudioStrength;
    }
    

	#include <morphtarget_vertex>
	#include <skinning_vertex>
	#include <displacementmap_vertex>
	#include <project_vertex>
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>
	vHighPrecisionZW = gl_Position.zw;
}
</script>

  
      <script  type="module">
/* Experience created by Josep Antoni Bover for https://devildrey33.es.
 *  This Audio shader is a part of my Audio-PlayGround experience :
 *     https://github.com/devildrey33/Audio-PlayGround
 * 
 *  #7 Floor and bars effect.
 *      - Play with Audio Strength, Line size, and radius to achieve different effects
 *      - This script creates an audio data texture with the getByteTimeFrequency values 
 *        on the red channel and the getByteTimeByDomain on the green channel. (Its more easy than creating extra attributes, and looks better)
 *      - Then this texture is sent to the sunset shader and the sunset depth shader.
 *      - You can drag & drop any song of your computer to play it.
 *      - Song name   : Alone
 *        Song artist : Color Out
 *        URL         : https://www.jamendo.com/track/1886257/alone
 * 
 *  Created on        : 16/05/2023
 *  Last modification : 16/05/2023
 */


import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'
import * as THREE from 'three'


// Simple 2d buffer canvas
class BufferCanvas {
    constructor(width, height) {
        this.canvas  = document.createElement("canvas");
        this.canvas.setAttribute("width", width);
        this.canvas.setAttribute("height", height);
        this.context = this.canvas.getContext("2d", { willReadFrequently : true }); 
        this.width   = width;
        this.height  = height;
    }
}

export default class CodepenThreeAudio {
    // Default options
    options = {
        // Y position. Use 'auto' to center canvas horizontaly to the view port
        top                     : 0,
        // X position. Use 'auto' to center canvas verticaly to the view port
        left                    : 0,
        // Width in pixels. Use 'auto' to fit all viewport width
        width                   : "auto",           
        // Height in pixels. Use 'auto' to fit all viewport height
        height                  : "auto",           
        // Show framerate inside the butons frame
        showFPS                 : true,            
        // Show full screen buton in the buttons frame
        buttonFullScreen        : true,            
        // Show my logo buton in the buttons frame (that redirects to devildrey33.es)
        buttonLogo              : true,            
        // Show a github icon to go to the example repository
        buttonGitHub            : true,
        // GitHub url for this project (only used if buttonGitHub is true)
        urlGitHub               : "https://github.com/devildrey33/Audio-PlayGround",
        // Element where canvas is inserted (by default is document.body)
        // For example you can use document.getElementById() to retrieve tag inside you want to create the canvas
        rootElement             : document.body,
        // Anti alias (by default true)
        antialias               : true,
        // OrbitControls enabled by default
        orbitControls           : true,
        // Song path
        songPath                : "//repo.bfw.wiki/bfwrepo/sound/5e148aa3821f2.mp3"
    };    
    

    constructor(options) {
        // Merge options
        this.options = { ...this.options, ...options };
        // Create basic html elements
        this.createHtml();
        // Setup sizes
        this.setupSizes();

        // Setup audio fft size
        this.fftSize         = 2048;
        this.square          = Math.sqrt(this.fftSize * 0.5);

        // Setup audio texture
        this.bufferCanvasLinear         = new BufferCanvas(1024, 1);
        this.bufferCanvasLinear.texture = new THREE.CanvasTexture(this.bufferCanvasLinear.canvas);
        this.imageDataLinear            = this.bufferCanvasLinear.context.createImageData(1024, 1);
        this.bufferCanvasLinear.texture.generateMipMaps = false;
        this.bufferCanvasLinear.texture.minFilter = THREE.NearestFilter;
        this.bufferCanvasLinear.texture.magFilter = THREE.NearestFilter;           
        this.bufferCanvasSquare         = new BufferCanvas(this.square, this.square);
        this.bufferCanvasSquare.texture = new THREE.CanvasTexture(this.bufferCanvasSquare.canvas);
        this.bufferCanvasSquare.texture.minFilter = THREE.NearestFilter;
        this.bufferCanvasSquare.texture.magFilter = THREE.NearestFilter;           
        this.imageDataSquare            = this.bufferCanvasSquare.context.createImageData(this.square, this.square);

        // Setup buffers for audio data
        this.analizerData    = new Uint8Array(this.fftSize * 0.5);
        this.analizerDataSin = new Uint8Array(this.fftSize * 0.5);
        // Fill the sin array with 128, because its the central point
        for (let i = 0; i < this.fftSize * 0.5; i++) {
            this.analizerDataSin[i] = 128;
        }

        // Setup a basic scene with camera
        this.setupBasicScene();
        
        // Time values
        this.timeStart        = Date.now();
        this.timeCurrent      = this.timeStart;
        this.timeElapsed      = 0;
        this.timeDelta        = 16;
        // Time from this second 
        this.timeActualFrame  = this.timeStart + 1000;
        // Number of frames during this second
        this.timeFrameCounter = 0;
        // actual framerate
        this.fps              = 0;        
        
        // Drag & drop events
        this.hEventDragEnter = this.eventDragEnter.bind(this);
        this.hEventDragOver  = this.eventDragOver.bind(this);
        this.hEventDrop      = this.eventDrop.bind(this);
        this.elementCanvas.addEventListener("dragenter", this.hEventDragEnter);
        this.elementCanvas.addEventListener("dragover" , this.hEventDragOver);
        this.elementCanvas.addEventListener("drop"     , this.hEventDrop);
        
        // song loaded
        this.songLoaded     = false;
        // default song true (not a drag & drop song)
        this.defaultSong    = true;

        // Setup a Three.js renderer
        this.setupRenderer();

        // Create the update loop
        window.requestAnimationFrame(() => { this.basicUpdate(); });
    }

    /* 
     * Create basic html elements (canvas, loading, framerate, fullscreen, github, and my web page button)
     */
    createHtml() {
        // Create an empty div element outside of the doom, to make it our root element
        const preElementExperience = document.createElement("div");
        // Id of this canvas (i need it for compatibility with my web page)
        this.id = 0;
        preElementExperience.id        = "Experience" + this.id; 
        preElementExperience.className = "Experience";
        // Add the Experience element to the options root element
        this.options.rootElement.appendChild(preElementExperience);
        this.elementExperience = document.getElementById(preElementExperience.id);
        // Setup the loading atribute
        this.elementExperience.setAttribute("loading", true);
        // String that has all the tags to be added
        let strHTML = ""
        // Add the canvas element
        strHTML += '<canvas id="Experience' + this.id + '_Canvas" class="Experience_Canvas"></canvas>';
        // Add the loading element
        strHTML += '<div class="Experience_Loading Experience_Panel"><span>Loading...</span></div>';
        // Add the main controls element
        strHTML += '<div class="Experience_Controls">';
        // Show FPS
        if (this.options.showFPS === true) {
            strHTML +=  "<div class='Experience_Panel Experience_Static' title='Frames Per Second'>" +
                            "<div class='Experience_FPS'>60</div>" +
                            "<div class='Experience_TxtFPS'>fps</div>" +
                        "</div>";
        }
        // Show full screen button
        if (this.options.buttonFullScreen === true) {
            strHTML +=  "<div id='fullScreen' class='Experience_Panel Experience_Control' title='Full screen mode'>" +
                            "<img draggable='false' src='https://devildrey33.es/Ejemplos/Three.js-Journey/Audio-PlayGround/icos.svg#svg-pantalla-completa' />" +
                        "</div>" +
                        "<div id='restoreScreen' class='Experience_Panel Experience_Control' title='Restore screen'>" +
                            "<img draggable='false' src='https://devildrey33.es/Ejemplos/Three.js-Journey/Audio-PlayGround/icos.svg#svg-restaurar-pantalla' />" +
                        "</div>";                
        }

       
        // Closing .Experience_Controls
        strHTML += '</div>';        

        // Play button
        strHTML += '<div class=&quo.........完整代码请登录后点击上方下载按钮下载查看

网友评论0