ジュリア集合は、フラクタルの一つで、フランスの数学者ジュリアによって、1918年に発表された集合です。
フラクタルは、いたるところで微分不可能で、自己相似な曲線です
(ハウスドルフ次元や位相次元から、集合がフラクタルかどうか検証されますが、ここでは、そんな難しいことはパスします)。
まぁ、ピクセルシェーダのテクスチャアドレッシング命令が使いたかっただけです。
まぁ、いつものように適当にファイルが入っています。
vs.vsh | 頂点シェーダー。 |
ps.psh | ピクセルシェーダー。 |
draw.cpp | メインの描画部分。 |
draw.h | 描画の各関数の定義。 |
main.h | 基本的な定数など。今回も出番無し。 |
main.cpp | 描画に関係しないシステム的な部分。変更が無いので、出番無し。 |
load.cpp | ロード。 |
load.h | ロードのインターフェイス。 |
あと、実行ファイル及び、プロジェクトファイルが入っています。
ジュリア集合をもう少し詳しく解説すると、適当な複素数 A を固定して、複素平面の一点Z0から
Zn+1 = Zn * Zn + A
の計算をはじめた時に、Z∞が発散する部分と発散しない部分の境界のことです。
複素平面は2次元の実平面と同じなので、テクスチャーの中心を原点としたテクスチャー座標でジュリア集合を表現します。
実2次元平面の変換の式は、
Zn+1 = Zn * Zn + A = (Xn + iYn) * (Xn + iYn) + (Ar + iAi) = Xn * Xn - Yn * Yn + Ar + i(2 Xn * Yn + Ai)
から、
Xn+1 = Xn * Xn - Yn * Yn + Ar Yn+1 = 2 Xn * Yn + Ai
に、なります。
座標の変換はピクセル単位に行います。
そのために、今まで使ってこなかったピクセルシェーダのテクスチャアドレッシング命令を使います。
いままで、テクスチャーの読み込みに
tex t0
を使ってきました。
この命令は、実際には、「t0 に設定されているoT0のテクスチャー座標を使って、
各ステージにセットされているテクスチャから色を引っ張り、t0 にその色を入れる」 作業をします。
tex 命令以外にもテクスチャアドレッシング命令は存在します。
今回使うのは、texreg2gb です。
texreg2gb t1, t0
あらかじめt0に(tex命令などを使って)テクスチャーをロードします。
texreg2gb は、t0の色ベクトルの緑成分と青成分で指定される 2次元ベクトルをテクスチャー座標として、
次のステージのテクスチャを読み込みます。
つまり、(x0,y0)から、(x1,y1)へ変換するテーブルをテクスチャーの緑と青色の成分に入れておいて、
テクスチャー座標を色として変換してから読みたい(前のフレームの)テクスチャー画像を読み込めば、
非線形なテクスチャーのロードが実現します。
この変換は、テクスチャーに保存します。
テクスチャーの緑色(y)にXnの変換、青色(z)にYnの変換を代入します。
算術的に変換のテクスチャーを作成すると、次のようになります。
draw.cpp 0134: //----------------------------------------------------------------------------- 0135: // Name: MakeTexture() 0136: // Desc: 座標変換マップ 0137: //----------------------------------------------------------------------------- 0138: VOID MakeTexture(D3DXVECTOR4* pOut, D3DXVECTOR2 *pTexCoord, D3DXVECTOR2 *pTexelSize, LPVOID pData) 0139: { 0140: float x = 2*pTexCoord->x-1; 0141: float y = 2*pTexCoord->y-1; 0142: float Ar = 0.378f; 0143: float Ai = 0.183f; 0144: 0145: float x1 = x*x-y*y+Ar; 0146: float y1 = 2*x*y+Ai; 0147: 0148: min(1,max(x1,0)); 0149: min(1,max(y1,0)); 0150: 0151: pOut->x = 0.0f; 0152: pOut->y = 0.5f*x1+0.5f; 0153: pOut->z = 0.5f*y1+0.5; 0154: pOut->w = 1.0f; 0155: }
定数Aは、A = 0.378 + 0.183 iを採用しました。
この関数で作成したテクスチャーは、
になります(このテクスチャーはタイトルの初期値としても使っています)。
このテクスチャーを使って変換することにより次の画像を作成し、
さらに次のテクスチャーは前のフレームの画像を変換して作ります。
この作業を続けると、ジュリア集合の境界を境に色が異なるので、フラクタルの模様が浮かび上がります。
上の図では最初の画像を変化させて、変換したものです。
本当は、はみ出た部分からも色を引っ張る必要があるのですが、それはできないので、
その分が誤差となって、完全に綺麗な模様ではなくなっています。
実際のピクセルシェーダープログラムは次のようになります。
ps.psh 0001: ps.1.1 0002: 0003: ; テクスチャーの色を引っ張ってくる 0004: tex t0 0005: texreg2gb t1, t0 ; t0 の(緑,青)をテクスチャー座標とみなして 0006: ; t1のテクスチャーから色を引っ張ってくる 0007: mov r0, t1
1つめのテクスチャーに変換のためのテクスチャーを入れて、
一行目で UV値から次のテクスチャーを参照するベクトル(色の緑、青成分)を読み取ります。
次に texreg2gb 命令で、t0 に入ったテクスチャー座標を使って前のフレームの画像のテクスチャーのピクセルをt1に読み込みます。
あとは、拾ったテクスチャーの色を出力にそのまま出力します。
これ以外のソースの部分は、レンダリングターゲットになるテクスチャーを毎フレーム切り替えて、 一つ前のフレームの結果を変換元のテクスチャーに使用しています。
非自明なテクスチャアドレッシング命令を作りたかっただけです。
作ってから言うのもなんですが、動的フラクタルは使いどころがあるんでしょうか?