/**/

チュートリアル 30:
3Dテキストの描画

このチュートリアルでは、jit.gl.text3djit.gl.render オブジェクトを使って jit.window オブジェクト内に3Dテキストの描画や配置を行なう方法について述べています。同時に、 jit.gl.render オブジェクトを用いた OpenGL グラフィックスの描画の基本を学習していきます。

jit.gl.text3d オブジェクトは、jit.gl.render オブジェクトと連結して動作する、Jitterの中の多くの OpenGL 描画オブジェクトのうちの1つです。OpenGLは、2Dおよび3Dグラフィックスの描画のためのクロスプラットフォーム規格で、グラフィックスコプロセッサによる描画が可能な画像を記述するために設計されたものです。これらのコプロセッサはグラフィックプロセッサユニット(グラフィック処理装置)もしくはGPUとも言われ、描画処理を非常に高速化し、テクスチャの付いたポリゴンによって作られた複雑なシーンのリアルタイムでのアニメーションを可能にします。OpenGLグラフィックスは、コンピュータのCPU動作に負担をかけずに、映像表示やビジュアルインタフェースを素早く作成できるように支援します。

・Jitterチュートリアルフォルダにある30j3DText というチュートリアルパッチを開いて下さい。

パッチの左下にhelloと名付けられた jit.window オブジェクトがあります。このウィンドウがOpenGLの描画先です。

・Start Rendering と表示された toggle をクリックして下さい。

この toggletrigger オブジェクトに bang メ ッセージを送る qmetro オブジェクトをスタートさせます。 trigger オブジェクトはインレットで bang を受信するごとに右アウトレットから erase メッセージを出力し、その後、左アウトレットから bang メッセージを出力します。これらのメッセージは jit.gl.render hello オブジェクトに送信されます。

描画コンテキストの生成

Jitterにおけるすべての OpenGL グラフィックスは、jit.gl.render オブジェクトを用いて描画されます。それぞれの jit.gl.render オブジェクトは、描画を行なうために、名前を付けられたデスティネーション(送り先)を参照しなければなりません。このデスティネーションは jit.gl.render の最初のアーギュメントを使って指定することができます。このチュートリアルパッチでは、jit.gl.render hellohello という名前をつけたデスティネーションのウィンドウに描画する jit.gl.render オブジェクトを生成します。

hellojit.gl.render オブジェクトの名前ではなく、単にそのデスティネーションの名前である点に特に注意して下さい。

描画コンテキスト(描画環境)としての、jit.gl.render オブジェクトと hello という名前を持ったデスティネーションの組み合わせを見てみましょう。描画コンテキストは OpenGL レンダリングのために必要とされます。このパッチの左側にあるオブジェクト群は有効な描画コンテキストを構築するために充分なものです。そのため、toggle オブジェクトがクリックされたとき、jit.gl.render :building GL on window hello’ というメッセージがMaxウィンドウに現れます。これはコンテキストが生成されたことを知らせるものです。


jit.gl.render オブジェクトと名前を付けられたデスティネーションによって描画コンテキストが生成されます

コンテキスト内のGLオブジェクト

OpenGLコンテキストで様々な描画を行なうために、様々な Jitter オブジェクトが用意されています。これらの Jitter オブジェクトはすべて "jit.gl" で始まる名前が付けられています。 jit.gl.model(3Dモデルを描画するもの)、 jit.gl.plato(正多面体を描画するもの)、このチュートリアルパッチの右上に示されているjit.gl.text3d オブジェクトなどはその例です。これらのオブジェクトは、すべて、最初のアーギュメントとして描画コンテキストの名前を持ち、jit.gl.render オブジェクトが bang メッセージを受け取るたびにコンテキストに対して自動的に描画が実行されます。そのため、この例では、metro オブジェクトが bang メッセージを送り続ける限り、jit.gl.render オブジェクトは画面の消去、全オブジェクトの描画という同じ処理のセットを繰り返し実行します。ウィンドウ上で jit.gl.text3d オブジェクトの出力が表示されていないのは、このオブジェクトに対して文字列が何も割り当てられていないためです。これを修正しましょう。

・Hello \,Jitter! と書かれているメッセージボックスをクリックして下さい。

これで、 jit.gl.text3d オブジェクトにテキストがセットされます。 jit.window に"Hello"という言葉が表示されるはずで。テキストの中のカンマの前にバックスラッシュがある点に注意して下さい。一般的に、カンマはメッセージボックス内の連続したメッセージを分割するのに使われます。そのため、カンマを通常の文字シンボルとして扱うようにMaxに命じる場合には、バックスラッシュが必要になります。

技術的な詳細:このパッチでは、これまでの多くのチュートリアルパッチとは違って jit.matrix オブジェクトが存在しないため、qmetro オブジェクトが必要になります。複雑なパッチでは、metro オブジェクトによって繰り返しトリガーされる描画やマトリックスの演算が、次の bang までに終了しないかもしれません。この例では、テキストのレンダリングに40ミリ秒以上かかった場合、このような状況が発生します。通常、Maxスケジューラはそのキュー(待ち状態のメッセージのリスト)に、この bang を置きます。Maxは本来、キューからのメッセージが欠落することを許可しません。そのため、このパッチでは、各々の bang が40ミリ秒以上の時間を要するイベントを次々と生成し、メッセージの欠落が許可されない場合、いずれキューがオーバーフローするか、追加されていく待ち状態のメッセージを格納するためのメモリを使い果たしてしまうことになります。その結果、スケジューラは停止し、パッチの動作も停止してしまいます!おそらく、このようなことは考えてもみなかったことでしょう。qmetro オブジェクト(これは単に metroオブジェクトと jit.qball オブジェクトを組み合わせただけのものです。チュートリアル16を参照して下さい。)は、計算待ちの状態にある bang メッセージをすべて捨ててしまうことによってこのような状況を回避してくれます。パッチの他の部分がレンダリングを行なっている間に bang が発生した場合、その bang はキューに置かれます。次のbang が発生したときに前の bang がまだ渡されずに残っている場合、最初の bang は2番目の bang によって取って代わられ、その bang がキューの中の次の位置に置かれます。その後の動作も同様です。

例えば、10ミリ秒ごとに bang を送信するように設定された metro オブジェクトの出力が jit.qt.movie オブジェクトに送られ、この jit.qt.movie では1フレームの描画に100ミリ秒かかるエフェクトがかけられているとします。この場合 jit.qt.movie オブジェクトは、エフェクトが処理されている間に10個の bang メッセージを受け取ることになります。jit.qt.movie オブジェクトは、処理がまだ完了していないという状況を認識し、次のタイミングでは、10個の jit_matrix メッセージを送信するのではなく、1つの bang だけ残して他の bang はすべて捨ててしまいます。jit.qt.moviejit.matrixオブジェクトにはこの機能が組み込まれていますが、jit.gl.render オブジェクトはこのような機能を持っていません。これは、多くの場合、jit.gl.render オブジェクトがフレームの適切な描画を行なうためには、メッセージのグループ内での整合性が必要になるからです。この例で言えば、erase メッセージは画面を消去する必要があり、bang メッセージはテキストを描画する必要があります。erase メッセージがキューのオーバーフローを避けるために失われた場合、複数の bang メッセージが連続して処理され、複数のテキストのコピーが一度に描画されてしまうかもしれません。これは私たちが望んでいる動作ではありません。パッチがより複雑なものであれば、結果として、様々な種類の視覚的な悪影響が現れてしまう可能性があります。そのため、どのメッセージをキューから破棄するのかをパッチの設計者が決定できるように qmetro オブジェクトが提供されています。この例でも、他のほとんどのケースと同様、 metro オブジェクトの代わりに qmetro オブジェクトを使うだけで、描画されたものが常に正しく表示され、キューのオーバーフローが生じないことが保証されます。

共通の3Dアトリビュート

Jitterのすべての OpenGL オブジェクト(すべて"jit.gl"で始まります)は、3次元空間における配置、色、もしくは他の変更を行うための一般的なアトリビュートのセットである「GLグループ」を共有しています。3Dグラフィックスを描くことはたいへん複雑な作業です。ob3d グループはこの作業をできるだけ容易なものにするために、1つの3Dオブジェクトを描くために学習したメッセージがすべてのオブジェクトで動作することを可能な限り保証してくれます。この共通なアトリビュートのグループは、JitterオブジェクトリファレンスのGLグループセクションで完全にドキュメント化されています。ここでは、その3Dグループを紹介するために、jit.text3d オブジェクトの操作の中で、位置、回転、スケーリング(拡大・縮小)、座標軸のアトリビュートの実例を示します。

空間的な操作を見やすく、理解しやすくするために、jit.text3dオブジェクトに1組の空間軸を加えることができます。これによって、テキストオブジェクトがどの方向を向いているかが見やすくなり、オブジェクトの原点の正確な場所がわかりやすくなります。

axes $1と書かれたメッセージボックスに接続された toggle をクリックして、3Dオブジェクトの座標軸を表示させて下さい。


原点にある3Dテキスト

x 軸は赤で描かれ、右方向を指しています。ゼロはスクリーンの中心にあり、スクリーンの右方向へ進むに従って x の値が増加します。y 軸は緑で描かれ、上方向を指しています。また、z軸は青で描かれ、あなたの方向を指しています(あなたにまっすぐ向いているため、小さな青い点としてしか見えないでしょう)。これらの座標軸はオブジェクトのローカル座標系を表していて、空間内を移動します。最初にGLグループオブジェクトが生成されたとき、そのローカル座標系は回転、移動、スケーリングを行なわれていません。デフォルトでは、その3D空間の他の部分と同じ座標系を持ちます。

"Hello, Jitter!" というテキストは jit.window オブジェクトの表示領域に表示されますが、スクリーンの中心から描かれるため、最後の部分がはみ出してしまいます。テキストを左に移動させてみましょう。

・パッチの Common 3D attributes という部分にある x と表示されたナンバーボックスの値を -1. にセットして下さい。

このナンバーボックスは pak オブジェクトに浮動小数点の値を送り、pak オブジェクトは3つの数値が後に続いた position メッセージを jit.gl.text3d オブジェクトに送ります。ナンバーボックスの値を変えると、テキストが左に移動し、スクリーン上で全体が見えるようになるのがわかるでしょう。


position アトリビュートの変更

ここでは、jit.gl.text3d オブジェクトの position アトリビュートを変更しただけです。position メッセージの後には、3次元でのオブジェクト位置を設定する3つの数値アーギュメントを続けることができます。position メッセージに続く数値が3つより少ない場合、軸は [x, y, z] の順で当てはめられ、指定されていない軸でのオブジェクト位置は0に設定されます。例えば、 position 5. というメッセージが送信されると、GLグループオブジェクトの位置は [5, 0, 0] に設定されます。

オブジェクトの位置を変化させる操作を移動(translation)と呼びます。

今度はテキストを回転させてみましょう。

・パッチの Common 3D attributesという部分の pak rotate オブジェクトのすぐ上にあるx, y, z と表示されたナンバーボックスを 1.にセットして下さい。これによって、回転軸が設定されます。

angle と表示されたナンバーボックスを320.までドラッグして下さい。テキストが、次のスクリーンショットで示されている位置まで、軸の周囲を回転するのがわかるでしょう。


移動と回転を実行した後の3Dテキスト

1 〜 4個の数値を続けた rotate メッセージを送ることによって、GLグループオブジェクトの回転アトリビュートが設定されます。最初の数字は、オブジェクトの回転の量を「度」で表したもので、オブジェクトの回転軸に対して反時計周りになります。その他の3つの数は [x, y, z] ベクトルによって回転軸を指定します。position アトリビュートの場合と同様、回転のアトリビュートに続く数値も省略することが可能で、その場合にはデフォルト値であると解釈されます。roration メッセージが 1つの数値だけを伴っている場合、その数値は「度」で表現される角度を意味し、回転軸は [0,0,1] であると解釈されます。このとき、オブジェクトは z 軸に対して回転します。これは、言い換えれば、スクリーン の x - y 平面で回転することになります。 rotation メッセージが2つ以上の数値を伴っている場合、最初の数値は常に回転角を指定し、他の数値はその順序に従って回転のベクトルを指定します。

pak scale オブジェクトのすぐ上にある x と表示されたナンバーボックスを0.5に設定して下さい。これにより、3Dテキストはローカルのx軸に沿って 1/2にスケーリングされます。


移動、回転、スケーリング後の3Dテキスト

オブジェクトと一緒に赤い軸もスケールされている点に注意して下さい。この線に沿った点は、スケーリング操作前と比較して半分の距離になっています。軸は常にGLグループのローカル座標系に描かれ、この場合には、jit.gl.render オブジェクトのワールド座標系に対して移動、回転、スケーリングされています。

幾何学的な変形を実行する場合には、処理の順序をよく考えることが重要です。オブジェクトの rotatepositionscale アトリビュートは、それぞれ独立して、任意の順序で実行することが可能です。しかし、個々の描画が実行される際には、オブジェクトは次の順序で変形されます。

  1. スケーリング
  2. 回転
  3. 移動

この順序を守っていれば、アトリビュートは予想通りに実行されます。スケーリングの前に回転が行なわれている場合には、例えば scale 0.5というメッセージでは、オブジェクトはそのx 座標上ではなく、オブジェクトの回転による x、y、z の組み合わせを使ってスケーリングされてしまいます。

次の例は、異なった順序で処理を行なうことによる違いを示しています。


処理の順序によって違いが生じます

まとめ

描画コンテキストの生成は、Jitter で OpenGL グラフィックスを使うための第一歩です。描画コンテキストは、ウィンドウのような名前を付けられたデスティネーションと、そのデスティネーションに描画を行う jit.gl.render オブジェクトによって構成されます。

jit.gl.render と連携してOpenGLグラフィックスを描く、様々な Jitter オブジェクトが存在し、それらの名前はすべて "jit.gl" で始まっています。jit.gl.text3d オブジェクトはその一例です。すべてのJitter OpenGLオブジェクトは、3D空間での移動を行なうための一般的なアトリビュートのセットを共有しています。このオブジェクトのグループはGLグループと呼ばれます。