iOSでとりあえずOpenGLで三角形を描くには

内容は[GLKit　ー　ハローOpenGL 三角形を書いてみる](http://lepetit-prince.net/ios/?p=402)にコメントを足しただけのものです。

![triangle.png](https://qiita-image-store.s3.amazonaws.com/0/12939/fcac11a0-cea4-25e8-5d57-cdd0ed5910d3.png)

基本的に以下のような流れになります。

頂点情報→頂点処理（バーテックス・シェーダ）→描画→面処理（フラグメント・シェーダ）→最終出力

今回は頂点処理も面処理も行っていません。

1. まず一画面アプリケーションを作成する。

2. ターゲットに **OpenGLES.framework** と **GLKit.framework** を追加する。

3. コードを以下のように編集してxibを排除する。

```objc:AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[ViewController alloc] initWithNibName:nil bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}
```

4. 次に **ViewController.h** を以下のように編集する。

```objc:ViewController.h
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>

// 自身はGLKViewControllerとなり、self.viewがGLKViewとなる。
// GLKViewにはglkView:drawInRect:メッセージが自動的に刺さってくるので、
// <GLKViewDelegate>としている。
@interface ViewController : GLKViewController <GLKViewDelegate>
{
    GLuint vertexBufferID;
}
@property (strong, nonatomic) GLKBaseEffect *baseEffect;

@end
```

5. 次に **ViewController.m** を以下のように編集する。

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

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

@implementation ViewController
@synthesize baseEffect;

typedef struct {
    GLKVector3 position;
} Vertex;

//　三角の座標
// verticesはvertex（頂点）の複数形
static const Vertex vertices[] =
{
    {{-0.5f, -0.5f,  0.0}},
    {{ 0.5f, -0.5f,  0.0}},
    {{-0.5f,  0.5f,  0.0}}
};

- (void)viewDidLoad
{
    [super viewDidLoad];
    GLKView *view = (GLKView*)self.view;
    
    // OpenGL ES2を指定
    view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    
    // set context
    [EAGLContext setCurrentContext:view.context];
    
    // 三角形に白を設定
    self.baseEffect = [[GLKBaseEffect alloc] init];
    self.baseEffect.useConstantColor = GL_TRUE;
    self.baseEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 1.0f, 1.0f);
    
    // 透明部分（背景）を黒に
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    /*
     ここからあとはGPUに頂点情報（vertices）を送っている
     */
    // GPUに点の情報を
    // vertexBufferIDという頂点バッファ・オブジェクト・ポインタを作成する
    glGenBuffers(1, &vertexBufferID);
    
    // バッファオブジェクトを有効にします
    // 頂点バッファオブジェクトの場合, 第1引数target には GL_ARRAY_BUFFER を指定します
    glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
    
    // バッファオブジェクトのメモリを確保し, そこにデータを転送します
    // 第1引数にはglBindBuffer() と同じものを指定します
    // 第2引数sizeには確保するメモリのサイズを byte で指定します
    // 第3引数dataは転送元のデータの配列を指定します
    // 第4引数usageにはバッファオブジェクトのメモリの使われ方のヒントを指定します
    // STATIC: メモリには一度しか書き込まれないが, 何回も使われる
    // DRAW: メモリの内容はアプリケーションプログラムから書き込まれ, OpenGL による描画データとして使用される
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
}

#pragma mark - GLKView delegate
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    [self.baseEffect prepareToDraw];
    
    // 画面クリア
    glClear(GL_COLOR_BUFFER_BIT);
    
    // 頂点情報の位置を、頂点処理の変数に指定する（これを用いて描画を行う）
    // Attributeは位置、色、テクスチャなどをひっくるめた「頂点の属性」
    //    typedef enum {     GLKVertexAttribPosition,
    //        GLKVertexAttribNormal,
    //        GLKVertexAttribColor,
    //        GLKVertexAttribTexCoord0,
    //        GLKVertexAttribTexCoord1,
    //    } GLKVertexAttrib;
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    
    // 頂点情報の格納場所と書式を頂点処理に教える
    // (void)glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer);
    // index には attribute 変数に割り当てられた index を指定する（ここでは位置）
    // size はデータの１頂点あたりの要素の数で, 1〜4 を指定する。２次元座標なら 2。
    // type には格納されているデータの形式を指定する
    // normalized はノーマライズされているか否か
    // stride には格納されているデータの間隔を byte で指定する。
    // 0 を指定したときは, データは密に並んでいるとみなされる。
    // pointer はバッファの先頭から最初の属性へのバイトオフセット。
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
    
    // 図形を描く
    // (void)glDrawArrays(GLenum mode, GLint first, GLsizei count);
    // mode には描画する基本図形の種類を指定する
    // first には描画するデータの, 格納場所の先頭からの位置を指定する
    // count には描画するデータの数を指定する
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    
    // current contextのbufferを消す
    GLKView *view = (GLKView *)self.view;
    [EAGLContext setCurrentContext:view.context];
    if (0 != vertexBufferID) {
        glDeleteBuffers(1, &vertexBufferID);
    }
    
    // contextを消す
    view.context = nil;
    [EAGLContext setCurrentContext:nil];
}


@end
```

-----
ブログやってます：[PAPA-tronix !](http://weed.cocolog-nifty.com/wzero3es/)