OpenGL始めるよ〜^^

色々な事情により、OpenGLを学ぶことにしました。

少し参考書を探したけれども、なかなか良さそうなのがない。
というか、どれも出版が古いものしか書店に並んでいない。
そもそもOpenGLの書籍の数が少ない。

新しく、自分が知らない技術を学ぶときのスタンスは人それぞれだと思いますが、
私の場合は、最初に「とにかく動くものを作る」ことから始めます。
サンプルコードを真似して打ってみて、動かしてみて、とりあえず喜ぶ。
その後、その背景にある深い部分を徐々に理解していきます。
理解がある程度深まったところで、自分で応用して、使って行く、という流れです。

ということで、最初に選んだ本がこれ!

OpenGLで作るiPhone SDKゲームプログラミング

OpenGLで作るiPhone SDKゲームプログラミング


この本は、サンプルを真似して動くものを作ってみるという私の目的に
まさにマッチしたものです。

しかしながら、問題もあります。

私がターゲットとしている、組み込み向けのOpenGLについては、
現在、Open GL ES 3.0が公開されています。
iPhoneで今主流なのはES 2.0です。
最近までは、XcodeOpenGLテンプレートは、1.1と2.0の両方に対応するコードを
吐き出していましたが、今では1.1用のコードは吐かれなくなっています。
(1.1が動かないというわけではない)

この参考書のサンプルは、ES 1.1のコードで実装されています。

そもそもES 2.0を詳しく解説している本というのは少ないですし、
あったとしてもES 1.1の知識を前提としていると思うので、
まずはES 1.1から学んで行くことにしました。

問題なのは、ES 1.1であることというよりも、Xcodeのバージョンの違いにより、
テンプレートのコードが参考書のものと全然違うということです。
つまり、最初の1章の内容がちんぷんかんぷんなのです!
ここでつまづいていては、後ろの章を読み進めて行くのが不安ですので、私は決心しました。

これから何をするかというと

OpenGLで作るiPhone SDKゲームプログラミング」

の第1章(1-1)の内容を、私が個人的に、勝手に、Xcode 4.5に置き換える

ということです。

「本買ったけど、テンプレート違くて全然わかんないよぉ;;」

という人に参考にしてもらえればと思います。



使用するXcodeのバージョンは4.5.2です。

プロジェクトの新規作成

OpenGL Gameテンプレートを選択します。

こんな感じのファイルが自動生成されます。
この時点では書籍のファイル構造と全く異なります。

インプレスジャパンのサイトからサンプルコードをダウンロードしておく
http://impressjapan.jp/books/2808/
zipを解凍すると、チャプターごとにディレクトリが分かれているので、
ここではch01の中身を参考にしてコードを修正していきます。

ESRenderer.hを作成する

プロトコルの定義だけなので、ファイルをまるごとコピーして、
自分のプロジェクトに追加すればOKです。
そもそも、この本ではES 1.1しか使わないので、このプロトコルは不要にも
思えますが、一応、形を合わせる意味で同様のファイルを作成しておくことにします。

ES1Renderer.hを作成する

最初はまだテクスチャを使わないので、テクスチャIDの属性は削除します。
renderMainというメソッドも最初の時点では存在しないメソッドであると
思われるため、
(P016のrendererメソッドの内容にrenderMainメソッドの呼び出しがないため)
最初は削除しておきます。
他はテンプレートが作成したっぽいコメントが付けられているので、
そのままコピーしてきます。すると以下の内容になります。

#import "ESRenderer.h"
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

@interface ES1Renderer : NSObject<ESRenderer>
{
@private
    EAGLContext* context;
	
    // The pixel dimensions of the CAEAGLLayer
    GLint backingWidth;
    GLint backingHeight;
	
    // The OpenGL names for the framebuffer and renderbuffer used to render to this view
    GLuint defaultFramebuffer, colorRenderbuffer;
}

-(void)render;
-(BOOL)resizeFromLayer:(CAEAGLLayer*)layer;

@end

ES1Renderer.mmを作成する

ダウンロードしてきたサンプルは1章の最後の時点でのソースになっているため、
この時点では不要なコードが多いです。
まず、graphicUtil.hは最初の時点では出てきていないはずなので、
#import "graphicUtil.h"
を削除します。

次にinitメソッド内でのテクスチャ初期化のコードを削除。
1章の最初の時点ではテクスチャはまだ使わないため。

次にrenderMainメソッドの実装をすべて削除します。

次にdeallocメソッド内のテクスチャ解放の命令コードを削除します。

最後に、renderメソッドの内容を、書籍のP016を参考にして書き換えます。
最終的に以下のような実装になります。

-(void)render {
    // Replace the implementation of this method to do your own custom drawing
    static const GLfloat squareVertices[] = {
      -0.5f,  -0.33f,
       0.5f,  -0.33f,
      -0.5f,   0.33f,
       0.5f,   0.33f,
    };
	
    static const GLubyte squareCollors[] = {
        255, 255, 0,   255,
        0,   255, 255, 255,
        0,   0,   0,   0,
        255, 0,   255, 255
    };
	
    static float transY = 0.0f;
	
    // This application only creates a single context which is already set current at this point.
    // This call is redundant, but needed if dealing with multiple contexts.
    [EAGLContext setCurrentContext:context];
    
    // This application only creates a single default framebuffer which is already bound at this point.
    // This call is redundant, but needed if dealing with multiple framebuffers.
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0f, (GLfloat)(sinf(transY / 2.0f)), 0.0f);
    transY += 0.075f;
	
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // ポリゴン描画
    glVertexPointer(2, GL_FLOAT, 0, squareVertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    glColorPointer(4, GL_UNSIGNED_BYTE, 0, squareCollors);
    glEnableClientState(GL_COLOR_ARRAY);
	
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    // This application only creates a single color renderbuffer which is already bound at this point.
    // This call is redundant, but needed if dealing with multiple renderbuffers.
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

EAGLView.h, EAGLView.mmをまるごとコピーする

サンプルコードのファイルをそのままコピーして、プロジェクトに追加します。
ざっと内容を見た感じ、P019〜P021の修正以外に、執筆者が手を加えた様子はないように見えます。
基本的に、過去のテンプレートのコードから、ES2.0対応の部分を削除しただけのものと推測しておきます。

ViewController.hを修正する

書籍ではViewControllerが使われていないので、ここでは定義だけを残して
中身を削除することにします。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
@end

ViewController.mを修正する

中身をすべて削除してしまって問題ないと思いますが、
念のため、オーバーライドしそうなメソッドの定義だけ残しておきます。

#import "ViewController.h"

@implementation ViewController

-(void)dealloc {
    [super dealloc];
}

-(void)viewDidLoad {
    [super viewDidLoad];
}

-(void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

MainStoryboard.storyboardを修正する

ViewController直下のViewのクラス指定をEAGLViewに変更します。

AppDelegate.mを修正する

サンプルではウィンドウとビューがアウトレット接続されていますが、
わざわざ同じようにアウトレットする必要はないと思います。
要するに、start, stop, deallocの制御がでれば良いのだろう
ということで、以下のようにします。

#import "AppDelegate.h"
#import "EAGLView.h"

@implementation AppDelegate

-(void)dealloc {
    [_window.rootViewController.view release];
    [_window release];
    [super dealloc];
}

-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
    EAGLView* view = (EAGLView*)_window.rootViewController.view;
    [view startAnimation];
    return YES;
}
							
-(void)applicationWillResignActive:(UIApplication*)application {
    EAGLView* view = (EAGLView*)_window.rootViewController.view;
    [view stopAnimation];
}

-(void)applicationDidEnterBackground:(UIApplication*)application {
    EAGLView* view = (EAGLView*)_window.rootViewController.view;
    [view stopAnimation];
}

-(void)applicationWillEnterForeground:(UIApplication*)application {
    EAGLView* view = (EAGLView*)_window.rootViewController.view;
    [view startAnimation];
}

-(void)applicationDidBecomeActive:(UIApplication*)application {
    EAGLView* view = (EAGLView*)_window.rootViewController.view;
    [view startAnimation];
}

-(void)applicationWillTerminate:(UIApplication*)application {
    EAGLView* view = (EAGLView*)_window.rootViewController.view;
    [view stopAnimation];
}

@end

shader.vshを修正する

vsh, fshっていうのは、たぶん、ES2.0のプログラマブルシェーダというものだと思います。
よって、ES1.1のみを使う場合は、これらは削除してしまっても動くはずですが、
一応書籍のサンプルと形を合わせておく意味で残しておきます。
そして、shader.vshの中身が違うので、コピペしておきます。

QuartzCore.frameworkをプロジェクトに追加する

どういうわけか、Xcode4.5のテンプレートではQuartzCoreフレームワークがプロジェクトに含まれていません。
それだと、CAEAGLLayerを使っているところでリンクエラーになるので、
QuartzCore.frameworkをプロジェクトに追加します。
やり方は、プロジェクトエクスプローラの一番上の階層を選択して、
「TARGETS」の「Build Phases」タブの「Link Binary With Libraries」の
「+」をクリックして、QuartzCore.frameworkを選択して「Add」する。





以上です!

ビルドして動かして、気持ち悪いカラフルな四角が上下に動けば成功。

これで書籍の1-1「iPhoneにおけるOpenGL」の最後(P021)の状態と同じになったはずです。
1-2からは普通に進めていけると思います。