もう 2 ヶ月経ってしまいましたが、Pixel Bender 3D のプレビュー 3 が Adobe Labs に公開されています。 (Adobe Pixel Bender 3D Preview@Labs)
Pixel Bender 3D は Pixel Bender を Stage3D 用に拡張したもので、Stage3D のシェーダの記述に使えます。AGAL は機械には分かり易い言語でも、人間が読んだり書いたりするのは困難です。Pixel Bender 3D を使えば、シェーダの開発も効率的になりそうです。
ところが、プレビュー 3 発表時のブログによると、現在のプレビュー 3 では、
- コマンドラインでの操作が必要
- Stage3D の制限を簡単に超えてしまう
などの問題があるものの、これ以上の Pixel Bender 3D の開発は、数ヶ月以内に、コミュニティからの強い要望が集まらない限り行われないだろうと書かれています。
現在の状況を鑑みて、プレビュー 3 以降が出てくることは期待薄にも思えますが、現バージョンでも十分に役には立つので、気を取り直して、そして開発の継続を期待して、Pixel Bender 3D のご紹介です。
2 種類の Pixel Bender カーネル
Pixel Bender のプログラムは "カーネル" と呼ばれます。カーネルは GLSL のようなシェーダ言語に近い言語で記述されます。
Pixel Bender 3D のカーネルは、最終的に、頂点シェーダと断片シェーダの AGAL バイトコードに変換されます。従って、シェーダの記述とコンパイル処理以外は、AGAL を利用した場合と同様に考えて大丈夫そうです。実際には、いくつか便利機能が提供されるため、Pixel Bender 3D の方がもうちょっと楽ができます。
さて、Pixel Bender 3D では、2 種類のカーネルを開発することになります。 (以下、正式な日本語訳が無いので名前は適当です)
- 頂点カーネル:
頂点の位置情報を操作する。必要に応じて、後の処理のための情報も計算
- マテリアルカーネル:
頂点および断片の見た目を操作する
頂点カーネルファイルの拡張子は .pbvk、マテリアルカーネルのファイルの拡張子は .pbmk とするのが一般的なようです。
3 種類の PB-ASM 中間プログラム
シェーダは、扱う対象が "頂点か断片か" で分けられていました。これに対して、Pixel Bender 3D カーネルは、扱う対象が "位置か見た目か" で分けられています。
数は同じ 2 つでも、これでは、カーネルをそのままコンパイルしてシェーダに、という訳には行きません。そのため、一旦中間プログラムを生成して、それをシェーダに変換する、という手順をとります。
生成される中間プログラムは PB-ASM と呼ばれる言語で記述されています。そのため、PB-ASM プログラムとも呼ばれます。
2 種のカーネルから生成される PB-ASM プログラムは、以下の 3 種類です。マテリアルカーネルは 2 つの PB-ASM プログラムに分かれます。
- 頂点カーネルから生成
- 頂点位置プログラム:頂点の位置を操作
- マテリアルカーネルから生成
- 頂点マテリアルプログラム:頂点の見た目を操作
- 断片マテリアルプログラム:断片の見た目を操作
カーネルから PB-ASM プログラムを生成するには pb3dutil というプログラムを使用します。このプログラムは Pixel Bender 3D のパッケージに含まれています。
以下は実際に変換を行う例です。pb3dutil をコマンドラインで実行します。
$ pb3dutil vertexKernel.pbvk vpProgram.pbasm $ pb3dutil materialKernel.pbmk vmProgram.pbasm fmProgram.pbasm
最初の引数が入力ファイル名です。2 つ目以降の引数は出力される PB-ASM ファイル名です。PB-ASM ファイルの拡張子は .pbasm を使うのが一般的です。
マテリアルカーネルからの出力は、頂点マテリアルプログラム、断片マテリアルプログラムの順に指定します。
シェーダへの変換
PB-ASM プログラムを生成したら、残るは PB-ASM プログラムからシェーダへの変換です。
3 つの PB-ASM プログラムの内、頂点位置プログラムと頂点マテリアルプログラムの 2 つから頂点シェーダが生成されます。断片マテリアルプログラムからは、断片シェーダが生成されます。
- 頂点シェーダ = 頂点位置プログラム + 頂点マテリアルプログラム
- 断片シェーダ = 断片マテリアルプログラム
PB-ASM プログラムからシェーダへの変換は、Pixel Bender 3D が提供するクラスを使い、ActionScript のプログラムとして実装します。一連の処理の記述に必要なクラスは pb3dlib.swc に定義されています。パスに pb3dlib.swc を追加したら次に進みましょう。
まずは、生成された 3 種類の PB-ASM プログラムを、SWF に埋め込みます。その際、以下のようにバイトデータとして埋め込みます。
[Embed(source="vpProgram.pbasm",mimeType="application/octet-stream")] protected static const VertexPositionProgram:Class; [Embed(source="vmProgram.pbasm",mimeType="application/octet-stream")] protected static const VertexMaterialProgram:Class; [Embed(source="fmProgram.pbasm",mimeType="application/octet-stream")] protected static const FragmentMaterialProgram:Class;
次に、これらのバイトデータを PBASMProgram のインスタンスに変換します。
以下の処理を PB-ASM プログラム全てに対して行って、3 つの PBASMProgram インスタンスを生成します
import com.adobe.pixelBender3D.PBASMProgram; var bytes:ByteArray = new VertexPositionProgram(); var vpProgram:PBASMProgram = new PBASMProgram(bytes.readUTFBytes(bytes.bytesAvailable));
最後に、PBASMCompiler クラスを使って PBASMProgram からシェーダを生成します。
PBASMCompiler.compile() メソッドに、頂点位置プログラム、頂点マテリアルプログラム、断片マテリアルプログラムの順で引数を指定すると、頂点シェーダと断片シェーダのペアが生成されます。生成されるオブジェクトの型は AGALProgramPair です。
import com.adobe.pixelBender3D.AGALProgramPair;
import com.adobe.pixelBender3D.PBASMCompiler; var translatedPrograms:AGALProgramPair = PBASMCompiler.compile(vpProgram, vmProgram, fmProgram);
後は、AGAL 利用時と同様に、Stage3D の API で生成されたシェーダを GPU にアップロードするだけです。頂点シェーダと断片シェーダのバイトコードは以下のように指定します。
program3D = context3D.createProgram(); program3D.upload(translatedPrograms.vertexProgram.byteCode, translatedPrograms.fragmentProgram.byteCode);
頂点属性の指定
Stage3D では、頂点バッファ内の数値の並びを、頂点属性に割り当てるという作業が必要です。その際、シェーダ内に記述されたレジスタ番号と、ActionScript で指定する属性の番号を合わせる必要があったりして、なかなか面倒です。
AGAL の場合はこんな感じで、属性を 1 つずつ指定しました。
// 属性レジスタ0番に、頂点バッファverticesの0番目からfloat型4つを属性として設定 context3D.setVertexBufferAt(0,vertices,0,Context3DVertexBufferFormat.FLOAT_4);
Pixel Bender 3D の場合は、カーネルに書かれた情報を元に、属性とレジスタの対応を Pixel Bender 3D が管理します。また、その情報を Context3D に設定するクラスも提供されます。
そのため、上のようなレジスタ番号やバッファ内の構造を指定するコードを書く必要がありません。間違え易く、間違えが見つけにくいコーディングを省略できるのが嬉しいところです。
頂点シェーダ、断片シェーダ、それぞれのレジスタと頂点バッファの関連情報は AGALProgramPair のインスタンスから RegisterMap として取得できます。以下はそのサンプルです。
import com.adobe.pixelBender3D.RegisterMap; var vertexRegisterMap:RegisterMap = translatedPrograms.vertexProgram.registers;
var fragmentRegisterMap:RegisterMap= translatedPrograms.fragmentProgram.registers;
このようにして取得した RegisterMap を使って VertexBufferHelper のインスタンスを初期化します。そして、VertexBufferHelper.setVertexBuffers() メソッドを呼ぶと、頂点属性の利用に必要な情報が Context3D に設定されます。
import com.adobe.pixelBender3D.utils.VertexBufferHelper; var vertexBufferHelper:VertexBufferHelper = new VertexBufferHelper( context3D, vertexRegisterMap.inputVertexRegisters, vertices); vertexBufferHelper.setVertexBuffers();
以上が主要な Pixel Bender 3D 固有のクラスの使い方です。後は、頂点バッファとインデックスバッファをアップロードすれば、シェーダを実行できます。
Pixel Bender 3D カーネルの書き方は次回に続きます。
1つのcontext3Dで複数のシェーダを使い分けることはできないのでしょうか?
dh さん、こんにちは。
可能です。
ただ、パフォーマンスへの影響は出ると思うので、その点は検証してから使うのが良いと思います。
回答ありがとうございます。
試行錯誤した結果レジスタの使用する数が1番多いシェーダに値の数をそろえることで複数のシェーダを使い分けることができました。
パフォーマンスへの影響はテクスチャの切り替えほど大きくない感じでした。
dh さん、こんにちは。
ご連絡ありがとうございました。
レジスタ数の件については認識がありませんでしたので調べてみます。
何か情報が見つかったら共有します。