
マイケル
みなさんこんにちは!
マイケルです!
マイケルです!

エレキベア
こんにちクマ〜〜〜

マイケル
今回は久しぶりのp5.jsを使ったゲーム数学シリーズ!
テーマは・・・「微分」です!!
テーマは・・・「微分」です!!

エレキベア
また久しぶりクマね〜〜〜
微分は難しそうクマ・・・
微分は難しそうクマ・・・

マイケル
ゲーム開発でも直接微分を使うことも少ないので、あまり実装したことがない人も多いのではないでしょうか!
今回はプログラムでの微分の実装と、微分の知識を使って波動方程式を解いて、簡単な波のシミュレーションを行ってみようと思います!
今回はプログラムでの微分の実装と、微分の知識を使って波動方程式を解いて、簡単な波のシミュレーションを行ってみようと思います!

エレキベア
中々本格的クマね〜〜〜
楽しみクマ
楽しみクマ
微分とは

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

エレキベア
ほえぇ・・・・

マイケル
まあよく分からないと思うので、図で書いてみます!
まず適当な関数を置いて、ある値xと、hだけ進んだx+hの値に注目します。
まず適当な関数を置いて、ある値xと、hだけ進んだx+hの値に注目します。


マイケル
このxとx+hの値を比べることで、どれだけ変化したかが分かります。
この差分hを極限まで0に近づければ地点xの傾きが求められるであろう、というのが微分の定義です。
この差分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に近づけるほど精度が向上します。
いろいろ値を変えてみると導関数の描画が少し変化するのが確認できると思います!
いろいろ値を変えてみると導関数の描画が少し変化するのが確認できると思います!

エレキベア
ただ変化量を見るだけと考えると、実装はすごく簡単クマね




エレキベア
こうして見てみると結構面白いクマね

マイケル
微分を理解するには、実際に図として確認するのがよさそうだね
応用:傾きと法線を求める

マイケル
これを実際にゲーム開発でどう活用するか?についてですが、分かりやすいのが地形のある地点の法線を求める場合の活用ですね

マイケル
地形を関数として描画している場合、ある地点を微分した値を求めることで、傾きと法線を求めることができます。
下記がある関数と、ある地点の傾き、法線をアニメーションで表示したものになります。
下記がある関数と、ある地点の傾き、法線をアニメーションで表示したものになります。
See the Pen
20221218_derivative_normal by masarito617 (@masarito617)
on CodePen.


エレキベア
おお〜〜〜
まるで関数の上を滑っているみたいクマ
まるで関数の上を滑っているみたいクマ

マイケル
関数の形状によって、船のような動きやスキ−のような動きをさせることもできます。
こちらもfunc関数の中身を書き換えることで変更することができるので、いろいろ試してみてください!
こちらもfunc関数の中身を書き換えることで変更することができるので、いろいろ試してみてください!



エレキベア
これも見てると中々面白いクマね〜〜〜
応用:波動方程式を使った波の動き

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

エレキベア
このシリーズを一通り見るだけでもかなり勉強になりそうクマ

マイケル
まず、波の動きをシミュレートするための波動方程式は、下記で表すことができます。
$$ \frac{1}{s^2}\frac{\partial^2u}{\partial t^2}=\Delta u $$

エレキベア
うへ〜〜〜
難しそうクマ〜〜〜
難しそうクマ〜〜〜

マイケル
この内、右辺は3次元を考慮したものになっているため、
今回は1次元に簡略化して下記のように定義します。
今回は1次元に簡略化して下記のように定義します。
$$ \frac{1}{s^2}\frac{\partial^2u}{\partial t^2}=\frac{\partial^2u}{\partial x^2} $$

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

エレキベア
ここまで来れば何とか意味は分かってきたクマ・・・

マイケル
2回微分するとはどういうことかというと、dt分の変化量の変化量、ということになります。
つまり、ある関数の2地点について微分し、それぞれの変化量をみればよい、ということです。
これを図に書くと下記のようになります。
つまり、ある関数の2地点について微分し、それぞれの変化量をみればよい、ということです。
これを図に書くと下記のようになります。


マイケル
tに関しては現在の値をV、未来の値をN、過去の値をPとして変化量を見ると求めることができます。
xに関しても同様に少し先の値R、少し前の値Lとして考えればよさそうです。
xに関しても同様に少し先の値R、少し前の値Lとして考えればよさそうです。

エレキベア
なるほどクマ
2回微分するというのも実際にやってみればそんなに難しくなさそうクマね
2回微分するというのも実際にやってみればそんなに難しくなさそうクマね

マイケル
あとはこれを整理すれば、未来の地点Nの値を求める式が完成します!
これを使えば次の波の状態に更新していけばシミュレーションできそうですね。
これを使えば次の波の状態に更新していけばシミュレーションできそうですね。
$$ N=\frac{s^2\Delta t^2}{\Delta x^2}(R+L-2V)+2V-P $$


エレキベア
おおお〜〜
なんかできそうな気がしてきたクマ〜〜〜〜
なんかできそうな気がしてきたクマ〜〜〜〜

マイケル
なお、ネットに落ちている論文等を見てみると、
テイラー展開というものを使用して式を導出していることが分かります。
難しく書かれてはいますが、やっていることはほぼ同じであることも分かりますね!
テイラー展開というものを使用して式を導出していることが分かります。
難しく書かれてはいますが、やっていることはほぼ同じであることも分かりますね!

エレキベア
テイラー展開使う方がかっこいいクマね・・・

マイケル
それでは気を取り直して、これらを元に実装したのは下記になります!
マウスクリックした箇所から波を発生させるサンプルです。
マウスクリックした箇所から波を発生させるサンプルです。
See the Pen
20221218_derivative_wave by masarito617 (@masarito617)
on CodePen.


エレキベア
うおお〜〜〜
ちゃんと波っぽいクマ〜〜〜〜〜
ちゃんと波っぽいクマ〜〜〜〜〜

マイケル
波動方程式の計算は下記の部分!
wave配列にtとxを要素数分を格納して、先ほど求めた計算式で次の状態を求めるよう実装しています。
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で学ぶゲーム数学「微分の実装と波動方程式」〜完〜
コメント