/**/
Jitter のディストリビューションによって多くのシェーダが提供されていますが、その多くは、より複雑な操作を行うために組み立てることが可能です。そして、Jitterによるシェーダのサポートの最もエキサイティングな一面は、あなた自身のために書くことができるということです。シェーダを書く事は基本的に難しいことではありませんが、テキストベースのプログラミングが必要になります。そのため、シェーダを書こうと試みる前に、CやJava、JavaScript のようなプログラミング言語に関して多少理解しておくことをお勧めします。
ライティングや複雑な形状を考える場合に考慮しておくことがいくつかあるため、jit.gl.slabオブジェクトのシンプルなデータ処理に焦点を絞ってみたいと思います。このチュートリアルでは GLSL シェーダ言語を使いますが、Jitter はその他にも Cg や ARB、NV アセンブリ言語で書かれたプログラムをサポートしていることは覚えておいて下さい。残念ながら、これらの言語に関して詳しく説明することは、Jitter ドキュメントの範囲を超えてしまいます。このチュートリアルでは、GLSLの非常に簡単な紹介を行い、シェーダコードをJitter が解釈できるフォーマットに統合する方法について例を挙げて説明することをねらいとしています。あなたが基本的に知っておく必要があることを説明し、より理解を深めるための信頼すべき資料について触れたいと思います。
動作環境:このチュートリアルをすべて体験するためには、プログラマブル・シェーダをサポートするグラフィックカード(例えば、ATI Radeon 9200、NVIDIA GeForce 5000シリーズ以降のグラフィックカード)が必要になります。また、グラフィックカードのOpenGLドライバを利用できる最新のものにアップデートしておくことを推奨します。これは、Macintosh では、最新のOS アップデートによって提供されます。また、PC の場合は、グラフィックスカード、あるいはコンピュータの製造会社から得ることができます。
4つのソースによるビデオミキサから始めましょう。これは、数学ライブラリによって提供されるインスタンスやシェーダの合成によって得られるインスタンスを少数組み合わせることによってすでに行っていることですが、これを1度に1つのシェーダで行うことはより効率的です。4つのソースをミックスするための計算式は次のようになるでしょう。ここで、a、b、c、d は個々の入力成分を反映させる度合いを表す定数です。
output = a*input0 + b*input1 + c*input2 + d*input3
GLSL では、これは次のように表現されます。
uniform vec4 a; uniform vec4 b; uniform vec4 c; uniform vec4 d; void main (void) { vec4 input0; vec4 input1; vec4 input2; vec4 input3; vec4 output; output = a*input0 + b*input1 + c*input2 + d*input3; }
ここでは、グローバル変数 a、b、c、dおよびmain() という関数を定義しています。main()には、 ローカル変数 input0、input1、input2、input3、およびoutput が含まれています。キーワード uniform は、フラグメントプログラムの実行範囲において値が一定(uniform)であることを示しています(すなわち、全てのフラグメントは a,b,c,d に関して同じ値を持ちます)。vec4 型は4つの値によるベクタのことで、一般的には一様座標の点 - point(x,y,z,w) - や 色 - color(r,g,b,a) -を記述する場合に使用されます。上記のように GLSL で2つの vec4 の要素を乗算する場合、各点毎の乗算になり、その結果、2つのオペランドの同じ要素の積をそれぞれの要素として持つようなvec4 になります(例えば m = p*q の場合 m.r = p.r*q.r 、m.g = p.g*q.g 、m.b = p.b*q.b 、m.a = p.a*q.a になります(訳注:m.r は m の要素 r を表します))。
このプログラムはフラグメントプログラムです。そして、メイン関数はイメージの個々のフラグメント(またはピクセル)ごとに1回実行されます。このプログラムは、隣接したピクセル出力や、それがいつ実行を開始されたかを知りません。これは、高速なパフォーマンスを得られるように、複数のフラグメントのためにいくつかのインスタンスを並列的に実行することを許可するということです。CPU にはこの種の制約はありませんが、コンピュータの演算は本質的には平行処理可能なものではありません。
GLSL に関する詳細な情報(キーワード:ビルトイン演算子 - built in operators、ビルトイン変数 - built-in variables、構文 - syntax 等)を得るためには、OpenGL Shading Language Reference(別名「オレンジブック (The Orange Book)」および、OpenGL Shading Language Specification を読むことを推奨します。Specification(仕様書)は opengl.orgからオンラインで得ることができます。また、lighthouse3d.comの1コーナーとしてGLSL のオンラインチュートリアルもあります。
uniform vec4 a;
uniform vec4 b;
uniform vec4 c;
uniform vec4 d;
//変化するテクスチャ座標を定義
varying vec2 texcoord0;
varying vec2 texcoord1;
varying vec2 texcoord2;
varying vec2 texcoord3;
// 矩形のテクスチャサンプラを定義>
uniform sampler2DRect tex0;
uniform sampler2DRect tex1;
uniform sampler2DRect tex2;
uniform sampler2DRect tex3;
void main (void)
{
// テクスチャのサンプル
vec4 input0 = texture2DRect(tex0, texcoord0);
vec4 input1 = texture2DRect(tex1, texcoord1);
vec4 input2 = texture2DRect(tex2, texcoord2);
vec4 input3 = texture2DRect(tex3, texcoord3);
vec4 output;
// 演算の実行
output = a*input0 + b*input1 + c*input2 + d*input3;
// フラグメントカラーへのデータの書き込み
gl_FragColor = output;
}
キーワード varying は個々のフラグメントごとにパラメータが変更されることを意味しています。例えば、texcoord0 は、一番左の入力テクスチャから抽出される、補間された (x,y) ピクセル座標を参照します。私たちは、texture2dRect()関数を対応するサンプラID と テクスチャ座標ベクタと共に呼び出すことによって、このテクスチャをサンプリングすることができます(すなわち、このテクスチャ座標に関する色の値を得ることができます)。 sample2DRect データ型、および texture2DRect() 関数は、私たちが矩形テクスチャを使っていることを示しています。通常の OpenGL のテクスチャは大きさを持っていますが、それぞれの大きさは2の累乗(例えば、256 X 256)に制限されています。そして、値は「正規化された」座標(0. 〜 1. の少数値)によってインデックス付けされています。矩形テクスチャは、一方で、任意の大きさを許します(例えば 720 X 480)、そして「座標」は言い換えれば「ピクセルの位置」を表しています(例えば、0. -720.、および 0. -480.)。Jitter では、ビデオと連動して動作することがよくあるため、2の累乗ではないデータセットを使う場合のパフォーマンス向上のために、デフォルトのテクスチャタイプとして、矩形テクスチャを使っています。sampler2D データ型と texture2D() 関数でも通常のテクスチャを使うことはできますが、入力されるテクスチャは、rectangle アトリビュートを 0 にセットして作られていなければなりません。
前のコードは、今や完全な GLSL フラグメントプログラムです。しかし、それはまだ何も行ないません。それは、私たちのテクスチャ座標を渡す頂点プログラムが必要なためです。名前から想像がつくように、頂点プログラムはジオメトリ(形状)の個々の頂点について1回実行されます。ここでは、テクスチャ座標をこのジオメトリに接続することを含む、ジオメトリに対する任意の修正が計算されます。一般的に私たちがテクスチャ座標によって行ないたいように、頂点プログラムによって生成される値はジオメトリの表面全体にわたって自動的に補間されます。私たちの頂点プログラムのために、頂点をカレントのモデルビューを投影するマトリックスによって変換し、テクスチャ座標をカレントのテクスチャ変換マトリックスによって変換し(これは、矩形テクスチャ座標をスケールし、場合によってはそれを反転させる方法です)て、その後、フラグメントプログラムにテクスチャ座標を変数値として渡したいと考えるでしょう。このような頂点プログラムは、次のようになります。
// 変化するテクスチャ座標を定義
varying vec2 texcoord0;
varying vec2 texcoord1;
varying vec2 texcoord2;
varying vec2 texcoord3;
void main( void )
{
// 入力される頂点の位置に対する、出力される頂点の位置はカレントの
//ModelViewProjection マトリックス によって変換されます。
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
// 適切なテクスチャマトリックスによって、変化するテクスチャ座標を変換し
// 入力テクスチャ座標に割り当てます。これは、矩形で反転されたテクスチャ
// のために必要です。
texcoord0 = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);
texcoord1 = vec2(gl_TextureMatrix[1] * gl_MultiTexCoord1);
texcoord2 = vec2(gl_TextureMatrix[2] * gl_MultiTexCoord2);
texcoord3 = vec2(gl_TextureMatrix[3] * gl_MultiTexCoord3);
}
これらのプログラムは一緒になって、すべてのピクセルデータの処理というハードな仕事をこなしますが、まだ、最後にもう1つのコンポーネントがあります。Jitter がとこれらのプログラムを読み込み、ユーザにパラメータを公開するためには、これらのプログラムを Jitter の XML シェーダファイル (JXS) でパッケージ化する必要があります。このファイルでは、ユーザがセットできる変数、a、b、c、d、およびその初期値を指定し、複数のテクスチャユニットをバインドします。これによって、プログラムはこれらに正しくアクセスできるようになり、プログラムの定義や、ユーザ変数のプログラム変数へのバインドが可能になります。
<jittershader name=”fourwaymix”> <param name=”a” type=”vec4” default=”0.25 0.25 0.25 0.25” /> <param name=”b” type=”vec4” default=”0.25 0.25 0.25 0.25” /> <param name=”c” type=”vec4” default=”0.25 0.25 0.25 0.25” /> <param name=”d” type=”vec4” default=”0.25 0.25 0.25 0.25” /> <param name=”tex0” type=”int” default=”0” /> <param name=”tex1” type=”int” default=”1” /> <param name=”tex2” type=”int” default=”2” /> <param name=”tex3” type=”int” default=”3” /> <language name=”glsl” version=”1.0”> <bind param="a" program=”fp" /> <bind param="b" program=”fp" /> <bind param="c" program=”fp" /> <bind param="d" program=”fp" /> <bind param="tex0" program=" fp” /> <bind param="tex1" program=" fp” /> <bind param="tex2" program=" fp” /> <bind param="tex3" program=" fp” /> <program name="vp" type=”vertex" source="43j-fourwaymix.vp.glsl" /> <program name="fp" type=”fragment” source="43j-fourwaymix.fp.glsl" /> </language> </jittershader>
jitter shader タグは、私たちのシェーダを、オブションである名前と共に定義します。 paramtag は、Max 環境に公開されるパラメータの名前、型、およびオブションである初期値を定義します。language タグは、指定した言語とバージョンによるプログラムのブロックを定義します。bind タグはユーザに公開されるパラメータを指定されたプログラムの変数にバインドします。最後に、program タグは私たちのプログラムを名前、型、ソースファイルと共に定義します。ソースは、XML コメント、または CDATA ブロックの内部にインクルードされることによって、任意にXML ファイル自身に埋め込むことができます。Jitter ディストリビューションによって提供されているシェーダファイルの多くはこの形になっています。XML タグの完全なリファレンスが提供されています。補遺 D の JXS フォーマットを参照して下さい。
これで、すべてのピースが揃いました。プログラムが処理を実行するようすを見てみましょう。
・Jitter チュートリアルフォルダにあるチュートリアルパッチ 43.jYourVeryOwnSlab.pat を開いて下さい。qmetro オブジェクトに接続された toggle ボックスをクリックして下さい。
・それぞれの jit.qt.movie のインスタンスにムービーを読み込んで下さい。メッセージボックスオブジェクトによって準備されているものを使っても、自分で選んだメディアを使っても構いません。
・jit.gl.slab オブジェクトに read 43j-fourwaymix.jxs というメッセージを送り、シェーダを読み込んで下さい。
すべてが配置されたパッチ
jit.gl.slab オブジェクトが 4 まで設定できる inputs アトリビュートを持っていることに気づいたと思います。これは、4 入力ミキサシェーダ用にデフォルトのインプット数 2 より多いインプットを持たせるために必要です。他にもこのプログラムについていくつか気がつく点があるはずです。メッセージ dump params あるいは dump assembly を送ると、コンパイルされたコードを眺めることができます。最後に、シェーダがロードされ、コンパイルされた場合に最適なパフォーマンスを得ることができるように、Jitter が全てのコンパイル済みシェーダのキャッシュを保持しているということは覚えておく価値があります。ディスク上のソースファイルが修正されたことが認識された場合、これはシェーダを再コンパイルするだけのものです。jit.gl.slab にメッセージ flush_chash を送ることによって、キャッシュの内容を消去することができます。
このチュートリアルでは、jit.gl.slabオブジェクトによって GPU 上で4つの入力ソースをミックスするシェーダを、GLSL でスクラッチから作る方法について紹介しました。これは、フラグメントプログラム、頂点プログラムを書き、それらをJitter XML シェーダファイルの中にラッピングすることによって行ないます。これにより、シェーダはJitter にロードされることが可能になり、パラメータはMax 環境でユーザに公開されます。input アトリビュートによって、jit.gl.slabで 2 つ以上のインプットを使う方法についても紹介しました。