WebGLで動画のポストプロセス/音声をWeb Audio APIのAnalyserNodeで解析して連動
概要
createMediaElementSourceでvideoの音声を取得、
analyserNode経由で解析した音声データをWebGLのシェーダーGLSLと連動させて
videoのテクセルを操作してリアルタイムでグリッチっぽいポストプロセスかけてます。
色々用語あっているのか不明ですが。
とりあえずの結果
色々相談重なって今月後半は忙しくなるぞーって思って片手間にゴニョゴニョやってたら未だ作業が始まらず。
フリーランスあるあるですがせっかくなので記事にしてみます。
素材動画は恥ずかしながら趣味でやってるバンドのリハーサル風景です。
Web Audio APIもスマホだと一回タップとか挟まないと制限があるのでロード用のUI入れてます。
こうゆう系僕の中ではmax/mspとかopenFrameworksとか利用する物という認識だったのですが
(あと使った事ないけどもSuperColliderとか?)
Audio API/WebGL使ってブラウザで結構さくさく動くので驚きです。
GPU処理っていうのが結構大きいのでしょうか?
[ブラウザ対応]
端末によっては見れないとか処理重いとかあると思います。
誰も興味ないと思いますが見れないよって場合に一応youtubeにもアップしてます。
こうゆうリアルタイム系の動画をデータとして形にするってどういうのが一般的なのだろうか、
サイズ大きいとquicktimeでフレーム落ちるので小さめです。
audio/analyserNode
video要素からcreateMediaElementSourceでaudioのsource取得して
this.video = document.createElement('video');
this.source = this.audioCtx.createMediaElementSource(this.video)
this.analyserNode = this.audioCtx.createAnalyser();
this.analyserNode.fftSize = 1024; //FFTサイズ
this.times = new Uint8Array(this.analyserNode.frequencyBinCount); //波形データ入れる配列Uint8Arrayで用意する
this.freqs = new Uint8Array(this.analyserNode.frequencyBinCount); //波形データ入れる配列Uint8Arrayで用意する
requestAnimationFrameとかでアップデートします
update(){
window.requestAnimationFrame(this.update.bind(this));
this.analyserNode.getByteFrequencyData(this.freqs); //周波数領域
this.analyserNode.getByteTimeDomainData(this.times); //時間領域
}
FFT
Wikipediaみても意味不明です。
フーリエ変換(多分離散フーリエ変換?)に関しては久々見ても窓関数や虚数とかしっかりとは良く分かってないのだが、
昔max(msp)触りながら下記のページとか参考に勉強させてもらって何となく自分の利用したいようには使えてるということで良しとします。
どこかでギター演奏の達人がギター本体やエフェクターとかの工学や細部を理解する必要はかならずしも無い。
とかの文言みかけて妙に納得したので甘んじて受け入れます。
描画側 WebGL/THREE
描画はWebGLです。THREE使ってます。
かなり抜粋ですがTHREE.VideoTexture使えば割と簡単に動画のテクスチャ利用ができました。
const uniforms = {
'uTex': {
type: "t",
value: new THREE.VideoTexture(window.glitch.audio.video)
};
// ~~~~
const material = new THREE.ShaderMaterial({
vertexShader : vertex,
fragmentShader : fragment,
uniforms : uniforms,
});
あとEffectComposerが分かりやすそうだったので利用しました。
ShaderPassにGLSL渡します。
GLSLもUV座標とかマトリックスとか何となーくの多少の知識でサンプル見ながら何とかとかします。
ポストプロセスなので多分fragmentShaderだけ変えたり加えたりしてればなんとなく変わります。
this.composer = new EffectComposer(this.renderer);
this.composer.addPass(new RenderPass(this.scene, this.camera));
this.effectPass = new ShaderPass("ここにGLSL");
this.composer.addPass(this.effectPass);
あとはまたrequestAnimationFrameとかでaudioの解析データに合わせて
uniformsとかに数値入れれば描画結果が変わります。
update(){
window.requestAnimationFrame(this.update.bind(this));
this.renderer.setClearColor(0x000000, 1.0);
this.renderer.clear();
// -------------------------
this.effectPass.uniforms.hoge.value = store.hoge;
// uniformsとかにaudio解析の結果とかの数値入れてrender
// -------------------------
this.composer.render(this.clock.getDelta());
}
描画と三角関数とかノイズ
GLSLに限らずですが描画に関しては色々な使い方あるとは思うのですが、
グリッジとかこうゆうアブストラクトな表現?って言い方であってるのか分からないけども
利用する場合とかもsin/cosの三角関数とかパーリンノイズとかの疑似乱数の使い方が重要になるのかなと個人的に思います。
例によって完全に理解はできないけども利用だけできれば個人的にはありだと思っています。
JavaScriptで取り組むクリエイティブコーディング – パーリンノイズを使いこなせ
以上です。