Flash3D/AGALでシェーダー
AGALでシェーダー
プログラマブルシェーダーの項でAGALMiniAssemblerというクラスを使ってシェーダーを作成する短いコードのサンプルを載せましたが、あの頃はまだAGALMiniAssemblerがSDKに標準で含まれているものと思ってました。あれはcom.adobe.utilsパッケージで配布されている便利クラスだったのですね。assembleメソッドに渡す文字列がAGALと呼ばれる言語で、それをバイトコードに翻訳してくれるのがAGALMiniAssemblerクラスの目的のようです。なので、このクラスを使わずにバイトコードに予め翻訳したものをProgram3D.uploadメソッドに渡すこともできるようです。バイトコードに翻訳する処理が繰り返し行われるような実装にすることはそんなにないと思うので、実行時に翻訳してもさして問題にならないと思います。何よりAGALをActionScriptの中で編集できるのは保守作業を少し楽にしてくれそうです。逆にPixelBender3Dでシェーダープログラムを書くと、何をするにも一段階作業が増えそうです。AGALの仕様を読んだ感じではシンプルな内容だったので、AGALでシェーダーを書くのでもよさそうに思いました。
なんか、AGALって言葉を適当に使って語弊が生じるような気がしてきたので、補足を入れるとソースに書くテキストの状態もAGALだし、バイトコードに変換された状態もAGALであると理解しています。両者を峻別する単語ってあるんでしょうかね。
AGALは現在シェーダーを想定した仕様にとどまっていますが、もうちょっと汎用的な用途に使えるようになったらいいんじゃないかなと夢をみています。将来的に物理演算部分もAGALで書けるようになるかもしれないし、AGALのことをもうちょっと詳しく調べてみようと思います。
OpCode
AGALではGPUを操作するためにOpCodeという命令が定義されています。命令のあとには出力を一つ、入力を一つか二つ持っています。この命令を組み合わせて必要な数値(座標位置とか表示色)を計算させるのですが、入力が二つまでに限定されているため、例えば、1+2+4を計算する場合は、1+2=3と3+4=7という二つの足し算の命令に分けてあげる必要があります。計算の順序を考えて、パズルのようにOpCodeを組み合わせれば複雑な計算も可能になります。
- mov
- 入力1を出力1にコピー
- add
- 入力1と入力2の和を出力1に設定
- sub
- 入力1と入力2の差を出力1に設定
- mul
- 入力1と入力2の積を出力1に設定
- div
- 入力1と入力2の商を出力1に設定
- rcp
- 入力1の逆数を出力1に設定
- min
- 入力1と出力1の小さい方を出力1に設定
- max
- 入力1と出力1の大きい方を出力1に設定
- frc
- 入力1の小数点以下の数値を出力1に設定
- sqt
- 入力1の平方根を出力1に設定
- rsq
- 入力1の平方根の逆数を出力1に設定
- pow
- 入力1の累乗(入力1を[入力2]個分乗算)を出力1に設定
- log
- 2を底とする入力1の対数を出力1に設定
- exp
- 2の累乗(2を[入力1]個分乗算)を出力1に設定
- nrm
- 入力1を正規化して出力1に設定
- sin
- 入力1の正弦を出力1に設定
- cos
- 入力1の余弦を出力1に設定
- crs
- 入力1と入力2の外積を出力1に設定
- dp3
- 入力1と入力2の内積(3x3)を出力1に設定
- dp4
- 入力1と入力2の内積(4x4)を出力1に設定
- abs
- 入力1の絶対値を出力1に設定
- neg
- 入力1の反数を出力1に設定
- sat
- 入力1を0-1の範囲に収めて出力1に設定
- m33
- 入力1と入力2の3x3の行列の積を出力1に設定
- m44
- 入力1と入力2の4x4の行列の積を出力1に設定
- m34
- 入力1と入力2の3x4の行列の積を出力1に設定
- kil
- 入力に0より小さい数値が含まれていたら描画をしない(フラグメントシェーダーのみ)
- tex
- 入力2のテクスチャ中の、入力1の座標位置からカラーサンプルをとって出力1に設定(フラグメントシェーダーのみ)
- sge
- 入力1が入力2以上であれば出力1に1を設定、そうでなければ0を設定
- slt
- 入力1が入力2より小さければ出力1に1を設定、そうでなければ0を設定
- seq
- 入力1と入力2が等しければ出力1に1を設定、そうでなければ0を設定
- sne
- 入力1と入力2が等しくなければ出力1に1を設定、そうでなければ0を設定
AGALMiniAssemblerの中身をみるとその他に"ifz", "inz", "ife", "ine", "ifg", "ifl", "ieg", "iel", "els", "eif", "rep", "erp", "brk", "sgn"といったOpCodeが予約されているようですが、まだ実用化されていないようです。なんとなく条件分岐に使うものが多そうですが、古いGPUで使えないとかそんな事情でもあるんでしょうか。
レジスター
OpCodeの入出力値に指定するのはレジスターと呼ばれる文字です。
- va0-va7
- Context3D.setVertexBufferAtで設定された入力値(座標、色、UV座標など)
- vc0-vc127
- Context3D.setProgramConstantsで設定された入力値(シェーダーで共通で利用する値など)
- fc0-fc27
- Context3D.setProgramConstantsで設定された入力値(シェーダーで共通で利用する値など)
- vt0-vt7
- 一時的な変数
- ft0-ft7
- 一時的な変数
- op
- バーテックスシェーダーの出力値(頂点の座標)
- oc
- フラグメントシェーダーの出力値(ピクセルの色)
- v0-v7
- バーテックスシェーダーからフラグメントシェーダーに渡される数値
- fs0-fs7
- テクスチャデータのサンプラー(サンプラーの動作を指定できる<(2d, 3d, cube)|(nomip, mipnone, mipnearest)|(nearest, linear)|(repeat, wrap, clamp)>)
Date: 2011/11/21