タイトルCocoaはやっぱり!出張版》8. OpenGLを使う(3)カテゴリーグラフィックス, Cocoa, 鶴薗賢吾のCocoaはやっぱり!出張版
作成日2002/2/19 14:1:16作成者新居雅行
■ 座標変換
CocoaのApplication Kitには、NSAffineTransformという2次元の座標変換のクラスがありますが、OpenGLにも3次元の座標変換の機能があります。これを使って、平行移動・拡大縮小・回転 ( ロール・ヘッド・ピッチ ) が簡単にできます。これらを実現するためのメソッドは以下のようになります。ただし、座標変換の関数は実行の順番で結果が変わりますので、汎用のメソッドというわけではありません。

//ソース:MyOpenGLView.m、
//メソッドtransformMoveX : moveY : moveZ : zoomX : zoomY : zoomZ : ...

- (void) transformMoveX : (float) mx
moveY : (float) my
moveZ : (float) mz
zoomX : (float) zx
zoomY : (float) zy
zoomZ : (float) zz
roll : (float) roll
head : (float) head
pitch : (float) pitch {

glMatrixMode( GL_MODELVIEW ); // 操作する座標系をモデルにする
glLoadIdentity(); // 座標系をリセットする

glTranslatef( mx, my, mz ); // 移動
glScalef( zx, zy, zz ); // 拡大縮小

glRotatef( roll , 0, 0, 1 ); // ロール
glRotatef( head , 0, 1, 0 ); // ヘッド
glRotatef( pitch, 1, 0, 0 ); // ピッチ

}

まず、座標変換を行う対象をglMatrixModeで選択します。GL_MODELVIEWで、これから描画するオブジェクトの座標が変換されることになります。glLoadIdentityで、座標変換を行うマトリックスを初期化します。そして、glTranslatefで平行移動、glScalefで拡大縮小、glRotatefで回転になります。

このメソッドをdrawRect : 内から以下のように呼び出します。

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

[ self transformMoveX : 0 moveY : 0 moveZ : 0
zoomX : 1 zoomY : 1 zoomZ : 1
roll : 30 head : 0 pitch : 0 ]; // 30度ロール

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

[ self transformMoveX : 0 moveY : 0 moveZ : 0
zoomX : 1 zoomY : 1 zoomZ : 1
roll : 45 head : 0 pitch : 0 ]; // 45度ロール

[ self drawGradRect ]; // グラデーションの四角形を描画

すると、以下のような結果になります。

◇座標変換したときの実行結果
 

ちょっと補足:ロールは「 手を前に伸ばして手首を回転させたときの動き 」、ヘッドは「 首を左右に振ったときの動き 」、ピッチは「 お辞儀をしたときの動き 」に相当します。

■ 初期化について
先程、initWithFrame : メソッドのためにCustom Viewを配置するようにしましたが、実装しなくてもとりあえずOpenGLを呼び出して描画を行うことが出来ました。これは、NSOpenGLViewの初期化メソッドがデフォルトの状態を決めて初期化を行ってくれているためです。

ただし、このデフォルトの状態だと必要のないバッファが取られている可能性もありますし、逆に必要なものが取られていない可能性もあります。そのため、自分で初期化を行いたい場合もあるでしょう。その場合は、initWithFrame : を記述していきます。サンプルを以下に書きます。

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

- (id) initWithFrame : (NSRect) frameRect {

NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFADoubleBuffer, // ダブルバッファを使う
NSOpenGLPFAAccelerated , // ハードウェアアクセラレーションを使う
NSOpenGLPFAStencilSize , 32, // ステンシルバッファのビット数を32bitにする
NSOpenGLPFAColorSize , 32, // 画像用バッファのビット数を32bitにする
NSOpenGLPFADepthSize , 32, // デプスバッファのビット数を32bitにする
0 // ターミネータ
};
NSOpenGLPixelFormat* pFormat;

pFormat = [ [ [ NSOpenGLPixelFormat alloc ] initWithAttributes : attr ]
autorelease ];
self = [ super initWithFrame : frameRect
pixelFormat : pFormat ]; // スーパークラスによる初期化

[ [ self openGLContext ] makeCurrentContext ];
// カレントコンテキストを変更
glClearColor( 1.0, 1.0, 1.0, 1.0 ); // 背景色を黒にする

return( self );

}

最初でNSOpenGLFormatAttributeの配列を定義しています。OpenGLでどういうバッファを使用するかとか、どういう属性を持つかの定数を定義しているのがNSOpenGLFormatAttributeです ( enumです )。沢山の指定がありますので、この定数のうち必要なものを配列に詰めて、それをパラメータとしてメソッドに渡すという形になっています。

NSOpenGLPixelFormatAttribute attr[] = {
NSOpenGLPFADoubleBuffer, // ダブルバッファを使う
NSOpenGLPFAAccelerated , // ハードウェアアクセラレーションを使う
NSOpenGLPFAStencilSize , 32, // ステンシルバッファのビット数を32にする
NSOpenGLPFAColorSize , 32, // 画像用バッファのビット数を32bitにする
NSOpenGLPFADepthSize , 32, // デプスバッファのビット数を32bitにする
0 // ターミネータ
};

NSOpenGLPFADoubleBufferのように定数単独のものと、NSOpenGLPFColorSizeのように数値を伴うものがありますが、前者は論理値の指定になります。存在しているとYES、存在しない場合はNOという指定になります。配列の最後には、ターミネータとして0を入れます。なお、NSOpenGLPixelFormatAttributeの定数の意味を理解するには、OpenGLの知識が必要になりますので、ここでは使用方法のみの説明にとどめます。

この配列を使って、NSOpenGLPixelFormatクラスのインスタンスを以下のように生成します。

NSOpenGLPixelFormat* pFormat;

pFormat = [ [ [ NSOpenGLPixelFormat alloc ] initWithAttributes : attr ]
autorelease ];

そして、このインスタンスを使って、NSOpenGLViewを初期化するという流れになります。

self = [ super initWithFrame : frameRect
pixelFormat : pFormat ]; // スーパークラスによる初期化

 ★ NSOpenGLContext : ピクセルフォーマット指定での初期化
  [書式] - (id) initWithAttributes : (NSOpenGLPixelFormatAttribute *) attribs
  [入力] attribs : ピクセルフォーマット。
  [出力] 返り値 : NSOpenGLContextのインスタンス。

 ★ NSOpenGLView : ピクセルフォーマット指定での初期化
  [書式] - (id) initWithFrame : (NSRect) frameRect
     pixelFormat : (NSOpenGLPixelFormat *) format
  [入力] frameRect : ビューのサイズ指定。
     format : ピクセルフォーマット。
  [出力] 返り値 : NSOpenGLViewのインスタンス。

‥‥‥‥‥‥‥この項、続く‥‥‥‥‥‥‥[鶴薗賢吾]‥‥‥‥‥‥‥
関連リンクCocoaはやっぱり!