XcodeとGLFWではじめるOpenGL
2019.05.19
この記事は最終更新日から1年以上が経過しています。
どもです。
前々から 3D まわりの仕組みをちゃんと触ってみたいなと思っていまして、今回は重い腰を上げて OpenGL に入門してみることにしました。
環境は Mac の Xcode、ウィンドウまわりは GLFW を使っています。ということで今回は、Xcode で OpenGL の環境を作って、点・線・三角形と図形を少しずつ描画していくまでをまとめた形になります。
Xcode で OpenGL プロジェクトを作る
まずは Xcode で新規プロジェクトを作成します。メニューの「File > New > Project…」から。

テンプレートの選択画面が出てきます。今回は C で書いていくので macOS の中から該当のテンプレートを選んで進めます。

プロジェクトができたら、main.c に GLFW のライブラリ(libglfw)などを組み込んでいきます。プロジェクトを右クリックして「Add Files to “opengl-test”…」から必要なファイルを追加します。

コード側では、まずこんな感じで GLFW のヘッダや、ファイル・配列を扱うための C++ ライブラリをインクルードしておきます。(#pragma comment によるライブラリ指定は Windows 向けの記述です。)
// ライブラリGLFWを // 「DLL」形式で利用するとコンパイラに教える(Windows) #define GLFW_DLL // 必要なヘッダをインクルード #include <GL/glfw.h> #include // C++でファイルを扱うライブラリ #include // C++で動的配列を扱うライブラリ #include // リンクするライブラリを指示 (Windows) #if defined (_MSC_VER) #pragma comment(lib, "GLFWDLL.lib") #pragma comment(lib, "opengl32.lib") #endif
このあたりのリンク設定(GLFW やフレームワークの読み込み)でつまずくと、描画がうまくいかず、こんな風に表示が乱れてしまうこともありました。。

まずはウィンドウを表示する
環境が整ったら、まずは描画用のウィンドウを出すところから。背景を黒でクリアした、まっさらなウィンドウが表示されれば第一段階クリアです。

コードで見ると、GLFW の初期化とウィンドウ生成はこの部分です。glfwInit で初期化し、glfwOpenWindow でウィンドウを開きます。
// プログラムの実行はmain関数から開始される
int main() {
if(!glfwInit()) {
return EXIT_FAILURE;
}
if (!glfwOpenWindow( 0, 0, 0, 0, 0, 0, 0, 0, GLFW_WINDOW)) {
glfwTerminate();
return EXIT_FAILURE;
}
そして、ウィンドウが開いている間ぐるぐると描画ループを回します。glClearColor で塗りつぶす色(ここでは黒)を指定して、glClear で背景を塗りつぶす、という流れですね。(描画後は glfwSwapBuffers で画面へ反映し、ウィンドウが閉じられたら glfwTerminate で後始末をします。)
// ウインドウが開いている間、ループ内を実行する
while (glfwGetWindowParam(GLFW_OPENED)) {
// 描画バッファを塗り潰す色の成分を それぞれ0.0〜1.0で指定
// glClearColor(赤, 緑, 青, アルファ)
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
点を描画する
さて、ここからが本番。まずは一番シンプルな「点」を1つ描画してみます。ウィンドウの中央にぽつんと白い点が表示されました。地味ですが、ちゃんと描けると胸アツですね。

四角形を描画する
続いて四角形です。頂点を4つ指定して、面を塗りつぶしてみます。まずは白で。

同じ四角形に色を指定してあげると、こんな感じで塗りの色を変えられます。シアンにしてみました。

コードでは、4つの頂点の座標を配列で用意して glVertexPointer で OpenGL に渡しています。各頂点を -0.5〜0.5 の範囲で指定しているのがポイントです。
// 描画する三角形の4頂点を配列で用意
static const GLfloat vtx[] = {
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, 0.5f
};
glVertexPointer(2, GL_FLOAT, 0, vtx);
あとは、この頂点配列を有効にして glDrawArrays(GL_QUADS, 0, 4) を呼べば、4頂点をつないだ四角形が描画されます。
線を描画する
お次は線。2点を指定して、その間を結ぶ直線を引いてみます。

三角形を描画する
そしていよいよ三角形。3つの頂点を指定して、面を描画します。

頂点の座標をいじれば、大きさや位置も自由自在。少し大きめの三角形にしてみました。

頂点ごとに色を付けてみる
最後に、三角形の各頂点にそれぞれ別の色を指定してみます。すると、頂点と頂点の間がいい感じに補間されて、こんなにキレイなグラデーションになります。
上の頂点を赤、左下を緑、右下を青にしてみたのですが、OpenGL が間の色をなめらかに塗ってくれるんですよね。いやぁ、これはなかなかに胸アツ。

色は、頂点ごとに「赤・緑・青」の成分を 0.0〜1.0 で配列に用意してあげます。
// 頂点1つ1つに対する色を配列で用意
// 赤、緑、青の成分をそれぞれ 0.0 ~ 1.0で指定する
static const GLfloat color[] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
};
おまけ:テクスチャを貼ってみる
図形が描けるようになったので、おまけで四角形にテクスチャ(画像)を貼るところまでやってみます。
まずは、画像ファイルを読み込んで OpenGL のテクスチャとして設定する関数を用意します。ファイルをバイナリで読み込んで、glTexImage2D でそのデータを OpenGL へ転送する、という流れです。今回は 256×256 ピクセルの RGBA 画像を前提にしています。
// 指定テクスチャ識別子へ
// ファイルから読み込んだデータを与える
// 処理が成功すると、trueを返す
bool setupTexture(const GLuint id, const char* file) {
// ファイルをバイナリモードで開く
std::ifstream fstr(file, std::ios::binary);
// ファイルが見つからない等のエラーがあれば 処理を中断
if (!fstr) return false;
// ファイルサイズを取得
// 読み込み位置をファイル末尾へ移動
// →ファイル先頭から読み込み位置までオフセット
// =ファイルサイズ
const size_t file_size = static_cast(fstr.seekg(0, fstr.end).tellg());
// 読み込み位置をファイル先頭へ戻す
fstr.seekg(0, fstr.beg);
// 動的配列を使ってファイルを読み込む場所を確保
// charをfile_size個, メモリに確保する
std::vector texture_buffer(file_size);
// 確保した場所へファイルの内容を全て読み込む
fstr.read(&texture_buffer[0], file_size);
// OpenGLに「これから、テクスチャ識別子idに対して指示を与えます」と指示。
glBindTexture(GL_TEXTURE_2D, id);
// 1ピクセルに「赤、緑、青、アルファ」の情報を持つ
// 幅256ピクセル、高さ256ピクセルの画像データをOpenGLへ転送
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texture_buffer[0]);
// 画像が拡大された場合にどう振る舞うか指定
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 画像が縮小された場合にどう振る舞うか指定
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
return true;
}
あとは、描画ループの中で頂点ごとのテクスチャ座標(UV)を指定し、glEnable(GL_TEXTURE_2D) でテクスチャ描画を有効にしてから glDrawArrays(GL_QUADS, 0, 4) を呼べば、四角形に画像が貼られた状態で描画されます。
// 頂点ごとのテクスチャ座標を配列で準備
static const GLfloat textture_uv[] = {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
glTexCoordPointer(2, GL_FLOAT, 0, textture_uv);
// OpenGLにテクスチャによる描画を有効にすると指示
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
// 描画の時にテクスチャ座標配列も使うと指示
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// 短形を1つ描画
glDrawArrays(GL_QUADS, 0, 4);
まとめ
ということで、Xcode + GLFW で OpenGL の環境を作って、点から始めてグラデーションの三角形までを描画してみました。
環境構築まわりは少しだけ手こずりましたが、いざ図形が描けるようになると、座標や色をいじるだけで結果がどんどん変わって、これがまた楽しいんですよね。次はこの三角形を回したり、3D にしていったりと、もう少し踏み込んでいければと思います。
ではではぁ。またまたぁ。



















