macOSでOpenGLプログラミング（1-1. プロジェクトの作成〜OpenGLの基本設定）

[macOSでOpenGLプログラミングの目次に戻る](http://qiita.com/sazameki/items/d060f20f621c658046d9)

# はじめに

2017年8月24日現在、iOSプログラミングに押されて、Macプログラミングの情報がかなり少なくなっています。この記事では、最新のmacOS 10.13とXcode 9を使って、OpenGLを使ったゲームを作る方法を解説します。

なお、最新のXcodeはMac App Storeから誰でも無料で入手できますので、インストールしておいてください。OpenGL用のフレームワークは、初めからmacOSに用意されていますので、Xcode以外にインストールが必要な物はありません。


# 1. Xcodeプロジェクトの作成

Xcodeのメニューから [File]-[New]-[Project…] を選択し、macOSのテンプレートから「Cocoa App」を選択して「Next」ボタンを押します。

　<img width="560" src="https://qiita-image-store.s3.amazonaws.com/0/166309/4fe8a603-fe94-c157-852f-b208380a62cd.png">


次の画面では、「Product Name」に「MyGLGame」と入力し、「Organization Name」には自分の名前を、「Organization Identifier」には「mytest」と入力します。**Swiftよりも、C言語/C++の記法が利用しやすいObjective-Cの方がOpenGLプログラミングに向いている**と思いますので、「Language」には「Objective-C」を選択します。またその下にある**「Use Storyboard」のチェックをONにしておきます**。Storyboardを使う場合に用意されるビュー・コントローラを利用した方が、OpenGL用のビューを作成しやすいためです。「Include Unit Tests」と「Include UI Tests」のチェックは外しておきましょう。全部入力し終えたら「Next」ボタンを押します。

　<img width="560" src="https://qiita-image-store.s3.amazonaws.com/0/166309/7206b5ad-45ed-9e8d-46dc-8949d2718473.png">


次の画面では、プロジェクトを作成するフォルダを指定して「Create」ボタンを押し、プロジェクトの作成を完了します。


# 2. フレームワークの追加

OpenGL関係のフレームワークをプロジェクトに追加します。

メインウィンドウ左側のプロジェクト名を選択し、「TARGETS」の中の「Build Phases」から「Link Binary With Libraries」を開いて、その下側にある「＋」ボタンを押します。

<img width="560" src="https://qiita-image-store.s3.amazonaws.com/0/166309/25581c54-9b1a-3a52-2a3f-f49470c8d383.png">

commandキーを押しながら「GLKit.framework」と「OpenGL.framework」の2つのフレームワークを選択して、「Add」ボタンを押してフレームワークを追加します。

　　<img width="260" alt="gl4.png" src="https://qiita-image-store.s3.amazonaws.com/0/166309/66a7bc3b-eda7-feaa-1bbd-d4e761f8a4b8.png">


追加したフレームワークは「Link Binary With Libraries」に表示されます。

　　<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/166309/e172146a-aa2f-16a4-f03a-765aa815fc6a.png">


# 3. カスタムのOpenGLビューを定義する

NSOpenGLViewクラスを継承したカスタムのOpenGLビューを定義して、独自のOpenGLのオプションを設定できるようにします。

ウィンドウ左側のプロジェクト・ナビゲータからViewController.mを選択し、右クリックでコンテキストメニューを出して「New File…」を選択します。

　　<img width="240" src="https://qiita-image-store.s3.amazonaws.com/0/166309/9a956759-29ec-a524-4619-f51d9a7978f6.png">

macOSのテンプレートから「Cocoa Class」を選択して「Next」ボタンを押します。

　　<img width="400" src="https://qiita-image-store.s3.amazonaws.com/0/166309/da7ac069-6cc8-0ebc-90bf-18584777d627.png">

次の画面では「Subclass of」の項目に「NSOpenGLView」と入力し、「Class」の項目に「MyGLView」と入力します。「Next」ボタンを押して、次の画面で「Create」ボタンを押して、クラスの作成を完了します。

　　<img width="70%" src="https://qiita-image-store.s3.amazonaws.com/0/166309/3caa5c85-6ae6-12fd-40ad-2cfdb191d712.png">

MyGLView.hとMyGLView.mの2つのファイルができます。

　　<img width="320" alt="gl13.png" src="https://qiita-image-store.s3.amazonaws.com/0/166309/6101b820-d07d-78ec-420a-f35f154fed47.png">



# 4. OpenGLビューを生成するコードを書く

ViewController.mファイルを編集して、MyGLViewクラスのインスタンスを生成して追加するコードを書きます。

先頭にMyGLViewクラスの定義を読み込む#import文を追加し、`viewDidLoad`メソッドの中にビューが読み込まれた直後にMyGLViewクラスのインスタンスを生成してサブビューとして追加するコードを書きます。最初から用意されている`setRepresentedObject:`メソッドはゲームでは通常使わないメソッドなので、削除しておきます。

```objc:ViewController.m
#import "ViewController.h"
#import "MyGLView.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSRect viewFrame = [self.view frame];
    MyGLView *glView = [[MyGLView alloc] initWithFrame:viewFrame];
    glView.translatesAutoresizingMaskIntoConstraints = YES;
    glView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
    [self.view addSubview:glView];
}

@end
```

Xcodeにおけるビューのレイアウトは、通常はStoryboardの上でグラフィカルに操作して行いますが、**OpenGLのバージョンなどを細かく指定してNSOpenGLViewを作成する**場合には、このように`alloc`メソッドと`initWithFrame:`メソッドの組み合わせで、**明示的にビューを生成するのがもっとも確実な方法**です。

`translatesAutoresizingMaskIntoConstraints`プロパティを使用して、`autoresizingMask`に`NSViewWidthSizable`と`NSViewHeightSizable`を指定することで、ウィンドウのリサイズに合わせて横方向にも縦方向にも伸縮するビューが作成できます。

> なお、今回はXcode 8.3.3の「Cocoa Application」のデフォルトで作成されるプロジェクトテンプレートに合わせてStoryboardの場合について説明していますが、ウィンドウなどをXIBで管理している場合も同様のことが言えます。
>
> XIBの場合は、インスペクタから "Prefer Coder" という設定のチェックを外せばGUI操作で貼り付けたビューの`initWithFrame:`メソッドが呼ばれるようになりますが（チェックが付いている場合には`initWithCoder:`メソッドが呼ばれる）、明示的に`alloc`して`initWithFrame:`メソッドを呼び出すと考えた方が、動作の流れが見えやすくなると思います。


# 5. MyGLViewクラスの実装を書く

最後に、MyGLView.mを次のように編集しましょう。OpenGL関係のヘッダファイルをインポートして、`initWithFrame:`メソッドでOpenGLのバージョンなどのオプションを指定してビューの初期化を行います。OpenGLの内部的な初期化が終わった直後には`prepareOpenGL`メソッドが呼ばれますので、ここでOpenGLの描画設定を行います。

```objc:MyGLView.m
#import "MyGLView.h"

#import <OpenGL/OpenGL.h>
#import <OpenGL/gl3.h>

@implementation MyGLView

- (instancetype)initWithFrame:(NSRect)frame
{
    NSOpenGLPixelFormatAttribute attrs[] =
    {
        NSOpenGLPFAAllowOfflineRenderers,
        NSOpenGLPFAAccelerated,
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFAColorSize, 32,
        NSOpenGLPFADepthSize, 32,
        NSOpenGLPFAMultisample,
        NSOpenGLPFASampleBuffers, 1,
        NSOpenGLPFASamples, 4,
        NSOpenGLPFANoRecovery,
        NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
        0
    };

    NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];

    self = [super initWithFrame:frame pixelFormat:pixelFormat];
    if (self) {
        [self setWantsBestResolutionOpenGLSurface:YES];
    }
    return self;
}

- (void)prepareOpenGL
{
    [super prepareOpenGL];

    NSOpenGLContext *glContext = [self openGLContext];

    glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    [glContext flushBuffer];
}

@end
```


## 5.1. OpenGL初期化オプションの指定

`initWithFrame:`メソッドの中では、様々なオプションを`NSOpenGLPixelFormatAttribute`の配列として指定して、それを元に`NSOpenGLPixelFormat`クラスのオブジェクトを作成します。このオプション指定の配列の中身は、基本的には、設定のキーと設定のバリューが交互に並んだ形になっています。そしてこれを親クラス (NSOpenGLViewクラス) の`initWithFrame:pixelFormat:`メソッドに渡すことで、指定した内容で初期化したOpenGLビューの作成が完了します。

とくにここでは、**「`NSOpenGLPFAOpenGLProfile`」をキーにして「`NSOpenGLProfileVersion3_2Core`」を指定する**ことによって、**OpenGLの最新のバージョンを利用する**ことを表しているところに着目してください。名前に「3_2Core」と付いているので、OpenGL 3.2 (Core Profile)に限定しているように見えますが、実際にはこれで最新のバージョン（筆者の環境だとOpenGL 4.1）が使用されるようになります。

なお、**作成完了直後のタイミングで`setWantsBestResolutionOpenGLSurface:`メソッドを`YES`を引数にして呼び出す**ことによって、**Retinaディスプレイの解像度での描画をサポート**できます。


## 5.2. prepareOpenGLでの処理

`prepareOpenGL`メソッドでは、見慣れた`glClearColor()`関数と`glClear()`関数を組み合わせて、紫色をクリアカラーに指定して画面をクリアしています。画面の描画処理が終わったあとに、OpenGLのコンテキストに対して`flushBuffer`メソッドを呼び出すことで、描画結果が画面に表示されるようになります。

<img width="450" src="https://qiita-image-store.s3.amazonaws.com/0/166309/02f97d81-858c-95e3-6906-57044b422700.png">


# 6. まとめ

今回の記事では、Xcodeの新規プロジェクトの中でNSOpenGLViewクラスのサブクラスを作成して、最新のOpenGLのバージョンを指定してウィンドウ描画ができるようにするところまで解説しました。

ここから数回の解説を通じて、Mac上でOpenGLを利用するための基本を身につけていきましょう。

ここまでのプロジェクト：[MyGLGame_step1-1.zip](http://sazameki.jp/temp/MyGLGame_step1-1.zip)

---

次の記事：[macOSでOpenGLプログラミング（1-2. ウィンドウのリサイズ方法の調整）](http://qiita.com/sazameki/items/86d6f320cebd80ba2f77)