diff --git a/12/README-jp.md b/12/README-jp.md index 22c0448..b8a66d8 100644 --- a/12/README-jp.md +++ b/12/README-jp.md @@ -37,7 +37,7 @@
-このサンプルでは、点の1つがマウスの位置に対応しています。何が行われているか、しっかり把握できるまで試してみてください。理解できたら下記に挑戦してみましょう。 +このサンプルでは、点の1つがマウスの位置に対応しています。何が行われているか、しっかり把握できるまで試してみてください。理解できたら以下に挑戦してみましょう。 - 他の点もアニメーションさせるにはどうすれば良いでしょう。 @@ -63,7 +63,7 @@ GLSLと配列、```for``` ループはあまり相性が良くないことに気 vec2 f_st = fract(st); ``` -次はどうするのでしょうか。このタイルの座標(```i_st``` に整数座標として格納されています)を点の位置をランダムに決めるために使います。```random2``` は ```vec2``` の値を受け取りランダムな座標を ```vec2``` で返します。こうして全てのタイルについて、その中でランダムな位置を持った点がひとつづつ決まります。 +次はどうするのでしょうか。このタイルの座標(```i_st``` に整数座標として格納されています)を点の位置をランダムに決めるために使います。```random2``` は ```vec2``` の値を受け取りランダムな座標を ```vec2``` で返します。こうして全てのタイルについて、その中でランダムな位置を持った点が1つずつ決まります。。 ```glsl vec2 point = random2(i_st); @@ -117,13 +117,13 @@ for (int y= -1; y <= 1; y++) { ... ``` -上のコードは[Inigo's Quilezの記事](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm) を参考にしました。彼はこう書いています。 +上のコードは[Inigo Quilezの記事](http://www.iquilezles.org/www/articles/smoothvoronoi/smoothvoronoi.htm)を参考にしました。彼はこう書いています。 -「上記のコードにはうまいトリックが使われていることに触れておいた方が良いかもしれません。世にある多くの実装は計算精度の問題を抱えています。ランダムな点を(ワールド空間やオブジェクト空間のような)ドメイン空間の中で生成するため、原点から点の位置が任意の距離だけ離れている可能性があります。 +>上記のコードにはうまいトリックが使われていることに触れておいた方が良いかもしれません。世にある多くの実装は計算精度の問題を抱えています。ランダムな点を(ワールド空間やオブジェクト空間のような)ドメイン空間の中で生成するため、原点から点の位置が任意の距離だけ離れている可能性があります。 (訳注:ドメイン空間 domain space — 数学的には「定義域」と訳したりしますが、ここでは分割された升目内のローカルな座標に対して、ワールド空間のようなプログラムが一般に使用する座標系を指している、くらいの解釈で良いと思います。) -この問題はコード全体でより精度の高いデータ型を用いるようにするか、もしくは少し頭を使うことで解決できます。私の実装では点の位置をドメイン空間ではなく、升目の中の空間で生成します。シェーディングする座標の整数部分と少数部分を抜き出して、どの升目を扱っているかを特定できれば、その升目の周囲で起きることだけに気をつければ良いことになります。つまり座標の整数部分をごっそり落としてその分のビットを精度を上げるために使うことができるのです。実際問題、標準的なボロノイの実装では、シェーディングする座標とランダムな点の座標の差を求める際に整数部分の値が打ち消し合うことになります。上の実装では、全ての計算を升目内の座標空間に移しているため、この打ち消し合いは発生すらしません。またこのトリックを使うと、世界を丸ごとボロノイでシェーディングするような場合も扱うことができます。入力を倍精度浮動小数点数に置き換えて floor() と fract() の計算を行いながら、他の箇所では単精度浮動小数点数を使うことで、全ての実装を倍精度に変えるコストを避けることができます。もちろん同じトリックはパーリンノイズにも使うことができます(私は実装もドキュメンテーションも見たことがありませんが)。」 +>この問題はコード全体でより精度の高いデータ型を用いるようにするか、もしくは少し頭を使うことで解決できます。私の実装では点の位置をドメイン空間ではなく、升目の中の空間で生成します。シェーディングする座標の整数部分と少数部分を抜き出して、どの升目を扱っているかを特定できれば、その升目の周囲で起きることだけに気をつければ良いのです。つまり座標の整数部分をごっそり落として、その分のビットを精度を上げるために使うことができるのです。実際問題、標準的なボロノイの実装では、シェーディングする座標とランダムな点の座標の差を求める際に整数部分の値が打ち消し合うことになります。上の実装では、全ての計算を升目内の座標空間に移しているため、この打ち消し合いは発生すらしません。またこのトリックを使うと、世界を丸ごとボロノイでシェーディングするような場合も扱うことができます。入力を倍精度浮動小数点数に置き換えて ```floor()``` と ```fract()``` の計算を行いながら、他の箇所では単精度浮動小数点数を使うことで、全ての実装を倍精度に変えるコストを避けることができます。もちろん同じトリックはパーリンノイズにも使うことができます(私は実装もドキュメンテーションも見たことがありませんが)。」 要点まとめ:空間をタイルに分割します。それぞれのピクセルについて、そのピクセルを含むタイルとその周囲の8つのタイルの中にある点までの距離を計算し、最も近い点までの距離を記録します。その結果、下のサンプルのようなディスタンスフィールドが得られます。 @@ -141,14 +141,14 @@ for (int y= -1; y <= 1; y++) { - このディスタンスフィールドを使って面白いパターンを作ることはできますか。 -このアルゴリズムはピクセルではなく、点からの視点で解釈することもできます。その場合は、それぞれの点が成長して、他の点からの領域にぶつかるまで膨らんでいく、といったように言い表せます。これはある種、自然界における成長の仕組みに似ています。生きている組織はこのように、内側からの広がり成長しようとする力と、外からの制約の緊張関係によって形作られます。この振る舞いをシミュレートする古典的なアルゴリズムは[Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy)の名前をとって名付けられました。 +このアルゴリズムはピクセルではなく、点からの視点で解釈することもできます。その場合は、それぞれの点が成長して、他の点からの領域にぶつかるまで膨らんでいく、といったように言い表せます。これはある種、自然界における成長の仕組みに似ています。生きている組織はこのように、内側から広がり成長しようとする力と、外からの制約の緊張関係によって形作られます。この振る舞いをシミュレートする古典的なアルゴリズムは[Georgy Voronoi](https://en.wikipedia.org/wiki/Georgy_Voronoy)の名前をとって名付けられました。 ![](monokot_root.jpg) ### ボロノイアルゴリズム -ボロノイ図をセルラーノイズから作図するのは見た目よりも簡単です。現在のピクセルにどの点がもっとも近いかという追加の情報を記録すれば良いのです。そのために ```m_point``` という名前の ```vec2``` を使います。距離だけではなく、その点固有の識別子として座標を記録します。 +ボロノイ図をセルラーノイズから作図するのは見た目よりも簡単です。現在のピクセルにどの点が最も近いかという追加の情報を記録すれば良いのです。そのために ```m_point``` という名前の ```vec2``` を使います。距離だけではなく、その点固有の識別子として座標を記録します。 ```glsl ... @@ -189,9 +189,9 @@ for (int y= -1; y <= 1; y++) { -Inigoのボロノイについての実験は続きます。2014年には彼が[voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm)と呼ぶ、標準的なノイズとボロノイを徐々にブレンドできる関数についての記事を書きました。彼は言います。 +Inigoのボロノイについての実験は続きます。2014年には彼が[voro-noise](http://www.iquilezles.org/www/articles/voronoise/voronoise.htm)と呼ぶ、標準的なノイズとボロノイを徐々にブレンドできる関数についての記事を書きました。 -「似てはいるものの、グリッドが両方のパターンで使われる方法は異なります。ノイズはランダムな値を(バリューノイズのように)補完や平均、もしくは(グラデーションノイズのように)平滑にしますが、ボロノイは最も近い特徴点への距離を計算します。スムースなバイリニア補完と最小値の評価は全く違う処理です。いや、本当にそうでしょうか。この2つをもっと一般化された形で組み合わせることはできないのでしょうか。もしできるならノイズとボロノイ図は、グリッドに基づくより一般的なパターン生成方法の特殊な例とみなせるのではないでしょうか。」 +>似てはいるものの、グリッドが両方のパターンで使われる方法は異なります。ノイズはランダムな値を(バリューノイズのように)補完や平均、もしくは(グラデーションノイズのように)平滑にしますが、ボロノイは最も近い特徴点への距離を計算します。スムースなバイリニア補完と最小値の評価は全く違う処理です。いや、本当にそうでしょうか。この2つをもっと一般化された形で組み合わせることはできないのでしょうか。もしできるならノイズとボロノイ図は、グリッドに基づくより一般的なパターン生成方法の特殊な例とみなせるのではないでしょうか。 diff --git a/13/README-jp.md b/13/README-jp.md new file mode 100644 index 0000000..235d917 --- /dev/null +++ b/13/README-jp.md @@ -0,0 +1,123 @@ +![Due East over Shadequarter Mountain - Matthew Rangel (2005) ](rangel.jpg) + +# 非整数ブラウン運動 + +ノイズという言葉は人によって違う物事を意味します。ミュージシャンであれば雑音のことを、コミュニケーションを仕事にする人であれば余計な干渉のことを、天体物理学者であれば宇宙マイクロ波背景放射のことを考えるでしょう。これらの概念は、世の中のランダムな現象の背後にある物理的な原因について考えさせてくれます。でも、まずはもっと根本的でシンプルなことから始めましょう。波とその特性についてです。波とは時間の上で起こる、ある特性を持った振動です。音波とは空気圧における振動、電磁波は電界、磁界における振動です。 + +波の2つ重要な性質に振幅(amplitude)と周波数(frequency)があります。単純な一次元の波を表す式はこのようになります。 + +
+ +* 波の性質について理解を深めるため、振幅と周波数の値を変えてみましょう。 + +* シェイピング関数を使って振幅を時間とともに変化させてみましょう。 + +* シェイピング関数を使って周波数を時間とともに変化させてみましょう。 + +2番目と3番目の演習をすることであなたはサイン波を「モデュレート」させて、AM波(amplitude modulated)とFM波(frequency modulated)を作り出したことになります。 + +波の興味深い特性の1つに、重ね合わせができるということがあります。下記のコードをコメントアウトしたり数値を変えたりしてみましょう(訳注: ```y +=``` で始まる行で試してください)。 + +
+ +* 重ね合わされている波の振幅や周波数を変えて実験してみましょう。 + +* 2つの波を互いに打ち消し合わせることはできますか。どのように見えるでしょう。 + +* 2つの波を、お互いを強め合うように重ね合わせることはできますか。 + +音楽では、それぞれの音程は特定の周波数に対応しています。これら音程の周波数は音階と呼ばれるパターンに従って並べられ、1オクターブごとに2倍または半分になります。 + +さて、サイン波の代わりにパーリンノイズを使ってみましょう。パーリンノイズの基本的な形状はサイン波(訳注:サイン波を重ね合わせたもの)に似た見た目をしています。振幅と周波数はいくらか変化しますが、振幅は比較的安定しており、周波数の変化はある値を中央値とした狭い範囲に制限されています。一方でパーリンノイズはサイン波ほど規則正しくはありません。幾つかのスケールを変えたノイズを重ね合わせればより不規則な見た目を作り出すのも簡単です。サイン波を重ね合わせたものをランダムに見せることも可能ですが、周期的で規則正しい性質を隠すにはたくさんの異なる波を重ねる必要があります。 + +周波数を一定の割合で増加させる(lacunarity)と同時に振幅を減らしながら(gain)ノイズを(octaveの数だけ)繰り返し重ねることで、より粒度の細かいディテールを持ったノイズを作り出すことができます。このテクニックは「非整数ブラウン運動(fBM)」または単に「フラクタルノイズ」と呼ばれていて、最も単純な形は下記のコードのようになります。 + +
+ +* 1から2、4、8、10と ```octave``` の値を徐々に変化させて何が起きるか観察してみましょう。 + +* ```octave``` の値が4より大きい状態で ```lacunarity``` の値を変化させてみましょう。 + +* 同じく ```octave``` の値が4より大きい状態で ```gain``` の値を変化させてみましょう。 + +```octave``` を増やすと曲線のディテールがより細かくなることに注目してください。また、その際の自己相似性にも注意しましょう。曲線を拡大しても、細かな部分の形が全体の形と同じになっているように見えます。また、拡大した部分は多かれ少なかれ他の部分と似ています。これは数学的なフラクタルの重要な性質で、私たちはこの性質をループの中でシミュレートしているのです。ここではシミュレーションは数回の繰り返しの後で打ち切られているので本当の意味でのフラクタルを作り出しているわけではありません。しかし理論上では、ループを永遠に繰り返して無限にノイズを重ね合わせれば、本当に数学的なフラクタルを得ることができるでしょう。コンピュータグラフィクスでは常に処理できる細かさに限界があります。例えばピクセルより小さなものは見えないので、フラクタルな見た目を作り出すのに無限回の足し算を行う必要はありません。たくさんの繰り返しが必要になることはあるかもしれませんが、無限回になることは決してありません。 + +下記のコードは2次元のfBMを実装してフラクタル状の模様を作り出した例です。 + +
+ +* 37行目の ```octave``` を減らしてみましょう。 + +* 47行目の ```lacunarity``` を変化させてみましょう。 + +* 48行目の ```gain``` を変えて試してみましょう。 + +このテクニックはプログラムで地形を作り出すのによく用いられています。fBMの自己相似性は、山を形づくる侵食過程の自己相似性に似ているため、山脈の形を再現するのに最適です。もし興味があればInigo Quilesによる[高度なノイズ](http://www.iquilezles.org/www/articles/morenoise/morenoise.htm)についての素晴らしい記事を読むと良いでしょう。 + +![Blackout - Dan Holdsworth (2010)](holdsworth.jpg) + +同様のテクニックを使って「乱流(turbulence)」と呼ばれる効果を作り出すこともできます。基本的にはfBMなのですが、鋭い谷間を作り出すために符号付きノイズ(signed noise)の絶対値を用いています。 + +```glsl +for (int i = 0; i < OCTAVES; i++) { + value += amplitude * abs(snoise(st)); + st *= 2.; + amplitude *= .5; +} +``` + + + +同じ仲間のアルゴリズムに「尾根(ridge)」があります。深い谷が上下逆さまにされて鋭い尾根を作り出しています。 + +```glsl + n = abs(n); // create creases + n = offset - n; // invert so creases are at top + n = n * n; // sharpen creases +``` + + + +他の便利な仲間には、ノイズを足し算する代わりに掛け合わせたものがあります。ノイズ関数をそれまでのループの結果に応じてスケールさせても面白いでしょう。こうすると厳密なフラクタルから離れて、より謎めいた「マルチフラクタル」の世界に足を踏み入れることになります。マルチフラクタルはフラクタルほどは数学的に厳密に定義されていませんが、だからと言って役に立たないというわけではありません(訳注:厳密に定義されてないというより、より一般化された概念なのでもっといろんなものを含んじゃうよ、という感じだと思います)。実際にマルチフラクタルシミュレーションは地形生成に用いる現代の商用ソフトで一般的に用いられています。より深く知りたければKenton Musgraveによる「[Texturing and Modeling: a Procedural Approach (3rd edition)](https://www.amazon.co.jp/Texturing-Modeling-Third-Procedural-Approach/dp/1558608486)」を読むと良いでしょう。残念なことにこの本は数年前に絶版になってしまいましたが、図書館や中古で見つけることができるでしょう(第1版のPDF版はオンラインで手に入りますが買わないように。お金の無駄です。1994年に出版されたもので、第3版に載っている地形生成についての部分は含まれていません)。 + +### ドメインワーピング + +Inigo Quilesはこの[興味をそそる記事](http://www.iquilezles.org/www/articles/warp/warp.htm)でfBMを使ってfBMの空間を歪める方法について書いています。ぶっ飛んでますね。インセプションに出てくる夢の中の夢のようです。 + +![ f(p) = fbm( p + fbm( p + fbm( p ) ) ) - Inigo Quiles (2002)](quiles.jpg) + +下記はこのテクニックのもう少しおとなしいサンプルです。空間の歪みが雲のようなテクスチャを作り出すのに使われています。ここでもまた自己相似性が見られることに注目してください。 + +
+ +このようにテクスチャ座標を歪める技はとても便利で、楽しく、しかし非道なほどマスターするのが難しいものです。とても強力なツールですが使いこなすにはかなりの経験が必要です。便利なテクニックとしてノイズの微分(勾配)を用いて座標をずらす方法があります。Ken PerlinとFabrice Neyretによる「[flow noise](http://evasion.imag.fr/Publications/2001/PN01/) 」という記事はこのアイデアに基づくものです。最近のパーリンノイズの実装の幾つかは、その関数の値と解析的な勾配の両方を計算してくれます。もしある関数について本物の勾配が得られない場合でも常に、手間がかかり正確さに劣りますが、差分法を用いて近似することができます。 + +(訳注:要するにノイズの値そのものではなくその微分を使うという話です。微分が直接計算できない場合は微妙にずらした2点の値を計算して差分をとることで近似することができます。この辺りはもう少し説明が必要ですね。参考になるかわかりませんがこの[サンプル](https://thebookofshaders.com/edit.php?log=161119153749)では ```normal``` 関数の中で少し座標をずらしてノイズの値を計算して比較することで傾きを計算しています。)