ジュリア集合


~なんとなくフラクタル~




■はじめに

ジュリア集合は、フラクタルの一つで、フランスの数学者ジュリアによって、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に読み込みます。
あとは、拾ったテクスチャーの色を出力にそのまま出力します。

これ以外のソースの部分は、レンダリングターゲットになるテクスチャーを毎フレーム切り替えて、 一つ前のフレームの結果を変換元のテクスチャーに使用しています。

■最後に

非自明なテクスチャアドレッシング命令を作りたかっただけです。
作ってから言うのもなんですが、動的フラクタルは使いどころがあるんでしょうか?




もどる

imagire@gmail.com