Molehill Stage3D APIでシェーダとして使用するAGAL言語について。
前回に引き続き、iFlash3D "My Name is AGAL, and I Come from Adobe – Part2"の拙訳を。
---
Part1ではAGALのメインコンセプトについて話した。
AGALはとりあえず、資料の少ない、しかし強力な"ちょっと"わけのわからない言語になっただろう。
Flashの歴史上初めて、我々は機械語のフルパワーを発揮できるようになった。これは「Flashだろ。遅いじゃん」とかいったものではない。AGALはGPUと同じ速度で実行できるのだ。
問題はAGALがアセンブリ言語であることだ。ゆえに我々が今まで書いてきた、慣れ親しんだ素晴らしいActionScriptとは違ってとても難しく、直感的でない。
初見ではAGALはヒエログリフの一種かと思うだろう。Part1を読んで少しでも理解を深めてもらえることを願う。これを読んだならオペコードとレジスタについて理解し、多少は親しめていることだろう。
しかしちょっと地球外文明、異なった文化の言語に触れることを考えてみてほしい。彼らとコミュニケートするには何が足りないだろうか。
そう。交信する窓口、スターゲイトだ。
我々の言語はActionScript。彼らの言語はAGAL。そしてスターゲイトとはMoleHill APIだ。
銀河間コーディングへようこそ!
Molehillの全てのレンダリングはシェーダが重要だ。シェーダなしではレンダリングすることはできない。 Molehill APIのセットアップはシェーダが実行できるようにするために行われる。AGALとシェーダがMoleHillにおいてどれだけ重要かということだ。
Part1の最もシンプルなシェーダはこうだった。
//vertex shader m44 op, va0, vc0 // pos to clipspace mov v0, va1 // copy uv //pixel shader tex ft1, v0, fs0 <2d,linear,nomip> mov oc, ft1
これをActionScriptから実行したい。
まず最初は、頂点を通してジオメトリを定義しなくてはならない。
4つの頂点で作られた四角形をディスプレイに出力したいとしよう。
var vertices:Vector.<Number> = Vector.<Number>([ -0.5,-0.5,0, 0, 0, // x, y, z, u, v -0.5, 0.5, 0, 0, 1, 0.5, 0.5, 0, 1, 1, 0.5, -0.5, 0, 1, 0]);
各頂点はx,y,z座標とu,vテクスチャ座標からなる。
この頂点はVertexBuffer3Dに保持される。
// 4 vertices, of 5 Numbers each var vertexbuffer:VertexBuffer3D = context3D.createVertexBuffer(4, 5);
これはcontext3Dを通してGPUにアップロードされる。
vertexbuffer.uploadFromVector(vertices, 0, 4);
次に三角形を定義する必要がある。これはindex bufferで行う。
// total of 6 indices. 2 triangles by 3 vertices each var indexbuffer:IndexBuffer3D = context3D.createIndexBuffer(6);
このindex bufferでは2つの三角形を定義する。1つめは頂点0,1,2から、2つめは頂点2,3,0からなる。
vertex bufferと同じようにindex bufferも使う前にGPUにアップロードしなくてはならない。
// offset 0, count 6 indexbuffer.uploadFromVector (Vector.<uint>([0, 1, 2, 2, 3, 0]), 0, 6);
テクスチャも必要だ。これはswfに埋め込んだjpg(もしくは外部から読み込んだ画像でも良い)を使い、GPUに送る。
var bitmap:Bitmap = new TextureBitmap(); var texture:Texture = context3D.createTexture(bitmap.bitmapData.width, bitmap.bitmapData.height, Context3DTextureFormat.BGRA, false); texture.uploadFromBitmapData(bitmap.bitmapData);
最後にAGALシェーダプログラムだ。まずAGALMiniAssemblerでコンパイルする必要がある。
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler(); vertexShaderAssembler.assemble( Context3DProgramType.VERTEX, "m44 op, va0, vc0\n" + // pos to clipspace "mov v0, va1" // copy uv ); var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler(); fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT, "tex ft1, v0, fs0 <2d,linear, nomip>;\n" + "mov oc, ft1" );
そして頂点、ピクセルシェーダプログラムをGPUにアップロードする。
var program:Program3D = context3D.createProgram(); program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
では全てアップロードされ、準備ができたのでついにレンダリングすることができる。
まず、viewportを好きな背景色でクリアする
context3D.clear ( 1, 1, 1, 1 );
そして、この描画に使うvertex bufferとindex bufferとテクスチャとシェーダプログラムを指定する。
// vertex position to attribute register 0 context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3); // uv coordinates to attribute register 1 context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_2); // assign texture to texture sampler 0 context3D.setTextureAt( 0, texture ); // assign shader program context3D.setProgram( program );
頂点座標とuv座標、2つのvertex buffer streamをどのように2つのattributeレジスタに割り当てるのかに注意してほしい。テクスチャは特定のtexture samplerに割り当てられる。
次に変換行列をシェーダに渡さなくてはならない。ここではすごい3D投影はせず、普通の変換行列にしよう。
var m:Matrix3D = new Matrix3D(); m.appendRotation(getTimer()/50, Vector3D.Z_AXIS); context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
ここでは行列は頂点シェーダで使えるようcontantレジスタ0にいれられる。
レンダリングするにはindex bufferを渡してdrawTrianglesを呼ぶだけだ。
context3D.drawTriangles( indexBuffer);
フレーム内で他のジオメトリを描画するにはdrawTrianglesを複数回呼べばいい。そして最後にフレームに実際にレンダリングするためにpresent()を呼ぶ。
context3D.present();
---
実行した結果とソースコードは元記事にリンクがある。テクスチャ貼られた四角形が回転するものだ。
1つのジオメトリに対して1つのvertex bufferとindex bufferを用意してdrawTriangles()するということなのかな。
テクスチャレジスタfsが8つまでなのもジオメトリを複数にすれば解決できるもんね。
ジオメトリの数だけdrawTriangles()読んだら大変なことにならんのかな。
Author:9ballsyn
ActionScriptについて
最近はMolehill