基本的な Pixel Bender 3D カーネルの書き方に続いて、Pixel Bender 3D でテクスチャマッピングを行う方法をご紹介します。
テクスチャマッピングは見た目に関する処理なので、関係するのはマテリアルカーネルだけです。
早速、マテリアルカーネルの定義です。まずは、変数の宣言からです。今回新しく登場しているのは input image です。
{ input vertex float4 UVCoord; interpolated float4 interpolatedUVCoord; input image4 inputImage; output float4 finalColor; }
input image の変数宣言の記述は、ちょっと input vertex とは異なります。"input image 型 変数名" ではなくて、"input 型 変数名" です。その際、型として、image1, image2, image3, image4 のどれかを指定するという制限がつきます。
本来 UV 座標は float が 2 つで表せますが、今回は float x 4 のベクターとして宣言しています。なので、頂点バッファ内の UV 座標データは、[U, V, 0, 0] の様に、2 つのダミーの数値を含む 4 つの float の組み合わせとして扱われます。
各変数の意味は、上から順番に、
- 頂点バッファから float x 4 つ分を、頂点の UV 座標として UVCoord という名前で使う
- 頂点マテリアルプログラムと断片マテリアルプログラム間の受け渡しに interpolatedUVCoord という変数を使う
- 断片マテリアルプログラムからテクスチャを参照するのに inputImage という変数を使う
- 最終的な断片の色は finalColor という名前の変数に代入する
となります。
さて、関数の宣言は以下のような感じになります。マテリアルカーネルでは、2 種類の関数を宣言するのでしたね。
{ void evaluateVertex() { interpolatedUVCoord = UVCoord; } void evaluateFragment() { float2 loc = interpolatedUVCoord.xy; finalColor = sample(inputImage, loc); } }
上の evaluateVertex()、すなわち、頂点マテリアルプログラムは、UV 座標をそのまま次の処理に渡しています。こちらは単純です。
一方、下の evaluateFragment() には、少し新しい記述が登場します。
最初の行は、まず、左辺で loc という変数を宣言しています。型は float2 つまり、float x 2 のベクターです。
右辺を見ると、補間された UV 座標を持つ interpolatedUVCoord の後に .xy が付いています。このように float4 型の変数の後に .xy を付けると、「float4 型を、最初の 2 つの要素を持つ float2 型に変換」 という指示になります。
つまり、最初の行は、ダミーの数値を取り除き、float4 → float2 の型変換を行うことが目的です。
2 行目では、sample() という関数を呼び出しています。sample() は、テクスチャから UV 座標に対応するデータのサンプリングを行う関数です。最初の引数にテクスチャ、2 つ目の引数に UV 座標をとります。
この evaluateFragment() の 2 行目は、AGAL ではこんな感じでした。
tex ft1, ft0, fs0 mov oc, ft1
こうして見ると、"一時レジスタは全部で 8 つ" の制限には、割と簡単に達してしまいそうです。
おまけ
ついでに、2 つの画像を合成する evaluateFragment() のサンプルも紹介します。
下は、inputImage0 と inputImage1 という 2 つのテクスチャの値を半分ずつ使って画像合成を行う場合です。
input image4 inputImage0; input image4 inputImage1; void evaluateFragment() { float2 loc = interpolatedUVCoord.xy; finalColor = (0.5 * sample(inputImage0, loc)) + (0.5 * sample(inputImage1, loc)); }
このコードでは合成の比率が 0.5 と固定されています。この比率を AS 側から指定できるように変更すると、イメージのクロスフェード表示も実現できそうです。
ということで、もう 1 つ変数を追加したのが、下のサンプルです。
void evaluateFragment() { parameter float weight; float a = clamp(weight, 0.0, 1.0); float b = 1.0 - a; float2 loc = interpolatedUVCoord.xy; finalColor = (a * sample(inputImage0, loc)) + (b * sample(inputImage1, loc)); }
AS 側で weight の値の指定をだんだん変えながら、このカーネルから生成されるシェーダを繰り返し呼び出すと、2 つのテクスチャの重なり方が徐々に変化させられます。
上で使われている clamp() は、値の最小値と最大値を指定するための関数です。最小値以下の数値は最小値に、最大値以上の数値は最大値に、それ以外の数値はそのままの値となります。上の例では、a の値が 0 から 1 の間にあることを保証するために使われています。
おまけ - その 2
久しぶりに話を ActionScript に戻します。
ActionScript プログラム内で Context3D にテクスチャなどの定数を設定する際に使えるよう、Pixel Bender 3D には、ProgramConstantsHelper というクラスが提供されています。その場合、定数の保管先の指定には、レジスタ番号ではなく、カーネル内の変数名を使います。
下は、ProgramConstantsHelper を使って、マテリアルカーネルの inputImage という名前の変数にテクスチャを設定するサンプルです。 (テクスチャのインスタンス生成については、Stage3D とテクスチャマッピング)
ProgramConstantsHelper のインスタンス生成には 2 種類の RegisterMap を指定します。 (RegisterMap については Pixel Bender 3D プレビュー 3 公開)
// ProgramConstantsHelperのインスタンスを生成 var programConstantsHelper = new ProgramConstantsHelper(context3D, vertexRegisterMap, fragmentRegisterMap); // テクスチャmyTextureをinputImageに設定 programConstantsHelper.setTextureByName("inputImage", myTexture);
// 定数(この場合はテクスチャ)をGPUにアップロード programConstantsHelper.update();
Stage3D 標準の API による、レジスタ番号を使った定数の設定手順 (Stage3D のシェーダのアップロードと AGAL の基本情報) よりは分かりやすいコードになっていると思います。
ちなみに、最初の 「おまけ」 で紹介した weight のように、float 型の定数に値を設定するには、setNumberParameterByName() が使えます。
programConstantsHelper.setNumberParameterByName( Context3DProgramType.FRAGMENT, "weight", 0.1);
コメントする