飴屋

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

Flash3D