Audio Viz+Acid

Audio Viz+Acid

更多有趣示例 尽在小红砖社区

示例

在这里插入图片描述

HTML

 <input type="file" id="thefile" accept="audio/*" />

    <audio id="audio" controls></audio>

<script id="vertex" type="x-shader/x-fragment">
  #define PI 3.14159265358979323846
  uniform float u_time;
			uniform sampler2D uAudio;
  uniform float uMove;
  uniform float uAverage;
  varying vec2 vUv;
  varying float height;
  void main(){
    float a = texture2D(uAudio, vec2(0.5,0. )).r;
    float doubleX = position.x * position.x ;
    float distanceSquared = doubleX + position.z * position.z;
    vec3 pos = position;
    // 0 instead of pos.y to get noise on 2d space
    float n = snoise(vec3(pos.x,0.,pos.z- u_time*(2.  ) - uMove )/20.);
    
    
    // pos.y +=1.*sin(distanceSquared*sin(u_time/143.0)/1000.);
    
   
    height = n *( sin(doubleX/1000.) );
    pos.y += height*(10. + 15. * uAverage / 255. );
    
    // pos.z += pos.z * ((sin(u_time/256.0) + 1.75) / 2.); 
    
    float diagonalSin = sin(u_time/200.0);
    float frequency = 5000.;
    float om = (sin(distanceSquared*sin(u_time/10.0)/frequency) * diagonalSin) /1.5;
    
    
	   pos.y = pos.x*sin(om)+pos.y*cos(om);
	    pos.x = pos.x*cos(om)-pos.y*sin(om);
    
      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos,1.);
    vUv = uv;
  }
</script>

<script id="fragment" type="x-shader/x-fragment">
  #define PI 3.14159265358979323846
  uniform float u_time;
  varying vec2 vUv;
  varying float height;
  uniform float uAverage;
			uniform sampler2D uAudio;
    uniform vec3 fogColor;
    uniform float fogNear;
    uniform float fogFar;
  vec3 palette( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d )
{
    return a + b*cos( 6.28318*(c*t+d) );
}
  void main(){
    float a = texture2D(uAudio, vec2(height/2.+0.5,0. )).r;
    float b = texture2D(uAudio, vec2(vUv.y, 0. )).r;
    float roadSize = 0.03;
    float road = smoothstep(0.-roadSize,0.,vUv.x -0.5) - smoothstep(0.,0.+roadSize,vUv.x -0.5 );
    gl_FragColor = vec4(palette(
      height/2.+0.5,
      vec3(0.5, 0.5, 0.5),
      vec3(0.5, 0.5*a, 0.5*b),
      vec3(2.0, 1.0*a, 0.0),
      vec3(0.50*b, 0.20, 0.25)	)+road,1.);
    
    
    // gl_FragColor = vec4(vec3(noise),1.);
      #ifdef USE_FOG
          #ifdef USE_LOGDEPTHBUF_EXT
              float depth = gl_FragDepthEXT / gl_FragCoord.w;
          #else
              float depth = gl_FragCoord.z / gl_FragCoord.w;
          #endif
          float fogFactor = smoothstep( fogNear, fogFar, depth );
          gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );
      #endif
  }
</script>



<script id="noise" type="x-shader/x-fragment">
  
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
//               noise functions.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
//

vec3 mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 mod289(vec4 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 permute(vec4 x) {
  return mod289(((x*34.0)+1.0)*x);
}

vec4 taylorInvSqrt(vec4 r){
  return 1.79284291400159 - 0.85373472095314 * r;
}

float snoise(vec3 v) {

  const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
  const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);

  // First corner
  vec3 i  = floor(v + dot(v, C.yyy) );
  vec3 x0 =   v - i + dot(i, C.xxx) ;

  // Other corners
  vec3 g = step(x0.yzx, x0.xyz);
  vec3 l = 1.0 - g;
  vec3 i1 = min( g.xyz, l.zxy );
  vec3 i2 = max( g.xyz, l.zxy );

  //   x0 = x0 - 0.0 + 0.0 * C.xxx;
  //   x1 = x0 - i1  + 1.0 * C.xxx;
  //   x2 = x0 - i2  + 2.0 * C.xxx;
  //   x3 = x0 - 1.0 + 3.0 * C.xxx;
  vec3 x1 = x0 - i1 + C.xxx;
  vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
  vec3 x3 = x0 - D.yyy;      // -1.0+3.0*C.x = -0.5 = -D.y

  // Permutations
  i = mod289(i);
  vec4 p = permute( permute( permute(
      i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
    + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
    + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));

  // Gradients: 7x7 points over a square, mapped onto an octahedron.
  // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
  float n_ = 0.142857142857; // 1.0/7.0
  vec3  ns = n_ * D.wyz - D.xzx;

  vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)

  vec4 x_ = floor(j * ns.z);
  vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)

  vec4 x = x_ *ns.x + ns.yyyy;
  vec4 y = y_ *ns.x + ns.yyyy;
  vec4 h = 1.0 - abs(x) - abs(y);

  vec4 b0 = vec4( x.xy, y.xy );
  vec4 b1 = vec4( x.zw, y.zw );

  //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
  //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
  vec4 s0 = floor(b0)*2.0 + 1.0;
  vec4 s1 = floor(b1)*2.0 + 1.0;
  vec4 sh = -step(h, vec4(0.0));

  vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
  vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;

  vec3 p0 = vec3(a0.xy,h.x);
  vec3 p1 = vec3(a0.zw,h.y);
  vec3 p2 = vec3(a1.xy,h.z);
  vec3 p3 = vec3(a1.zw,h.w);

  //Normalise gradients
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
  p0 *= norm.x;
  p1 *= norm.y;
  p2 *= norm.z;
  p3 *= norm.w;

  // Mix final noise value
  vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
  m = m * m;
  return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) );

}
  
// via: https://petewerner.blogspot.jp/2015/02/intro-to-curl-noise.html
vec3 curlNoise( vec3 p ){

  const float e = 0.1;

  float  n1 = snoise(vec3(p.x, p.y + e, p.z));
  float  n2 = snoise(vec3(p.x, p.y - e, p.z));
  float  n3 = snoise(vec3(p.x, p.y, p.z + e));
  float  n4 = snoise(vec3(p.x, p.y, p.z - e));
  float  n5 = snoise(vec3(p.x + e, p.y, p.z));
  float  n6 = snoise(vec3(p.x - e, p.y, p.z));

  float x = n2 - n1 - n4 + n3;
  float y = n4 - n3 - n6 + n5;
  float z = n6 - n5 - n2 + n1;


  const float divisor = 1.0 / ( 2.0 * e );
  return normalize( vec3( x , y , z ) * divisor );
}
  
</script>

CSS

body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: #DDA0DD;
}
#thefile{
  position: fixed;
  top: 15px;
  left: 100px;
}
audio {
 position: fixed;
  width: 100%;
  bottom: 5px;
}
.stats {
  position: absolute;
  top: 5px;
  left: 5px;
}

JS

// GIF Generation: https://github.com/antimatter15/jsgif
class ThreeBasic {
  constructor(withControls = false){
    this.hasControls = withControls;
    this.useControls = false;
    this.renderer = null;
    this.camera = null;
    this.scene = null;
    this.controls = null;
    this.getClampedData = this.getClampedData.bind(this);
  }
  
  
  init(){
    const VIEW_ANGLE = 45,
    ASPECT = window.innerWidth / window.innerHeight,
    NEAR = 0.1,
    FAR = 10000;
    const camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    camera.position.z = 20;
    camera.rotation.x = 20 * Math.PI / 180;
    camera.updateProjectionMatrix();
    
    
    const scene = new THREE.Scene();
    scene.background = new THREE.Color( 0xDDA0DD );
    scene.fog = new THREE.Fog( 0x800080, 0.2,350. );
    
    const renderer = new THREE.WebGLRenderer({ antialias: true});
    document.body.appendChild(renderer.domElement);
    if(this.hasControls){
      this.controls = new THREE.OrbitControls(camera, renderer.domElement);
    }
    
    
    this.camera = camera;
    this.scene = scene;
    this.renderer = renderer;
    this.onResize();
  }
  add(mesh){
    this.scene.add(mesh);
  }
getClampedData (width,height){
    // change size if its bigger that gl canvas
    const gl = this.renderer.context;
    var pixels = new Uint8Array(width * height * 4);
    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    const clamped = Uint8ClampedArray.from(pixels);
    return clamped;
}
onStart(){
  
}
onResize(){
  console.log('resize')
  this.camera.aspect = window.innerWidth / window.innerHeight;
  this.camera.updateProjectionMatrix();
  
  this.renderer.setSize(window.innerWidth, window.innerHeight);
  // uniforms.u_res.value.x = renderer.domElement.width;
  // uniforms.u_res.value.y = renderer.domElement.height;
  this.render(this.scene,this.camera);
}
  render(){
	  this.renderer.render( this.scene, this.camera );
  }
}
class Giffer {
  constructor(width,height,totalFrames = 5){
    this.encoder = new GIFEncoder();
    this.encoder.setSize(width,height);
    this.encoder.setRepeat(0);
    this.encoder.setDelay(1000/60);
    this.width = width;
    this.height = height;
    this.totalFrames = totalFrames;
    this.frames= 0;
    this.started = false;
    this.done = false;
    this.isActive = false;
    
   }
  initializeEncoder(){
    this.encoder = new GIFEncoder();
    this.encoder.setSize(width,height);
    this.encoder.setRepeat(0);
    this.encoder.setDelay(100);
  }
  start(totalFrames = 1, width, height){
    this.started = true;
    this.frames = 0;
    this.done = false;
    this.totalFrames = totalFrames;
    if(width && height) {
      this.width  = width;
      this.height = height;
    }
  }
  onInitialFrame(){
    this.encoder.setSize(this.width,this.height);
    this.encoder.start();
  }
  capture(getData) {
    if(this.done || !this.started ) return;
    if(this.frames === 0) {
      this.onInitialFrame();
    }
    if(this.frames >= this.totalFrames){
      this.onFinish();
      return;
    }
    const clampedData = getData(this.width,this.height);
    this.encoder.addFrame(clampedData,true);
    
    this.frames++;
  }
  onFinish(){
    this.encoder.finish();
    this.encoder.download('download.gif');
    this.done = true;
  }
}


const getShaders = (name, options) =>{
  const shaders = {
    fragmentShader: (document.getElementById(name+'-fragment') || document.getElementById('fragment')).textContent,
    vertexShader: (document.getElementById(name+'-vertex') || document.getElementById('vertex')).textContent
  }
  if(options){
    
  if(options.noise === true){
    const noise = document.getElementById('noise');
    if(noise){
      shaders.fragmentShader = noise.textContent + shaders.fragmentShader;
      shaders.vertexShader = noise.textContent + shaders.vertexShader;
    } else {
      console.error('NOISE NOT FOUND');
    }
  }
  }
  
  
  return shaders;
}

const app = new ThreeBasic(false);
app.init();

const framesPerLoop = 60;
const config = {
  framesPerLoop: framesPerLoop,
  timeSpeed: Math.PI*2./framesPerLoop *0.1
}
// CODE GOES HERE
  var file = document.getElementById("thefile");
  var audioElement = document.getElementById("audio");

  file.onchange = function(){
    audioElement.classList.add('active');
    var files = this.files;
    
    audioElement.src = URL.createObjectURL(files[0]);
    audioElement.load();
    audioElement.addEventListener('play', ()=>{
      config.timeSpeed = Math.PI*2./framesPerLoop;
      play()
    }) 
  }
function play(){
		audio.setMediaElementSource( audioElement );
}
let listener = new THREE.AudioListener();
let audio = new THREE.Audio(listener);

		var fftSize = 128;
let analyser = new THREE.AudioAnalyser(audio, fftSize);

const uniforms = {
   u_time: {type:'f', value: 0},
  uAudio: { value: new THREE.DataTexture( analyser.data, fftSize / 2, 1, THREE.LuminanceFormat ) },
    fogColor:    { type: "c", value: app.scene.fog.color },
    fogNear:     { type: "f", value: app.scene.fog.near },
    fogFar:      { type: "f", value: app.scene.fog.far },
    uMove:        {type:'f', value: 0.},
  uAverage: {type: 'f', value: 0.}
}

let segments = 200;
let height = 500;
let width = 200;
let geometry = new THREE.PlaneBufferGeometry(width,height,segments,segments);
geometry.rotateX(-20);

const material = new THREE.ShaderMaterial({
  uniforms,
  side: THREE.DoubleSide,
  ...getShaders('',{noise: true}),
  fog: true
});
console.log(material);
const mesh = new THREE.Mesh(geometry, material);

// ADD TO SCENE
app.add(mesh);

// STATS
let stats = new Stats();
stats.showPanel(0);
stats.domElement.className = "stats"
document.body.appendChild( stats.domElement );


// GUI
const gui = new dat.GUI()
 // GUI controls go here
const gui_time = gui.add(config,'timeSpeed',Math.PI*2./240,Math.PI*2./60, Math.PI/120).name('Frame speed');
gui_time.onChange((r)=>{
  config.framesPerLoop = Math.floor((Math.PI * 2)/r);
})
// gui_time.onFinishChange(()=>{})

// GIF maker stuff
const giffy = new Giffer(window.innerWidth,window.innerHeight, config.framesPerLoop);
const gui_functions = {
  start_gif: ()=>{
    giffy.start(config.framesPerLoop,app.renderer.context.drawingBufferWidth,app.renderer.context.drawingBufferHeight);
  },
  randomize: ()=>{
    // Generate a new seed or something
  }
}
gui.add(gui_functions,'randomize').name("Randomize");
gui.add(gui_functions,'start_gif').name("Generate GIF");

// Life loop
// var capturer = new CCapture( { format: 'gif', workersPath: 'https://cdn.jsdelivr.net/gh/spite/ccapture.js/src/' } );
let frames = 0;

    var bufferLength = analyser.frequencyBinCount;
    var dataArray = new Uint8Array(bufferLength);
const update = ()=>{
		const data = analyser.getFrequencyData();
  
const average = avg(data);
		uniforms.uAudio.value.needsUpdate = true;
  uniforms.u_time.value += config.timeSpeed  ;
  uniforms.uMove.value += (average * 0.005);
  uniforms.uAverage.value = analyser.getAverageFrequency();
  // mesh.rotation.y=uniforms.u_time.value;
}
function draw(){
  stats.begin();
  app.render();
  update();
  stats.end();
  requestAnimationFrame(draw)
  giffy.capture(app.getClampedData);
  // if(frames < 10){
  //   capturer.capture(app.renderer.domElement);
  // } else if(frames === 10){
  //   capturer.stop();
  //   capturer.save();
  // }
  // frames++;
  }
function init(){
  // capturer.start()
  app.onStart();
  requestAnimationFrame(draw)
}


window.addEventListener('resize', ()=>{
  app.onResize();
  
  
});
window.addEventListener('mousemove',(e)=>{
  // uniforms.u_mouse.value.x = e.clientX/window.innerWidth;
  // uniforms.u_mouse.value.y = e.clientY/window.innerHeight;
})

init();
function avg(arr){
  var sum = 0;
  for(var i = arr.length-1.;i>-1;i--){
    sum +=  arr[i];
  }
   return (sum / arr.length) || 0;
}


function max(arr){
  var max = arr[0];
  for(var i = arr.length-1.;i>-1;i--){
    max =  arr[i] > max ? arr[i] : max;
  }
   return max;
}

猜你喜欢

转载自blog.csdn.net/weixin_45544796/article/details/107344148