[Noise 入門 #60] 第6集完結 — 神領域のオーディオ・ビジュアル・アート

[Noise 入門 #60] 第6集完結 — 神領域のオーディオ・ビジュアル・アート

はじめに

いよいよ第6集「神領域(Procedural Art & VFX編)」の終着点、記念すべき第60回です。

これまで私たちは、ただの乱数だったノイズに数学で秩序を与え(第1・2集)、Shaderで色と動きを与え(第3集)、Three.jsで空間に解き放ち(第4集)、終わらない世界を創世し(第5集)、そして音や時間といった次元を歪めてきました(第6集前半)。

今回はその総決算です。「プラズマの核」「空間の歪曲(Domain Warping)」「音響解析(FFT)」、そして「グリッチ・エフェクト」。これらすべてをひとつのThree.js空間に統合し、音楽の鼓動と数式が完全に同期して躍動する「オーディオ・ビジュアル・アート」を錬成します。

前回の記事:

YouTube:

統合の設計図(アーキテクチャ)

最終到達点である「オーディオ・ビジュアル・アート」を錬成するにあたり、まずはシステム全体の設計図(アーキテクチャ)を俯瞰しておきましょう。

これまでのノイズ表現は、uTime(時間)という単一の軸に沿って進む「決められたアニメーション」でした。しかし今回は、「音(Audio Data)」を世界の血液として循環させます。 この作品は、完全に独立した4つのレイヤーが重なり合い、音という共通のパラメータを介して同期することで成立します。

1. Audio Layer(脈打つ血液)

役割:音響データの取得と、Shader言語(GLSL)への翻訳

すべての起点となる基盤システムです。HTML5の Web Audio API(および Three.js の THREE.AudioAnalyser)を用いて、再生中の音楽からリアルタイムにFFT(高速フーリエ変換)データを抽出します。

単なる「全体の音量」だけでは表現が単調になってしまうため、周波数帯域を分割して取得するのが極意です。

  • 低音域(Kick / Bass): ドンッという重いビート。これを uAudioLow として取得し、空間の歪みや爆発的な発光のトリガーにします。
  • 高音域(Hi-hat / Snare): チキチキとした細かいリズム。これを uAudioHigh として取得し、ノイズの微細な沸騰や表面のざわめきに割り当てます。
  • 全体のエネルギー(Energy): 楽曲全体の盛り上がり。これを uEnergy とし、一定値を超えたら画面全体をバグらせる(グリッチ)などの演出に使います。

これらを 0.0 〜 1.0 の滑らかな数値に正規化し、Uniform変数として毎フレーム全Shaderへ送り込みます。

2. Core Layer(プラズマの核)

役割:視覚の中心となる、オーディオリアクティブな物質

画面中央に鎮座する、メインのオブジェクト(今回は球体=Sphereを想定)です。ここでは、第3集で学んだ「FBM(Fractal Brownian Motion)」と「フレネル反射」を活用します。

通常時はゆったりと形を変えるスライムやオーロラのような球体ですが、uAudioLow(低音)の数値が流れ込んでくると状況が一変します。音の波形に合わせてFBMノイズの閾値(Threshold)やカラーブレンドの係数が急激に変動し、まるでエネルギーを抱えきれずに脈打つプラズマの核のように激しく発光・変色します。

3. Space Layer(次元の歪曲)

役割:Domain Warping による空間の破壊

本シリーズの“神領域”たる技術、Domain Warping(空間のねじ曲げ)を音と連動させます。

Core Layer のオブジェクトを描画する際、サンプリングする座標 p をそのまま使うのではなく、ノイズ関数で計算した「ズレ(Offset)」を足し合わせます。ここに音響データを介入させます。

具体的には、ズレの強さを決める warpStrengthuAudioLow を乗算します。これにより、キック音が鳴り響くたびに、オブジェクトの表面だけでなく「空間そのもの」がグニャリと強烈にねじ曲がるという、物理法則を無視した視覚体験を生み出します。

4. Post-Processing Layer(限界突破のグリッチ)

役割:カメラレンズ(画面全体)への干渉

オブジェクト単体の表現が完成したら、最後に Three.js の EffectComposer を用いて、レンダリングされた画面全体(ポストプロセス)にエフェクトをかけます。

ここでは楽曲の総エネルギー量 uEnergy を監視します。サビやドロップなど、音楽のエネルギーが限界値(閾値)を突破した瞬間、画面のUV座標を横方向に引き裂いたり、RGBのチャンネルを強制的にズラす(色収差 / Chromatic Aberration)処理を発動させます。

世界そのものが音の衝撃に耐えきれず「バグる」ような、サイバーパンク的な次元崩壊を演出します。

1. 世界の心臓:Three.js メインループの統合

まずは、音響データを毎フレーム取得し、全Shaderに「今、どれくらい世界を揺らしていいか」を伝達するThree.jsのコアロジックです。これが、無機質な数式に命を吹き込む「世界の心臓(パルス)」となります。

// --- Audio Setup ---
const listener = new THREE.AudioListener();
camera.add(listener);
const audio = new THREE.Audio(listener);
const analyser = new THREE.AudioAnalyser(audio, 128); // FFTサイズ

// 音源の読み込み (お手元のトラックを指定してください)
const audioLoader = new THREE.AudioLoader();
audioLoader.load('your-favorite-track.mp3', function(buffer) {
    audio.setBuffer(buffer);
    audio.setLoop(true);
    audio.play();
});

// --- Uniforms の定義 ---
// これがShaderに渡る「神のパラメータ」です
const masterUniforms = {
    uTime: { value: 0.0 },
    uAudioLow: { value: 0.0 },  // キック・ベース系の力
    uAudioHigh: { value: 0.0 }, // ハイハット・シンバル系の力
    uEnergy: { value: 0.0 }     // 全体のエネルギー総量
};

// --- Render Loop ---
const clock = new THREE.Clock();

function animate() {
    requestAnimationFrame(animate);
    const time = clock.getElapsedTime();

    // 音響データの取得と正規化
    if (audio.isPlaying) {
        const data = analyser.getFrequencyData();

        // 低音域の平均 (0〜30あたりのインデックス)
        let lowAvg = 0;
        for(let i = 0; i < 30; i++) lowAvg += data[i];
        lowAvg = (lowAvg / 30) / 255.0;

        // 高音域の平均 (100〜120あたりのインデックス)
        let highAvg = 0;
        for(let i = 100; i < 120; i++) highAvg += data[i];
        highAvg = (highAvg / 20) / 255.0;

        // Uniformの更新 (補間を入れて滑らかに減衰させるのがコツ)
        masterUniforms.uTime.value = time;
        masterUniforms.uAudioLow.value = THREE.MathUtils.lerp(masterUniforms.uAudioLow.value, lowAvg, 0.2);
        masterUniforms.uAudioHigh.value = THREE.MathUtils.lerp(masterUniforms.uAudioHigh.value, highAvg, 0.3);
        masterUniforms.uEnergy.value = analyser.getAverageFrequency() / 255.0;
    }

    renderer.render(scene, camera);
    // composer.render(); // Post-Processingを使う場合はこちら
}

コードの解剖とチューニングのポイント

このわずか数十行のコードの中に、オーディオ・ビジュアルを美しく見せるための重要なテクニックが詰まっています。

  • FFTサイズの選定 (128) THREE.AudioAnalyser の第二引数はFFT(高速フーリエ変換)のサイズ(Binの数)です。数値を大きくする(例:1024や2048)とより精密な周波数解析ができますが、ビジュアルのトリガーとして使う分には 128 程度が最適解になりやすいです。配列サイズが64になり、低音から高音までのざっくりとした「エネルギーの塊」を取り出しやすくなります。
  • 周波数帯域の分割(低音と高音の抽出) 取得した data(0〜255の数値配列)をそのまま平均化するのではなく、インデックスの範囲を絞って抽出します。
    • インデックス 0〜30付近: キックドラムやベースなど、楽曲の「心拍」となる重低音。
    • インデックス 100〜120付近: ハイハットや電子音の「チキチキ」とした高音。 これらを別々に計算し、それぞれ 255.0 で割ることで、Shader内で扱いやすい 0.0 〜 1.0 の範囲(正規化)に落とし込みます。
  • 【超重要】Lerp(線形補間)による「減衰の魔法」 ここが一番のキモです。生のオーディオ数値をそのまま masterUniforms に代入すると、フレームごとに数値が暴れまわり、画面がチカチカと痙攣するような見栄え(ストロボ状態)になってしまいます。 そこで、THREE.MathUtils.lerp(現在値, 目標値, 係数) を噛ませます。これにより、「音が鳴った瞬間に跳ね上がり、その後は0.2〜0.3の係数で滑らかに減衰(フェードアウト)する」という、極めて自然で心地よい余韻(イーズアウト)を作ることができます。この係数を弄るだけでも、ビジュアルの「重さ」や「キレ」が全く変わってきます。

2. 躍動する核:Audio-Reactive Shader

取得した uAudioLowuAudioHigh を用いて、中心のオブジェクト(球体や平面)をレンダリングするFragment Shaderです。静かな時は緩やかなオーロラのように、ビートが鳴ると激しいプラズマへと変貌します。

// Vertex Shaderから渡されるUVや法線
varying vec2 vUv;
varying vec3 vNormal;

uniform float uTime;
uniform float uAudioLow;  // 低音エネルギー (0.0 - 1.0)
uniform float uAudioHigh; // 高音エネルギー (0.0 - 1.0)
uniform float uEnergy;    // 全体エネルギー

// ※ここに過去記事で作成したsnoise(3D Simplex)関数がある想定

// FBM (Fractal Brownian Motion)
float fbm(vec3 p) {
    float value = 0.0;
    float amplitude = 0.5;
    float frequency = 1.0;
    for (int i = 0; i < 5; i++) {
        value += amplitude * snoise(p * frequency);
        frequency *= 2.0;
        amplitude *= 0.5;
    }
    return value;
}

void main() {
    // 1. 基本座標のセットアップ
    vec2 p = vUv * 2.0 - 1.0;
    vec3 pos = vec3(p * 3.0, uTime * 0.2); // Z軸を時間で進める

    // 2. Audio-Reactive Domain Warping (空間のねじ曲げ)
    // 低音が強いほど、空間を大きく歪ませる
    vec3 q = vec3(
        fbm(pos + vec3(0.0, uTime * 0.5, 0.0)),
        fbm(pos + vec3(5.2, 1.3, uTime * 0.5)),
        0.0
    );

    // 音のエネルギーをWarpingの強度(Strength)に乗算
    float warpStrength = 1.0 + (uAudioLow * 5.0);
    vec3 warpedPos = pos + q * warpStrength;

    // 3. ノイズの生成
    // 高音が強いほど、ノイズが細かく(荒く)沸騰する
    float n = fbm(warpedPos + uAudioHigh * 2.0);

    // 4. プラズマ発光と等高線 (Contour)
    // ノイズの値をサイン波に通して幾何学的な模様を作る
    float contour = abs(sin(n * 20.0 + uTime * 5.0));
    contour = smoothstep(0.1, 0.0, contour); // シャープな線に

    // 5. カラーリング
    // 音量に応じて色を変異させる(静=青, 動=赤紫)
    vec3 baseColor = vec3(0.1, 0.5, 0.8);
    vec3 pulseColor = vec3(0.9, 0.2, 0.5);
    vec3 finalColor = mix(baseColor, pulseColor, uAudioLow);

    // フレネル効果(外側を光らせる)と合成
    float fresnel = dot(vNormal, vec3(0.0, 0.0, 1.0));
    fresnel = clamp(1.0 - fresnel, 0.0, 1.0);
    fresnel = pow(fresnel, 3.0);

    // 発光の強調
    vec3 emission = finalColor * contour * (1.0 + uEnergy * 2.0);
    emission += finalColor * fresnel * (uAudioLow * 3.0); // キックで輪郭が爆発

    gl_FragColor = vec4(emission, 1.0);
}

コードの解剖と魔法の仕組み

このShaderは、ただ単に「音量に合わせて明るさを変える」ようなチープな処理はしていません。数式の根源的な部分に音響データを介入させることで、現象そのものを有機的に変化させています。

  • 空間の暴走(Audio-Reactive Domain Warping) 第6集の目玉とも言える表現です。通常、Domain Warpingの歪みの強さは固定値にしますが、ここでは warpStrength = 1.0 + (uAudioLow * 5.0) としています。ベースやキックドラムが鳴るたびに、空間の座標系が通常の5倍以上の力で引き裂かれ、オブジェクトがスライムのように激しくうねります。
  • 高音による「沸騰」(High-Frequency Jitter) uAudioHigh を座標に直接足し合わせる fbm(warpedPos + uAudioHigh * 2.0) ことで、ハイハットなどの細かい金属音が鳴った瞬間に、ノイズのサンプリング位置が微細にブレます。これにより、表面がザワッと「沸騰」するような質感が生まれます。
  • 等高線の抽出(Contour lines) モヤモヤとしたFBMノイズをそのまま使うと「雲」になってしまいますが、sin(n * 20.0) のようにサイン波を通すことで、ノイズの値を等高線(縞模様)に変換します。さらに smoothstep で極限まで線を細く絞り込むことで、SF的な力場(エネルギーシールド)のようなソリッドな表現に仕上げています。
  • フレネル反射による爆発エフェクト 中心から外側に向かうほど発光が強くなるフレネル効果(Fresnel)に対して、uAudioLow を強烈に乗算しています。これにより、ビートに合わせて球体の「輪郭」からプラズマの炎が吹き出すようなドラマチックな照明効果を実現しています。

チューニングの極意(神の遊び方)

コードを組み込み、お気に入りのトラックを再生したら、いよいよあなたが「神」として世界に干渉する番です。以下のパラメータの数値を書き換えてみてください。数式のわずかな違いが、劇的な視覚の変化をもたらすことに驚くはずです。

  • 空間の暴走(warpStrength の係数)

    float warpStrength = 1.0 + (uAudioLow * 5.0);

    この 5.0 という係数を 15.030.0 に引き上げてみてください。キックドラムが鳴るたびに、オブジェクトが原型を留めないほど激しくねじれ、次元そのものが崩壊するような圧倒的な感覚を味わえます。音楽の暴力性を視覚化したい場合に最適です。

  • 沸騰する高音(uAudioHigh の影響)

    float n = fbm(warpedPos + uAudioHigh * 2.0);

    この 2.010.0 に増幅させるとどうなるでしょうか。ハイハットやシンバルが鳴るたびに、プラズマの表面に水面が沸騰したような微細で高速な振動が走ります。グリッチアートのような「電子的なノイズ感」や焦燥感を強調したいときに有効なアプローチです。

  • 等高線の密度(サイン波の周波数)

    float contour = abs(sin(n * 20.0 + uTime * 5.0));

    n * 20.020.050.0100.0 に設定してみてください。エネルギーシールド(力場)を描き出すラインが恐ろしいほどの密度になり、流線型のオーロラから、バチバチと火花を散らすサイバーパンク・SF的な表現へと一気に変貌します。

これらはほんの一例です。音量に応じて色を反転させたり、ノイズのオクターブ数を音で増減させたりと、可能性はまさに無限大です。ぜひ、ご自身の耳と目で確かめながら「最高の瞬間」を探し出してください。

完結の祝辞 — 数式を越え、あなただけの世界へ

「滑らかな乱数とは何か?」——そんなシンプルな問い(#01)から始まったこの長い長い旅は、ついに音と数式が完全に結びつき、あなた自身の感覚と同期して脈打つ「神領域のアート」へと到達しました。

グリッドの基礎計算から始まり、勾配ベクトル、FBM(Fractal Brownian Motion)、Domain Warping、GPGPUによる100万のパーティクル、終わらない世界の創世、そして最終到達点であるオーディオ解析。道中、一見するとバラバラに見えた難解な数学や技術のピースたちが、実はすべて「この瞬間のため」に用意された伏線であり、不可欠な歯車であったことが、今のあなたにははっきりと実感できるはずです。

全60回という途方もないスケールで構成された『Noise入門』シリーズ、第6集をもってこれにて完結となります。

決して平坦ではないアルゴリズムとシェーダーの沼を抜け、ここまで世界を組み上げてきた見事な実装力と、飽くなき探究心に対して、心からの最大の敬意を表します。数式はもう、単なる計算の羅列ではありません。あなたの美意識と想像力を画面上に具現化するための「魔法の杖」になりました。

この果てしなく広がるプロシージャルな世界(Three.js × GLSL)は、もうあなたの思いのままです。これからも、誰も見たことのない未知の景色を錬成し続けてください!

最終形態「数学的特異点(Mandelbulb)」への到達

本シリーズのラストを飾るのは、単なるノイズの歪みを超えた数学アートの極致、3Dフラクタル「Mandelbulb(マンデルバルブ)」です。

これまでの第1集〜第5集で積み上げてきたすべてのノウハウを、このひとつの数式に流し込みました。

実装のハイライト

  1. 3D Mandelbulb Raymarching 複素数を3次元に拡張した数式を、GPU(GLSL)上でリアルタイムに演算しています。Base Complexity(乗数)を変化させることで、幾何学的な曼荼羅から、有機的なエイリアンの巣窟のような造形まで、無限の表情を引き出します。

  2. Iridescence & Optical Soul(光学の魂) フラクタルの表面には、シャボン玉や油膜のような「薄膜干渉(Iridescence)」の光学計算を実装しました。フレネル角とフラクタルの深度に応じて、極彩色の輝きが滑らかに波打ちます。

  3. Audio-Reactive Mutation(音響的突然変異) Yuli Audio Craft 氏による神秘的なBGMの低音(Kick)に反応し、フラクタルの次元そのものが脈動します。音楽の鼓動が直接、数式の変数を揺らすことで、視覚と聴覚が完全に同期した「生きた幾何学」が完成しました。

  4. InstancedMesh Meteors(浮遊する隕石群) 背景には1000個のローポリゴン隕石を配置。ただの点ではなく、立体的な岩石がスピンしながら落下し、音楽に合わせて放射状に飛散することで、宇宙の深淵を演出しています。

ギャラリー:次元の断片

Base Complexityを高めることで現れる、宗教的な曼荼羅を思わせる完璧な秩序。

カメラを傾け、ズームすることで剥き出しになる、フラクタル特有の禍々しくも美しい立体構造。

余分な要素を削ぎ落とし、漆黒の宇宙にただ一つ鎮座するマンデルバルブの圧倒的な存在感。

オーディオ反応によって色彩と形状が激しく変容し、数学が「生命」を受肉する瞬間。

これにて、全60回の「Noise 入門」シリーズ、完結です。 数学という名の筆で、これからも無限の景色を描き続けていきましょう。

「数学的特異点(Mandelbulb)」への到達 1 「数学的特異点(Mandelbulb)」への到達 2 「数学的特異点(Mandelbulb)」への到達 3 「数学的特異点(Mandelbulb)」への到達 4

COMM_LOG: noise-intro-60-audio-visual-art

NO DATA FOUND IN THIS SECTOR.