OB3Dの詳細

この章の目的は、OB3D と呼ばれる Jitter の OpenGL の詳細についての説明を加えることです。ここでは、OB3D のデフォルトのアトリビュートやメソッドを無効にしたり、あるいはオーバーライドする方法について、マトリックスの入出力のサポートについて、そして、テクスチャ、ディスプレイリスト、シェーダなどのリソースの管理について説明します。この章は、OpenGL API や OB3D クイックスタートの章の内容について熟知していることを前提に書かれています。OpenGL API については、このドキュメントで説明する範囲を超えています。OpenGLAPI に関する情報を得るためには、OpenGL Red Book や多くのオンラインチュートリアルを参照することを推奨します。

訳注:通称 Red Book とは 「OpenGL ProgrammingGuide : The Official Guide to Learning Opengl」という書籍です。最新版 Version 2.1 は 、出版社:Addison-Wesley Pub (Sd); 6版 (2007/7/27) ISBN-10: 0321481003 ISBN-13: 978-0321481009 となっています。
日本語版は、第5版が「OpenGL プログラミングガイド 原著第5版」著者:OpenGL策定委員会者 
松田 晃一訳 出版社:ピアソンエデュケーション; 原著第5版(2006/12/19)
ISBN-10: 4894717239ISBN-13: 978-4894717237 として出版されています。

Jitter OB3D クラスの定義

OB3D クイックスタートで述べたように、Jitter OB3D は多くのデフォルトのアトリビュートとメソッドを持っており、いくつかの特定メソッドの定義を必要とします。このセクションでは、これらの共通したアトリビュートとメソッドについて、さらに、必要な場合にカスタマイズした動作の実行を可能にする方法について明確にしようと思います。

Draw メソッドの宣言

すべての Jitter OB3D で、ob3d_draw というシンボルにバインドされたメソッドを定義しなければなりません。このメソッドはオブジェクト構造体だけを引数として取り、プライベートで、A_CANT 型を使って定義しなければなりません。プライベートな、ob3d_drawメソッドは、すべての OB3D に付加される標準の draw および drawraw メソッドによって呼び出されます。drawメソッドでは、ob3d_draw の呼び出しの前に、デフォルトの OB3D アトリビュートに関連した OpenGL のステートを設定しますが、drawraw メソッドではこの設定を行ないません。

デスティネーションとジオメトリ関係のメソッド

Jitter OB3D のアトリビュートの変更や、オブジェクトがレンダリングを行なうデスティネーションの変更を行なうことができます。この場合、リソースの解放やリビルド(再構築)が必要になります。このようなイベントが生じた場合に OB3D と通信を行なうメソッドが3つあり、これらのメソッドによって OB3D はリソースの管理を行なうことができます。 dest_closing はデスティネーションが解放されたことを OB3D に報告し、コンテキストに属する、テクスチャ、ディスプレイリスト、シェーダなどのリソースを解放しなければならないことを伝えます。dest_changed は、デスティネーションがリビルド(再構築)されたことを OB3D に報告し、新しいリソースの割り当てが可能であることを伝えます。 rebuild_geometry は、テクスチャユニットの変更や、jit_gl_drawinfo_setup の変更、あるいは jit_gl_texcoord などの t_jit_gl_drawinfo 関連の関数に影響を与えるような変更を受けた OB3D に対して、このような関数が使用するジオメトリをリビルド(再構築)するように要求します。これらのメソッドは、オブジェクト構造体だけを引数として取ります。 dest_closing および dest_changed メソッドはプライベートで、A_CANT 型を使って定義しなければなりません。また、rebuild_geometry メソッドは通常、型によって定義しますが、オブジェクト構造体以外の引数は取りません。そのため、ユーザが必要であると考える場合に、明確に呼び出すことができます。SDK のプロジェクト jit.gl.gridshape はこれらのメソッドの良い例です。ここでは、レンダリングを行なうデスティネーションが変更された場合、ディスプレイリストの解放と割り当てを行なう必要があり、マルチテクスチャリングをサポートするために jit_gl_texcoord を利用します。この利用の際には、テクスチャユニットの数や他のアトリビュートの変更によってリビルド(再構成)されたジオメトリが要求されます。

訳注:jit.gl.gridshape の例の中の jit_gl_gridshape_dest_changed では、recalcというフラグを設定するだけで、実際の処理は、git_gl_gridshape_draw の中で行なわれています。

Register メソッドの宣言

jit.gl.sketch や、デフォルトの registration(登録)メソッド jit_object_register を追加する必要があるオブジェクトでは、すべての jitter OB3D は名前をつけられ、名前による参照をサポートしています。オブジェクトの登録と通知に関する詳細は、後の章で説明します。

回転、およびスケール関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setup によってクラスに追加された rotaterotatexyzscaleviewalign というアトリビュートを持っています。これらのアトリビュートはob3d_draw_preamble 関数で使用され、オブジェクトの drawメソッドを呼び出す前に OpenGL のステートを設定しますが、これを無効にするためには、JIT_OB3D_NO_ROTATION_SCALE フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glMatrixModeglTranslateglRotateglScale を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

色関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setupによってクラスに追加された coloraux_colorsmooth_shading というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前に ob3d_draw_preamble 関数で使用されますが、これを無効にするためには、JIT_OB3D_NO_COLOR フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glColorglShadeModel を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

テクスチャ関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setup によってクラスに追加された texturecapturetex_maptex_plane_stex_plane_t というアトリビュートを持っています。 これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前に ob3d_draw_preamble 関数で使用されますが、これを無効にするためには、 JIT_OB3D_NO_TEXTURE フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glEnableglTexGenjit_gl_bindtexturejit_gl_unbindtexturejit_gl_begincapturejit_gl_endcapture を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

ライティング、およびマテリアル関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setup によってクラスに追加された lighting_enableauto_materialshininessmat_ambientmat_diffusemat_specularmat_emission というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前に ob3d_draw_preamble 関数で使用されますが、これを無効にするためには、JIT_OB3D_NO_LIGHTING_MATERIAL フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたのdrawメソッドの中では、適切な glEnableglLightglLightModelglMaterial を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

フォグ関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setup によってクラスに追加されたfogfog_params というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前にob3d_draw_preamble 関数で使用されますが、これを無効にするためには、JIT_OB3D_NO_FOG フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glEnableglHintglFog を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

ポリゴン変数関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setupによってクラスに追加された cull_facepoint_sizeline_width というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前に ob3d_draw_preamble 関数で使用されますが、これを無効にするためには、JIT_OB3D_NO_POLY_VARS フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glPolygonModeglEnableglCullFaceglPointSizeglLineWidth.を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

ブレンド関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setup によってクラスに追加された blend_modeblend_enable というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前に ob3d_draw_preamble 関数で使用されますが、これを無効にするためには、JIT_OB3D_NO_BLEND フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glEnableglBlendFunc を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

デプスバッファ、および アンチエイリアシング関連のアトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setup によってクラスに追加された depth_enableantialias というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前にob3d_draw_preamble 関数で使用されますが、これを無効にするためには、それぞれ JIT_OB3D_NO_DEPTH および JIT_OB3D_NO_ANTIALIAS フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。しかし、あなたの draw メソッドの中では、適切な glEnableglHint を呼び出すことによって、必要な OpenGL のステートを管理する必要があります。

マトリックス出力、および Automatic アトリビュートのオーバーライド

デフォルトでは、それぞれの Jitter OB3D は、jit_ob3d_setupによってクラスに追加されたmatrixoutputautomatic というアトリビュートを持っています。これらのアトリビュートは、オブジェクトの draw メソッドを呼び出す前に ob3d_draw_preamble 関数で使用されますが、これを無効にするためには、それぞれ JIT_OB3D_NO_MATRIXOUTPUT および JIT_OB3D_AUTO_ONLY フラグを使用します。これらのアトリビュートは、独自のアトリビュートを同じ名前で定義することによって、オーバーライドできます。

ユーザインターフェイスオブジェクトの宣言

jit.gl.handle のような、OB3D に対するユーザインターフェイスを宣言することができます。これを行なうためには、jit_ob3d_setup JIT_OB3D_DOES_UI フラグを使用しなければなりません。さらに、シンボル ob3d_ui にバインドされたメソッドを、プライベートな A_CANT 型を使って定義しなければなりません。プロトタイプ宣言は、次の jit.gl.handle の例と同様な形で行ないます。

t_jit_err jit_gl_handle_ui(t_jit_gl_handle *x, t_line_3d *p_line, t_wind_mouse_info *p_mouse);

Jitter クラスのコンストラクタ、デストラクタ

あなたの Jitter クラスのコンストラクタの中では、新規にオブジェクトを割り当てられるポインタとレンダリング先(デスティネーション)を使って jit_ob3d_new を呼び出さなければなりません。jit_ob3d_new 関数は標準の OB3D アトリビュートやいくつかの OB3D ステートを格納する構造体を割り当て、それらをデフォルトの値に初期化します。この構造体の内容は見えない形になっています。さらに、クラス定義の中で jit_ob3d_setup 関数を呼び出した際に指定されたバイトオフセットの位置にポインタをセットします。オブジェクトがマトリックス出力をサポートする場合や、描画の際に t_jit_glchunk 構造体を使用する場合、通常、コンストラクタの中で jit_glcunk_new または jit_glchunk_grid_new 関数を使って、最初の t_jit_glchunk の割り当てを行なわなければなりません。t_jit_glchunk 構造体やマトリックス出力の使い方に関しては、この章で後述します。同様に、あなたの OB3D Jitter クラスのデストラクタでは、jit_ob3d_setup を呼び出して、共通のOB3D ステートの格納に使用される内容の見えない形の構造体を開放し、jit_glchunk_free を使って割り当て済みの t_jit_glchunk のインスタンスをすべて開放し、ディスプレイリストやテクスチャなどの割り当て済みのリソースをすべて開放しなければなりません。

OB3D の draw メソッド

オブジェクトが実行する描画処理をは、すべて ob3d_draw メソッドに記述します。また、通常、コンテキストが有効でかつ設定済みであることがわかっているため、このメソッドでコンテキストに従ってリソースを割り当て、コンテキストのステートの問合せを行なわなければなりません。OB3D の draw メソッドで実行する描画処理の大部分は、純粋でシンプルな OpenGL ですが、いくつかの注意点があり、これについては後述します。

t_jit_glchunk 構造体とマトリックス出力

Jitter は汎用のマトリックス処理フレームワークであるため、当然ながら、ジオメトリ表現がマトリックスによる表現に適している場合には、マトリックスとして Jitter ネットワーク上でジオメトリ情報の受け渡しを行なう機能を持っています。マトリックスのセルには、位置、テクスチャ座標、法線ベクトル、色、エッジフラグなどの頂点情報を格納することができます。これについては、Jitter チュートリアル の「覆いの下のジオメトリ」で述べられています。また、頂点の接続性がマトリックス表現に含まれない場合には、頂点の接続状態を参照するための接続マトリックスを指定するというオプションもあり、頂点の描画を行なう際に使用するための描画プリミティブを指定することもできます。これらすべての情報、およびジオメトリマトリックスを直ちにレンダリングするか、Jitter ネットワークを通じて送信するかは、t_jit_glchunk によって管理されます。SDK サンプルの jit.gl.gridshape には、この t_jit_glchunk の使用例が示されています。t_jit_glchunk 構造体は、これに含まれる頂点マトリックスと共に、jit_glchunk_new または、jit_glchnuk_grid_new 関数によって割り当てられ、jit_glchunk_delete 関数によって開放されます。また、描画処理は、 jit_ob3d_draw_chunk 関数によって行なわれます。参考のために、t_jit_glchunk 構造体と、関連した chunk フラグを次に示します。

// jit_glchunk は1つの gl-コマンドのデータを格納するパブリックな構造体で、このフォ // ーマットによってglDrawRangeElemets に対して容易にデータを送ることができます。 typedef struct _jit_glchunk { t_symbol *prim; // GL_TRI_STRIP, GL_TRIANGLES など t_jit_object *m_vertex; // xyzst... データの jit_matrix. t_symbol *m_vertex_name; // 頂点マトリックスの名前 t_jit_object *m_index; // マトリックスの 1D 接続。オプション t_symbol *m_index_name; // 接続マトリックス名 unsigned long m_flags; // 特別なフラグ void *next_chunk; // 個々のリンクリスト, 通常は NULL です。 } t_jit_glchunk; // chunk 生成のためのフラグ #define JIT_GL_CHUNK_IGNORE_TEXTURES 1 << 0 #define JIT_GL_CHUNK_IGNORE_NORMALS 1 << 1 #define JIT_GL_CHUNK_IGNORE_COLORS 1 << 2 #define JIT_GL_CHUNK_IGNORE_EDGES 1 << 3

OB3D OpenGL の注意点

ob3d_draw メソッドの中では、標準の OpenGL 呼び出しを任意に使用できるとはいえ、Jitter の仕様に従うために注意すべき点がいくつかあります。その第1はテクステャ座標のバインディングです。jitter OB3D は、デフォルトでマルチテクスチャリングをサポートするため、必ずしも glTexCoord によって1つのテクスチャ座標だけを提示すれば十分というわけではありません。Jitterには、jit_gl_texcood(1/2/3)(f/fv) によってバインドされるテクスチャユニットに対応するだけのテクスチャ座標をセットするためのユーティリティルーチンがいくつかあります。デフォルトの OB3D アトリビュートでバインドされているテクスチャの数の判定には、すべての jit_gl_texcood の呼び出しごとにこれを行なう場合より若干多くのオーバーヘッドが必要になります。jit_gl_texcood 関数はt_jit_gl_drawinfo 構造体を引数として取ります。この構造体は jit_gl_drawinfo_setup 関数によって多くの頂点がレンダリングされる前に、一度設定されます。jit_gl_texcoord および jit_gl_drawinfo_setup 関数の使用例は、SDK の jit.gl.videoplane プロジェクトの中で示しています。もう1つの Jitter に特有なメカニズムは、jit.gl.texture の名前をつけられたインスタンスを使用したテクスチャのバインド方法です。この方法では、独自のテクスチャを OB3D 内で生成してバインドすることができますが、他の場合であれば jit.gl.texture が行なってくれるこれらに関する管理をすべて自分で行なわなければなりません。jit.gl.texture のインスタンスのバインド、およびアンバインドを行なうためには、jit_gl_bindtexture および jit_gl_unbindtexture 関数を呼び出さなければなりません。この関数は、引数としてt_jit_gl_drawinfojit.gl.texture のインスタンスの名前のシンボル、バインドされるテクスチャユニットのための整数を引数として取ります。これは、OpenGL で通常のテクスチャのバインドを行なう場合と異なり、jit.gl.texture のインスタンスをアンバインドする上で重要です。そうでない場合、何らかの問題が発生するかもしれません。

OB3D アトリビュートに関する情報の取得

デフォルトの OB3D アトリビュートは、通常、ob3d_draw メソッドの前に、あなたのオブジェクトのために自動的に処理されるコードにとって適切なものではありますが、時として、これらの値にアクセスする必要がある場合があります。デフォルトの OB3D アトリビュートは、外から見えない ob3d 構造体のメンバとして格納されているため、オブジェクトによって単にポインタの指す値を参照する方法ではアクセスすることができません。これらのアトリビュートにアクセスするためには、その代わりに、jit_attr_get* 関数を使用する必要があります。これらの関数に対しては、最初の引数として ob3d 構造体メンバを渡すのではなく、あなたのオブジェクト構造体を渡さなければなりません。次はその例です。

float pos[3]; jit_attr_getfloat_array(x,gensym(“position”),3,pos);

この値を、頻繁に取得する場合には、各呼び出しごとにシンボルを生成するのではなく、前もってシンボルを生成しておくほうが望ましいという点に留意して下さい。

コンテキストに関する情報の取得

レンダリングコンテキストはob3d_drawdest_closingdest_changed メソッドの中から常にセットされており、その中で aglGetCurrentContext、あるいは wglGetCurrentContext 関数を使って ネイティブなコンテキストを取得することができます。また、これらのメソッドの中で、標準的な OpenGL の glGet* 関数を使用して、ビューポートや変換マトリックスといったような、コンテキストの OpenGL ステートの判定を行なうこともできます。他のメソッドの中からネイティブなコンテキストの取得や、OpenGL のステートを取得しようとした場合、それらが無効である可能性があり、推奨できません。

他の描画オブジェクトとの関連

OpenGL のステートが持続的であり、あなたのオブジェクトの描画処理の後に、そのOpenGLのステートに従って描画されるオブジェクトが存在するかもしれないことを認識しておくことは重要です。あなたのオブジェクトがOpenGL のステートに対して何らかの変更を行なった場合、それに続くオブジェクトに影響を与える可能性があります。そのため、OpenGL のステートを、あなたのルーチンが呼び出される前の状態に復元しておかなければなりません。例えば、あなたのオブジェクトがテクスチャ変換マトリックスに対する変更を行なった場合、glMatrixModeglPushMatirxglPopMatrix を使用してテクスチャ変換マトリックスのプッシュとポップを行い、他のオブジェクトに対する問題が生じるのを防いでおかなければなりません。

OB3D Max ラッパークラスの定義

「OB3D クイックスタート」で述べたように、あなたの Max ラッパークラスの定義では、標準の描画メソッドを追加するための max_ob3d_setup 関数の呼び出しを追加し、特に独自にカスタマイズした assist メソッドを定義しようとするのでない限り、assist メソッドとしての max_jit_ob3d_assist 関数の呼び出しを追加するだけです。それ以外は、「Max ラッパークラス」の章で示したような、Jitter クラスのラップを行なう標準的なテクニックと全く同様です。OB3D Max ラッパークラスに関する必要な情報については、「OB3D クイックスタート」の章、および SDK の jit.gl.simple プロジェクトですべて示してありますので参考にして下さい。

マトリックス入力

時として、jit.gl.videoplane jit.gl.mesh の場合のように、OB3D によって入力されるマトリックスのサポートを行なうことが望ましい場合があります。OB3D と MOP を混在させることは望ましくありません。アーギュメントや標準のインレット、アウトレットでのコンフリクトが生じます。あなたの OB3D でマトリックス入力をサポートしたい場合、その代わりにあなたの Jitter クラスに jit_matrix シンボルにバインドされたメソッドを追加し、必要に応じて入力マトリックスのデータを処理するべきです。例えば、jit.gl.videoplane の場合ではテクスチャとして扱い、jit.gl.mesh の場合ではジオメトリデータとして扱っています。SDK の jit.gl.videoplane プロジェクトでは、マトリックス入力もサポートする OB3D の例を示しています。複数の入力マトリックスを処理する必要がある場合には、通常、それぞれの入力に対して別々の名前を与えたメソッドを宣言するか、jit_matrix メソッドが呼び出された場合にどちらかの入力を指定するアトリビュートを公開する方法によって管理します。この場合、Max ラッパークラスの中でのインレットを割り振りは自動的には行なわれないため、これを実現するための独自の方法が必要になる点に注意して下さい。