Loading...

【ゲーム数学】第六回 p5.jsで学ぶゲーム数学「微分の実装と波動方程式」

JavaScript
マイケル
マイケル
みなさんこんにちは!
マイケルです!
エレキベア
エレキベア
こんにちクマ〜〜〜
マイケル
マイケル
今回は久しぶりのp5.jsを使ったゲーム数学シリーズ
テーマは・・・「微分」です!!
エレキベア
エレキベア
また久しぶりクマね〜〜〜
微分は難しそうクマ・・・
マイケル
マイケル
ゲーム開発でも直接微分を使うことも少ないので、あまり実装したことがない人も多いのではないでしょうか!
今回はプログラムでの微分の実装と、微分の知識を使って波動方程式を解いて、簡単な波のシミュレーションを行ってみようと思います!
エレキベア
エレキベア
中々本格的クマね〜〜〜
楽しみクマ
スポンサーリンク

微分とは

マイケル
マイケル
まず微分とはなんぞやという話ですが、関数のある地点の傾き(変化率)を表したもののことで、
ある関数f(x)を微分した導関数f'(x)は下記の数式で表せます。

$$ f'(x)=\lim_{h\to0}\frac{f(x+h)-f(x)}{h} $$

エレキベア
エレキベア
ほえぇ・・・・
マイケル
マイケル
まあよく分からないと思うので、図で書いてみます!
まず適当な関数を置いて、ある値xと、hだけ進んだx+hの値に注目します。
ScreenShot 2022 12 18 23 14 00
マイケル
マイケル
このxとx+hの値を比べることで、どれだけ変化したかが分かります。
この差分hを極限まで0に近づければ地点xの傾きが求められるであろう、というのが微分の定義です。
エレキベア
エレキベア
なるほどクマ、その値の接線を求めるイメージクマかね
マイケル
マイケル
関数f(x)を微分した導関数f'(x)は下記のように求められます。

$$ f(x)=x^2\\f'(x)=2x\\f”(x)=2\\f”'(x)=0 $$

マイケル
マイケル
またsin(x)の微分はcos(x)cos(x)の微分は-sin(x)になります。
これも面白い性質ですね。

$$ f(x)=sin(x)\\f'(x)=cos(x)\\f”(x)=-sin(x) $$

エレキベア
エレキベア
定義は何となく分かったクマが、
これを実際どう使うのかがイメージ湧かないクマね・・・
マイケル
マイケル
とりあえず、実際に導関数を求めるプログラムを書いてみよう!

微分の実装

マイケル
マイケル
ということで、実際にある関数とそれを微分した導関数を描画するプログラムが下記になります!
青が関数、ピンクが導関数になりますね。

See the Pen
20221218_derivative
by masarito617 (@masarito617)
on CodePen.

マイケル
マイケル
関数と導関数を定義しているのは下記の部分!
導関数の説明部分は先ほど説明した内容と同様になっていますね。
// 関数
function func(x) {
  // return x**2;
  return x**3;
  // return -2 * x*x*x + 3 * x*x;
  // return cos(x);
  // return sin(x);
}

// 導関数
function derivative(x) {
  let h = 0.01; // ここを小さくするほど精度が上がる
  return (func(x+h) - func(x))/h;
}
マイケル
マイケル
このhの値を0に近づけるほど精度が向上します。
いろいろ値を変えてみると導関数の描画が少し変化するのが確認できると思います!
エレキベア
エレキベア
ただ変化量を見るだけと考えると、実装はすごく簡単クマね
ScreenShot 2022 12 18 2 35 05↑f(x)=x^2 の場合
ScreenShot 2022 12 18 2 35 40↑f(x)=x^3 の場合
ScreenShot 2022 12 18 2 36 13↑f(x)=cos(x) の場合
エレキベア
エレキベア
こうして見てみると結構面白いクマね
マイケル
マイケル
微分を理解するには、実際に図として確認するのがよさそうだね

応用:傾きと法線を求める

マイケル
マイケル
これを実際にゲーム開発でどう活用するか?についてですが、分かりやすいのが地形のある地点の法線を求める場合の活用ですね
マイケル
マイケル
地形を関数として描画している場合、ある地点を微分した値を求めることで、傾きと法線を求めることができます。
下記がある関数と、ある地点の傾き、法線をアニメーションで表示したものになります。

See the Pen
20221218_derivative_normal
by masarito617 (@masarito617)
on CodePen.

ScreenShot 2022 12 18 2 36 53↑関数のある地点での傾きと法線が表示される
エレキベア
エレキベア
おお〜〜〜
まるで関数の上を滑っているみたいクマ
マイケル
マイケル
関数の形状によって、船のような動きやスキ−のような動きをさせることもできます。
こちらもfunc関数の中身を書き換えることで変更することができるので、いろいろ試してみてください!
01 normal↑船のような動き
02 normal↑こちらはスキーのようだ
エレキベア
エレキベア
これも見てると中々面白いクマね〜〜〜

応用:波動方程式を使った波の動き

マイケル
マイケル
最後に、微分の知識を使って「波動方程式」というものを解いて波の動きを作ってみます!
こちらはUnity公式の下記動画を参考にさせていただきました。
安原さんの数学系の動画は、好きな気持ちがすごく伝わってきて何度も見てしまいますね・・・。

エレキベア
エレキベア
このシリーズを一通り見るだけでもかなり勉強になりそうクマ
マイケル
マイケル
まず、波の動きをシミュレートするための波動方程式は、下記で表すことができます。

$$ \frac{1}{s^2}\frac{\partial^2u}{\partial t^2}=\Delta u $$

エレキベア
エレキベア
うへ〜〜〜
難しそうクマ〜〜〜
マイケル
マイケル
この内、右辺は3次元を考慮したものになっているため、
今回は1次元に簡略化して下記のように定義します。

$$ \frac{1}{s^2}\frac{\partial^2u}{\partial t^2}=\frac{\partial^2u}{\partial x^2} $$

マイケル
マイケル
この中で、2乗等行っている箇所が分かりにくいですが、これはそれぞれ2回微分するのと同様の意味になります。
xを微分している箇所について整理すると下記のように表せます。

$$ \frac{\partial^2u}{\partial x^2}=\frac{\partial}{\partial x}\frac{\partial}{\partial x}u $$

エレキベア
エレキベア
ここまで来れば何とか意味は分かってきたクマ・・・
マイケル
マイケル
2回微分するとはどういうことかというと、dt分の変化量の変化量、ということになります。
つまり、ある関数の2地点について微分し、それぞれの変化量をみればよい、ということです。
これを図に書くと下記のようになります。
ScreenShot 2022 12 18 23 14 35
マイケル
マイケル
tに関しては現在の値をV、未来の値をN、過去の値をPとして変化量を見ると求めることができます。
xに関しても同様に少し先の値R、少し前の値Lとして考えればよさそうです。
エレキベア
エレキベア
なるほどクマ
2回微分するというのも実際にやってみればそんなに難しくなさそうクマね
マイケル
マイケル
あとはこれを整理すれば、未来の地点Nの値を求める式が完成します!
これを使えば次の波の状態に更新していけばシミュレーションできそうですね。

$$ N=\frac{s^2\Delta t^2}{\Delta x^2}(R+L-2V)+2V-P $$

ScreenShot 2022 12 18 23 14 47
エレキベア
エレキベア
おおお〜〜
なんかできそうな気がしてきたクマ〜〜〜〜
マイケル
マイケル
なお、ネットに落ちている論文等を見てみると、
テイラー展開というものを使用して式を導出していることが分かります。
難しく書かれてはいますが、やっていることはほぼ同じであることも分かりますね!

波のシミュレーション

エレキベア
エレキベア
テイラー展開使う方がかっこいいクマね・・・
マイケル
マイケル
それでは気を取り直して、これらを元に実装したのは下記になります!
マウスクリックした箇所から波を発生させるサンプルです。

See the Pen
20221218_derivative_wave
by masarito617 (@masarito617)
on CodePen.

03 wave
エレキベア
エレキベア
うおお〜〜〜
ちゃんと波っぽいクマ〜〜〜〜〜
マイケル
マイケル
波動方程式の計算は下記の部分!
wave配列にtとxを要素数分を格納して、先ほど求めた計算式で次の状態を求めるよう実装しています。
let s = 4; // 波の速度(m/s)
let dt = 1/60; // 1ステップの時間(s)
let dx = 0.2; // 要素間の距離(m)
let alpha = (s*dt/dx)**2; // 波動方程式で使用する係数
let attenuation = 0.985; // 適当な減衰率

// u(x,t) -> wave[t][x]
// tは過去、現在、未来で3つ分格納しておく.
// [0]未来 -> [1]現在 -> [2]過去 
let wave = [];
let waveLengthX = 50+1;

// 波の描画
function drawWave() {
  // 波情報を初期化
  if (wave.length == 0) {
    for (let i = 0; i < 3; i++) { // 未来、現在、過去の3つ分
      wave.push(new Array(waveLengthX).fill(0));
    }
  }
  
  // マウスクリックで波を発生させる
  if (mouseIsPressed) {
    let x = Math.floor(mouseX / width * maxGridX);
    let maxPower = 20;
    let power = Math.abs(height/2 - mouseY)/height/2 * maxPower;
    wave[0][x] = power; // 適当に波を発生させてみる
  }
  
  // 波情報を更新
  wave.unshift(wave.pop()); // 一つ分動かす
  for (let x = 0; x < waveLengthX; x++) { // 端は0で固定する
    // 輪の場合、端と端を繋ぐ
    // let prevX = x == 0 ? waveLengthX-1 : x-1;
    // let nextX = x == waveLengthX-1 ? 0 : x+1;
    
    // 自由端の場合、端は一つ内側を見る
    let prevX = x == 0 ? 1 : x-1;
    let nextX = x == waveLengthX-1 ? waveLengthX-2 : x+1;
    
    // 波動方程式を使った計算
    let V = wave[1][x];
    let R = wave[1][nextX];
    let L = wave[1][prevX];
    let P = wave[2][x];
    // 次の状態であるNを求める
    let N = alpha*(R+L-2.0*V) + 2.0*V - P;
    N *= attenuation; // 波を減衰させる
    wave[0][x] = N;
  }
  
  // 波を描画
・・・省略・・・
}
マイケル
マイケル
意外とコード量も少なくシンプルにまとまっているのが分かりますね!
なお実装するにあたり、下記の記事を参考にさせていただきました。
本当に助かりました・・・・・。

波のシミュレーション

エレキベア
エレキベア
これだけで波が実装できるなんて胸熱クマ〜〜〜

おわりに

マイケル
マイケル
というわけで、今回は微分の実装と活用方法についてでした!
どうだったかな??
エレキベア
エレキベア
学校だと問題を解くだけだったクマが、やっぱり実際に使ってみると楽しいクマね
波を作れたのは感動したクマ〜〜〜
マイケル
マイケル
中々イメージしにくい微分だけど、プログラムを書くことで確実に理解は深まる気がするね!
簡単なので是非みなさんも実装してみてください!
マイケル
マイケル
それでは今日はこの辺で!
アデューーー!!
エレキベア
エレキベア
クマ〜〜〜〜〜

【ゲーム数学】第六回 p5.jsで学ぶゲーム数学「微分の実装と波動方程式」〜完〜

コメント