一畳のくつろぎタイム

このブログでは紹介する商品画像をAmazonアソシエイトより借りています。画像やリンクにはアフィリエイト広告が含まれる事があります

2026年2月22日日曜日

VRM待機モーションに命感を足す — fBm(Perlinノイズ合成)の活用

環境

  • three.js
  • pixiv/three-vrm

はじめに

AItuber-kitでの待機モーションは、VRMAモーションのidle_loop.vrmaやサッケード(急速眼球運動)、まばたきなどで動きが工夫されています。 止まった絵にならないよう配慮されており、十分に自然に見えます。

しかし長時間見ていると、どこか規則性を感じることがあります。 ループしているような印象というか、「生きている」という感覚が少し弱い。

配信で拝見したAITuber「零音ほのか」さんは、待機状態でもどこか躍動感があり、パターンを感じさせません。 そこで、その実装思想をVRMにも応用できないか試してみました。 

 

やっぱりsin波では足りなかった

最初は単純に sin 波で揺らしてみました。

Math.sin(t) * 係数

身体の回転や位置に適用すると確かに“揺れている”感じは出ます。 しかし sin 波は周期関数です。 一定時間で必ず同じ動きを繰り返します。

長く見ていると、その規則性を無意識に認識してしまい、 機械的な印象が残りました。

Perlinノイズ 

これまで調べた事がなかったのですが、sin波より不規則な波が作れるという認識です。 
使用したのは josephg さんの noisejs です。

https://github.com/josephg/noisejs

 

fBm

規則性を弱めるために、sin波による簡易実装ではなく本家ほのかさんの実装通りにfBm(フラクタルブラウン運動)も試しました。

noise発生源をnoisejsのperlin2に変えた形です。

実装例(ほのかさんのpythonによるfbmコードとほぼ同じ)


fbm(t, octaves = 3, lacunarity = 2.0, persistence = 0.5, yOffset = 0.0) {
    let value = 0.0;
    let amp = 1.0;
    let freq = 1.0;
    let maxVal = 0.0;

    for (let i = 0; i < octaves; i++) {
        value += amp * noise.perlin2(t * freq, yOffset);
        maxVal += amp;
        amp *= persistence;
        freq *= lacunarity;
    }

    return maxVal > 0.0 ? value / maxVal : 0.0;
}

周波数を段階的に上げながら、振幅を減衰させて足し合わせているらしいのですが
出てくるものはわかっても、中身はよくわからない。 

適用箇所

  • 身体のわずかな揺れ(fbm)
  • 首の微小な回転(Perlin Noise)
  • 目の瞬き間隔の不定期化(Perlin Noise)

特に瞬き間隔にノイズを使うことで、 「規則的なまばたき」から「生理的な揺らぎ」へと印象が変わりました。

結果

sin波と比べて、長時間見てもパターンを感じにくくなりました。

揺れ幅はごく小さいものです。 しかしその微小な揺らぎが加わることで、 立っているだけの状態から、そこにいる状態へと印象が変わります。


実際の動きが確認できます。

アリシア・ソリッドアバターによる稼働確認URL

👉https://kinkuman.net/threejs/ 
(three.jsとthree-VRM.jsによるモーション確認のみですが、グリーンバックがないところで右クリックしてソース見れます。)

 

まとめ

  • sin波は規則的
  • fBm、Perlinは非周期的
  • 生き物らしさは不規則な「揺らぎ」にある

数学的な理解が完全でなくても、 「周期を崩す」という意識だけで印象は大きく変わります。