タイトルCocoaはやっぱり!出張版》8. OpenGLを使う(2)カテゴリーグラフィックス, Cocoa, 鶴薗賢吾のCocoaはやっぱり!出張版
作成日2002/2/19 14:0:52作成者新居雅行
続いて、MainMenu.nibウィンドウのClassesタブの中でNSOpenGLViewのサブクラスを作成します。クラス名はMyOpenGLViewとします。これを、先程のCustom Viewのクラスに割り当てて、ソースの出力を行います。この辺りの流れは、自前のビューを作成するのと同じです。ビューの形状ですが、今回は、以下のように正方形にしてください。

◇正方形にビューを配置する
 

そうしましたらコーディングに入ります。まず、ビューの真ん中にOpenGLを使って黄色い四角形を書いてみましょう。これもいつもと同様にdrawRect : メソッドに描画処理を書いていきます。

//ソース:MyOpenGLView.m、メソッド:drawRect :

- (void) drawRect : (NSRect) rect {

glClearColor( 1.0, 1.0, 1.0, 1.0 ); // 背景色指定
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// 色とデプスバッファをクリア

glColor4f( 1.0, 1.0, 0.0, 1.0 ); // 描画色を指定
glRectf( -0.6, -0.6, 0.6, 0.6 ); // 矩形を描画

glFinish(); // 描画コマンド実行
[ [ self openGLContext ] flushBuffer ]; // 画面更新

}

glで始まっているのがOpenGLの関数です。このように、OpenGLに大きく依存したコードになります。そして、OpenGL.frameworkのヘッダーを読み込むために、MyOpenGLView.hに以下の記述を足してください。

//ヘッダー:MyOpenGLView.h

#import
#import
#import

この3つが、必要なヘッダーです。これを実行すると、このようになります。

◇黄色の四角の描画結果
 

では、コードを簡単に説明します。glClearColorで背景色を黒に指定して、glClearで実際にクリアしています。glClearColorのパラメータは、4つありますが、透明度を含んだRGBAの4つを指定できるようになっています。値の範囲は、0.0〜1.0です。

glClearのパラメータは、どのバッファをクリアするかという情報です。OpenGLは、画面に表示される各ピクセルの色情報を覚えるバッファ以外にもいくつかのバッファを持っています。陰面処理を行うためのデプスバッファ ( Zバッファともいいます ) や、全ピクセルに対する一括演算を行うためのアキュムレーションバッファなどです。今回は、色情報のバッファ ( GL_COLOR_BUFFER_BIT ) とデプスバッファ ( GL_DEPTH_BUFFER_BIT ) をクリアするために、これらの論理和を取ってパラメータとしています。

ちょっと補足:OpenGLは、3Dのグラフィックを描画する際の陰面処理をデプスバッファで行っています。デプスバッファとは各ピクセルの視点からの距離を記憶しているバッファです。ピクセルを描画するときには、そのピクセルの視点からの距離をデプスバッファへも書き込んでいきます。次回、同じピクセルへの書き込みが発生したときは、視点からの距離がそれよりも近い場合にのみ書き込むようにすることで陰面処理を行います。デプスバッファをクリアするということは、全ピクセルを最も遠い値で埋めることになります。また、Zバッファと呼ばれるのは、視線方向にZ軸があるためです。

glColor4fでは、描画色を指定します。RGBAの値をパラメータとします。glRectfでは、パラメータで指定された四角形を描画します。デフォルト状態の座標系は、左下が ( -1.0, -1.0 ) で、右上が ( +1.0, +1.0 ) になっています。OpenGLは、3Dグラフィックのエンジンですが、2Dの座標指定も出来ます。2Dで座標指定すると3D空間の中の特定の面に対しての描画になります。ちなみに、Z軸は、ビューの面に垂直で手前に向かって伸びています。

◇OpenGLのデフォルトでの座標軸
 

最後のglFinishで、これらの処理を実行させます。

OpenGLにはダブルバッファの機能がついていて ( オフにすることも出来ます )、画面に表示されているフロントバッファと、OpenGLが直接描画するバックバッファの2つのバッファを持っています。バックバッファに描画しておいて、描画が一段落したところでバックバッファからフロントバッファへコピーする ( バックバッファとフロントバッファをスイッチする場合もあるようです ) ことで、描画過程を見せなくしたり、描画時のちらつきを抑えたりするわけです。

この、バックバッファからフロントバッファへのコピーを行うのが、NSOpenGLContextクラスのflushBufferメソッドです。NSOpenGLViewであるselfのopenGLContextメソッドで、OpenGLのコンテキストが取得できますので、それに対してflushBufferを実行します。

 ★ NSOpenGLView : OpenGLのコンテキストを取得
  [書式] - (NSOpenGLContext *) openGLContext;
  [入力]
  [出力] 返り値 : 割り付けられているOpenGLのコンテキストのインスタンス。

 ★ NSOpenGLContext : バックバッファを画面に表示する
  [書式] - (void) flushBuffer
  [備考] ダブルバッファを使用しているときのみ有効。
  [出力] 返り値 :

OpenGLの関数の名前は、glで始まっていることはお話しましたが、関数名の末尾の部分にも意味のある文字が付いています。glColor4fの末尾の4fは、パラメータが4つの浮動小数値 ( Float ) であるということ表しています。ここには出てきていませんが、glColor3iという関数もあります。こちらは、透明度を指定しないRGBの3つの整数値 ( Integer ) で色を指定することになります。このように、OpenGLには同じ機能でパラメータだけが違うという関数が沢山あります。そして、関数名からそれらを見分けることが出来ます。

■ グラデーション付きの描画
先程のサンプルはちょっと地味でしたので、もう少し派手なものにしてみましょう。グラデーション付きの四角形を書いてみます。以下のメソッドを新たに書いて、drawRect : メソッド内から [ self drawGradRect ]; で呼び出してください。

//ソース:MyOpenGLView.m、メソッド:drawGradRect

- (void) drawGradRect {

glBegin( GL_QUADS ); { // 四角形列の定義

glColor4f( 1.0, 0.0, 0.0, 1.0 ); // 赤
glVertex3f( -0.5, -0.5, 0.0 ); // 左下の頂点

glColor4f( 0.0, 1.0, 0.0, 1.0 ); // 緑
glVertex3f( 0.5, -0.5, 0.0 ); // 右下の頂点

glColor4f( 0.0, 0.0, 1.0, 1.0 ); // 青
glVertex3f( 0.5, 0.5, 0.0 ); // 右上の頂点

glColor4f( 1.0, 0.0, 1.0, 1.0 ); // 紫
glVertex3f( -0.5, 0.5, 0.0 ); // 左上の頂点

} glEnd();

}

glRectfよりももっと凝った四角形を描く場合は、glBegin( GL_QUADS ); 〜 glEnd(); を使用します。この2つの間で様々な指定が出来ます。glVertex3fは、各頂点の座標を指定しています ( Vertexとは頂点のこと )。3fですので、3Dでの座標指定になります。glVertex2fを使えば、2Dでの指定も出来ます。頂点の色指定は、頂点の座標指定の前に行います。glColor4fがそれです。頂点毎に別の色を指定することでグラデーションを作ることが出来ます。

GL_QUADSと複数形になっているのは、複数個の四角形を一気に描けることを意味しています。もし、直方体などの立体を描画したい場合は、glBegin( GL_QUADS ); 〜 glEnd(); の間に座標指定を4つずつ増やして6つの四角形を指定すればよいのです。GL_QUADS以外にも、GL_POINTS、GL_LINES、GL_POLYGON、GL_TRIANGLESなどがあり、様々な図形を描画することが出来ます。

実行結果は以下のようになります。

◇グラデーション付きの四角の描画結果
 

ちょっと補足:四角形の頂点の指定の順番は、このサンプルでは左回りになっています。OpenGLでは、左回りに見える側が表 ( おもて ) 側になります。立方体のような閉じた物体を描画する場合は、裏面を描画することはあり得ないので、裏面を描画しないことで高速化を行うという描画オプションがあります。
‥‥‥‥‥‥‥この項、続く‥‥‥‥‥‥‥[鶴薗賢吾]‥‥‥‥‥‥‥
関連リンクCocoaはやっぱり!