Welcome to Mashykom WebSite



OpenGL によるコンピュータ・グラフィックス入門


 OpenGL(Open Graphics Library)はオープンソースのグラフィックスAPI(アプリケーションプログラミングインターフェイス)で、グラフィックスハードウェア向けの2次元/3次元コンピュータグラフィックスライブラリです。OpenGLは、Linux、FreeBSDなどのPC UNIXに加え、Windows、macOS等で使用できるクロスプラットフォームなグラフィックスAPIです。また、スマホ、タブレットなどへの組み込み用途向けのバージョンであるOpenGL ES (OpenGL for Embedded Systems) 規格も存在します。2006年9月21日以降からは、100以上の企業で構成される標準化団体クロノス・グループ (The Khronos Group) へ管理が移行し、OpenGL ARB Working Group (OpenGL ARB WG) となった。現在では、OpenGL ESから派生したWebGL というHTML5 に対応した3Dグラフィックス API も開発されています。

 コンピュータのビデオカードなどに専用のグラフィックスチップ(GPU)が搭載されている場合には、その機能を直接呼び出すことができるため、通常のソフトウェアのようにCPUだけで処理する場合に比べ格段に高速に処理することができます。GPU内部の動作を細かく記述することができる独自の言語GLSL(OpenGL Shading Language)を規定しており、開発者が独自のプログラマブルシェーダを作成して対応GPUに転送することで様々な表現を行うことができます。Windows, Mac OS, Linux, BSD等の様々なOSの上で利用可能であり、個人でも無料のソフトウェア群のみでGPUの能力を最大限に利用できる3Dプログラミング環境を整備できる。OpenGLそのものはハードウェア寄り(低レベル)のAPIであるため、きめ細かな制御やプログラムの作り込みが可能だが、初心者には取っ付きにくい。

 OpenGLはプログラマが挙動を指示するために使うインターフェースを定義するものです。つまり、OpenGLは単にAPIを規定した"仕様"であり、実際の処理はintelとかNVIDIAとかのハードウェアベンダーが独自に実装することになる。コンピュータ上でグラフィックを描画する作業はOSが管理してる。 ユーザーがプログラムからグラフィックを描画するには、OSに「こんな画面描いて」と指示を出す必要があります。 ところが、Windows、macOS、LinuxなどのOSでは、グラフィックの扱い方に違いがあるため、OSへの指示の出し方がそれぞれ異なります。 OSごとに指示の出し方が違うのは面倒だということで、共通の規格として定められたのがOpenGLです。

 したがって、OpenGLが使えるか、また、どのバージョンが利用できるかは、OSがOpenGLをサポートしているかどうか、また使っているGPUがOpenGLに対応しているかどうかに依存します。 普通のライブラリのように、インターネットからファイルをダウンロードしてくれば最新版が使えるようものではないのです。OpenGLはあくまでレンダリングエンジンのAPIだけを規定したものであり、プログラム実行時にウィンドウを表示したり、そこにOpenGLのコンテキストを紐付けたり、マウスやキーボードの入力を受け付けたりといった機能は入っていません。 これらの機能は自分の環境のウィンドウシステムに合わせて個別に実装しなければなりません。この実装を代わりにやってくれるのが、GLUT、GLFWやGLEWなどのUtility Toolkitです。

 OpenGL version2.0 以前では、リアルタイム3Dコンピューターグラフィックスは、OpenGLのAPIを通してグラフィックスカード上のチップ(GPU)にあらかじめ用意された固定のレンダリングパイプライン上で、固定機能のシェーダー(頂点トランスフォームや陰影計算を専門に担当するユニット)を組み合わせることで実現されていた。OpenGL version2.0以降は、グラフィックスカードの進化・性能向上に伴い、ハードウェア実装による固定機能ではなく、アプリケーション開発者がソフトウェアプログラム(プログラマブルシェーダー)によって頂点レベル・フラグメントレベル(ピクセルレベル)での制御・カスタマイズを行うようになった。OpenGL ARBは、グラフィックス処理を行うプログラミングをより直感的・効率的にできる方法として、OpenGL Shading Languageを作り出した。GLSL (OpenGL Shading Language) はGLslangとしても知られ、C言語をベースとした高レベルシェーディング言語である。現在、GLSLはOpenGLに組み込まれています。

 このページでは、GLFWとGLEWを用いてOpenGLを利用してグラフィックスを作成する初歩的な手法を説明します。プラットフォームとして、Mac OS version 10.12を使います。Mac OS version 10.14からOpenGLが非推奨となっているので、本来ならば、Metalに変更すべきかもしれませんが、このページでは敢えてOpenGLを取り上げます。MacOS 10.14 MojaveにおけるOpen GL /GL ESとOpenCLの扱いは、あくまで非推奨になったというだけで、Mojaveにアップデートしたら即、動作しなくなるというわけではありません。

Last updated: 2019.1.23



*******************************
OpenGLの使い方
*******************************



 上で触れた通り、OpenGL はどのプラットフォームでもGPUに対応したバージョンが標準で組み込まれています。改めてインストールする必要はありません。GPUの情報は本体のシステム情報から、MacOSに組み込まれているOpenGLのバージョンはAppleのサイトを見るとわかります。

 現時点でのMacOS XでインストールされているOpenGLのバージョンは4.1、対応するGLSLのバージョンは4.1です。OpenGLのCoreの部分はOpenGL v3.2以降同一で、拡張機能の部分が異なります。OpenGL v3.2に対応するGLSLはバージョン1.5ですが、OpenGL v3.3 以降はGLSLのバージョン名はOpenGLのバージョンと同じになりました。2010年以降のMacのIntel GPUはOpenGL v3.3以上をサポートしています。OpenGLは後方互換性を持っているので、例えば、OpenGL v3.3がインストールされているMac OSの場合でも、OpenGL v1.0 で書かれたソースファイルを実行できます。

 MacOSではXcodeを使います。XcodeはVisual Studioよりも 機能的には若干劣るものの、よくできた統合開発環境です。XcodeのインストールはApp Storeから行います。最新バージョンは、Xcode10.1ですが、各バージョンは、Appleの公式websiteからダウンロードできます。

 OpenGLを利用するためにGLFW(OpenGL Frameworkの略)というライブラリを使用します。 以下では、MacOSでの開発環境の設定方法について説明します。Homebrewを利用するときは、
$ brew install glfw3

とコマンドを入力します。

 GLFWをソースコードからコンパイルしてインストールする場合には、 GLFWのソースコードをこの公式ページからダウンロードできます。2019年1月でのバージョンは glfw-3.2.1 です。「ターミナル」を開いて、以下のスクリプトを1行1行実行していきます。
$ git clone https://github.com/glfw/glfw.git    # gitHubからソースコードのダウンロードするとき
$ cd glfw                                       # ダウンロードしたソースのディレクトリに移動
$ mkdir build && cd build                       # ビルド用のディレクトリを作成して、そこに移動
$ cmake ..                                      # CMakeを利用したビルドの準備
$ make                                          # ビルド
$ sudo make install                             # インストール

ファイルは /usr/local/include にインストールされます。

 高度な操作でソースコードを動かすためには, 最近のOpenGLの機能を扱うためのライブラリが必要です。 GLEW (OpenGL Extension Wrangler Library) を使ってプログラムを作る必要があります。ベクトルや行列の計算を簡単に扱うためのライブラリとしてGLM (OpenGL Mathematics) も導入します。

 GLEW、GLMともにソースコードのzip ファイルをダウンロードし、GNU Makeあるいは CMakeを使用してビルド、インストールを行います。まず、GLEWの公式Webページ からソースコードのzipファイルを ダウンロードして、ビルドとインストールを行います。ターミナルを開いて、次のコマンドを実行してください。
($ wget https://github.com/nigels-com/glew/releases/download/glew-2.0.0/glew-2.0.0.tgz)
($ tar xvf glew-2.0.0.tgz)
$ cd glew-2.0.0/build
$ cmake ./cmake
$ make
$ sudo make install

ファイルは /usr/local/include にインストールされます。最新バージョンはglew-2.1.0 です。

 次に、GLMの公式ページからソースコードを ダウンロードして、ビルドとインストールを行います。GLEWの時と同様に、 次のコマンドをターミナルで実行してください。
$ git clone https://github.com/g-truc/glm.git
$ cd glm
$ mkdir build && cd build
$ cmake ..
$ make
$ sudo make install

GLMのファイルは /usr/local/include にインストールされます。 glm は C++ の座標系ベクターの取り扱いを中心とした数学系のライブラリーです。基本的には3DCG向けの、特にOpenGLとの親和性の高いグラフィックス用途のライブラリーですが、その機能はよく整理されて実装されており、比較的低次のベクター処理に汎用に扱えるものとなっています。これで必要な環境のインストールは完了です。

 プログラムを作成するために、Xcode の設定を行います。インストールが完了したらXcodeを起動します。起動すると[Welcome to Xcode]という画面が表示されます。「Create a new Xcode Project」をクリックします。

Xcode10_1.png

すると、どのようなプロジェクトを作成するのかを指定する画面が表示されます。「macOS」の「Command Line Tool」を選択して、右下の「Next」をクリックします。

Xcode10_2.jpg


すると、次はプロジェクトの詳細を設定する画面が現れます。ここは、どのように設定しても 基本は問題ないのですが、project name を例えば GLsample にして、一番下の「Language」を「C++」にするのだけは忘れないようにしてください。

 次にXcodeでGLFWなどを使うための設定をします。まずはGLFWのインクルードディレクトリと ライブラリディレクトリの設定です。上記の通りにGLFW、GLEW、GLMをインストールしている場合には、それぞれが以下のようなパスになっています。インクルードディレクトリは
/usr/local/include
ライブラリディレクトリは
/usr/local/lib
です。
Xcodeの画面左側にあるプロジェクト名GLsampleをクリックすると、 設定画面が現れるので、その設定画面の「Build Settings」をクリックして開きます。検索窓で「search」として、この画面を下にスクロールしていくと「Search Paths」という項目が見つかるので、 その中にある"Header Search Paths"および"Library Search Paths"を以下のように編集します。“Header Search Paths”の右側をダブルクリックすると、入力ウインドウがポップアップしますので、“+”をクリックして、" /usr/local/include"と入力します。次に、"Library Search Paths"をクリックして、その右側をバブルクリックする。入力ウインドウがポップアップしますので、“+”をクリックして、" /usr/local/lib"と入力します。  MacOSでGLFWなどを使うためには、いくつかのフレームワーク(ライブラリの集合みたいなもの)をプロジェクトに 追加する必要があります。リンクするライブラリとフレームワークを設定します。検索窓で"linker"を入力して、"Other Linker Flags"という項目を探して、その右側をバブルクリックします。入力ウインドウの下にある"+"をクリックして、以下の内容を入力します。
 
-lglfw3 -lGLEW -framework OpenGL -framework CoreVideo -framework IOKit -framework Cocoa

最初の二つの項目、"-lglfw3 -lGLEW"は必須です。それ以外については、以下のように設定することもできます。 先ほどの設定画面上部で「Build Settings」を選んでいた箇所の一番左にある「General」を選択します。すると、画面下部に「Linked Frameworks and Libraries」という項目があるので、こちらに使用する フレームワークを追加していきます。ここで使用するフレームワークは最小限の4種類で「OpenGL」、「Cocoa」、「CoreVideo」、「IOKit」を追加します。 いずれも「Linked Frameworks and Libraries」項目の下側にある「+」ボタンを押すと入力のポップアップが現れるので、 その画面で検索することにより追加できます。

 最後に、コンパイラーのバージョンを設定します。検索窓に"compiler"と入力して、"C++Language Dialect"を探して、クリックします。“C++11 [-std=c++11]”を選びます。

 Xcodeの場合には、上記のやり方でプロジェクトを作成すると、すでに「main.cpp」という ファイルが作成されています。このファイルをファイルリストから開き、 このファイルのコードを、コピペ等で修正します。ソースコードのコンパイルと実行は簡単です。projectの main.ccp をクリックして、上部にある実行アイコン▶️をクリックします。白色に描画されたウインドウがポップアップします。

 例えば、以下のコードはOpenGL 3.3がインストールされたOSで実行できるソースですが、OpenGL 2.1用のコードで書かれています。つまり、OpenGL 2.1バージョンを使っています。
//
//  main.cpp
//  OpenGL 1.0
// 引用先 https://tatsy.github.io/OpenGLCourseJP/sections/animation/one_cube.html

#include 
#include 

#define GLFW_INCLUDE_GLU  // GLUライブラリを使用するのに必要
#include 

static int WIN_WIDTH   = 500;                       // ウィンドウの幅
static int WIN_HEIGHT  = 500;                       // ウィンドウの高さ
static const char *WIN_TITLE = "OpenGL Course";     // ウィンドウのタイトル
static const double fps = 30.0;                     // FPS

static const double PI = 4.0 * atan(1.0);           // 円周率の定義

static float theta = 0.0f;

static const float positions[8][3] = {
    { -1.0f, -1.0f, -1.0f },
    {  1.0f, -1.0f, -1.0f },
    { -1.0f,  1.0f, -1.0f },
    { -1.0f, -1.0f,  1.0f },
    {  1.0f,  1.0f, -1.0f },
    { -1.0f,  1.0f,  1.0f },
    {  1.0f, -1.0f,  1.0f },
    {  1.0f,  1.0f,  1.0f }
};

static const float colors[6][3] = {
    { 1.0f, 0.0f, 0.0f },  // 赤
    { 0.0f, 1.0f, 0.0f },  // 緑
    { 0.0f, 0.0f, 1.0f },  // 青
    { 1.0f, 1.0f, 0.0f },  // イエロー
    { 0.0f, 1.0f, 1.0f },  // シアン
    { 1.0f, 0.0f, 1.0f },  // マゼンタ
};

static const unsigned int indices[12][3] = {
    { 1, 6, 7 }, { 1, 7, 4 },
    { 2, 5, 7 }, { 2, 7, 4 },
    { 3, 5, 7 }, { 3, 7, 6 },
    { 0, 1, 4 }, { 0, 4, 2 },
    { 0, 1, 6 }, { 0, 6, 3 },
    { 0, 2, 5 }, { 0, 5, 3 }
};

// OpenGLの初期化関数
void initializeGL() {
    // 背景色の設定 (黒)
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    
    // 深度テストの有効化
    glEnable(GL_DEPTH_TEST);
}

// キューブの描画
void drawCube() {
    glBegin(GL_TRIANGLES);
    for (int face = 0; face < 6; face++) {
        glColor3fv(colors[face]);
        for (int i = 0; i < 3; i++) {
            glVertex3fv(positions[indices[face * 2 + 0][i]]);
        }
        
        for (int i = 0; i < 3; i++) {
            glVertex3fv(positions[indices[face * 2 + 1][i]]);
        }
    }
    glEnd();
}

// OpenGLの描画関数
void paintGL() {
    // 背景色と深度値のクリア
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    // ビューポート変換の指定
    glViewport(0, 0, WIN_WIDTH, WIN_HEIGHT);
    
    // 座標の変換
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, (float)WIN_WIDTH / (float)WIN_HEIGHT, 0.1f, 1000.0f);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(3.0f, 4.0f, 5.0f,     // 視点の位置
              0.0f, 0.0f, 0.0f,     // 見ている先
              0.0f, 1.0f, 0.0f);    // 視界の上方向
    
    glRotatef(theta, 0.0f, 1.0f, 0.0f);
    
    // キューブの描画
    drawCube();
}

void resizeGL(GLFWwindow *window, int width, int height) {
    // ユーザ管理のウィンドウサイズを変更
    WIN_WIDTH = width;
    WIN_HEIGHT = height;
    
    // GLFW管理のウィンドウサイズを変更
    glfwSetWindowSize(window, WIN_WIDTH, WIN_HEIGHT);
    
    // 実際のウィンドウサイズ (ピクセル数) を取得
    int renderBufferWidth, renderBufferHeight;
    glfwGetFramebufferSize(window, &renderBufferWidth, &renderBufferHeight);
    
    // ビューポート変換の更新
    glViewport(0, 0, renderBufferWidth, renderBufferHeight);
}

// アニメーションのためのアップデート
void animate() {
    theta += 2.0f * PI / 10.0f;  // 10分の1回転
}

int main(int argc, char **argv) {
    // OpenGLを初期化する
    if (glfwInit() == GL_FALSE) {
        fprintf(stderr, "Initialization failed!\n");
        return 1;
    }
    
    // Windowの作成
    GLFWwindow *window = glfwCreateWindow(WIN_WIDTH, WIN_HEIGHT, WIN_TITLE,
                                          NULL, NULL);
    if (window == NULL) {
        fprintf(stderr, "Window creation failed!");
        glfwTerminate();
        return 1;
    }
    
    // OpenGLの描画対象にWindowを追加
    glfwMakeContextCurrent(window);
    
    // ウィンドウのリサイズを扱う関数の登録
    glfwSetWindowSizeCallback(window, resizeGL);
    
    // OpenGLを初期化
    initializeGL();
    
    // メインループ
    while (glfwWindowShouldClose(window) == GL_FALSE) {
        // 描画
        paintGL();
        
        // アニメーション
        animate();
        
        // 描画用バッファの切り替え
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
}

このコードをXcodeを用いて'run'すると、ウインドウに立方体が回転するCGが表示されます。 このコードでは、シェーダファイルは利用されていませんので、main.cpp ファイル一つだけで済みます。興味のある人はファイルをコピペして、実行して見てください。OpenGL 2.1バージョンを利用する場合、GLUTなどのAPIが使えるので、より簡易にコーディングが可能となります。その代わりに、高度なCGはできません。

 次に、OpenGL 3.3用のコードで書かれたソースをみてみましょう。以下のコードをmain.cppにコピペしてください。

//
//  main.cpp
//  OpenGL 3.3 sample
//

#include 
#include 
#include 
#include 

int main()
{
    // GLFWを初期化する
    if (glfwInit() == GL_FALSE)
    {
        // 初期化に失敗した
        std::cerr << "Can't initialize GLFW" << std::endl;
        return 1;
    }
    
    //プログラム終了時の処理を登録する
    atexit(glfwTerminate);
    //OpenGL Version 3.3 Core Profile を選択する
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL3.xを使います。
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);// OpenGLのマイナーバージョン
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);// MacOS用:必ずしも必要ではありません。
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 古いOpenGLは使いません。
    
  // Windowを開き、OpenGLコンテキストを作ります。ウインドウを作成します。
    GLFWwindow *const window(glfwCreateWindow(640, 480, "OpenGL sample", NULL, NULL));
    if (window == NULL)
    {
        //ウインドウが作成できなかったとき
        std::cerr << "Can't create GLFW window." << std::endl;
        return 1;
    }
    //作成したウインドウをOpenGLの処理対象にする
    glfwMakeContextCurrent(window);
    
    //GLEWを初期化する
    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK)
    {
        // GLEWの初期化に失敗したとき
        std::cerr << "Can't initialize GLEW" << std::endl;
        return 1;
    }
    
    //垂直同期のタイミングを待つ
    glfwSwapInterval(1);
    
    //背景色を指定する
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    
    //ウインドウが開いている間繰り返す
    while (glfwWindowShouldClose(window) == GL_FALSE)
    {
        //ウィンドウを消去する
        glClear(GL_COLOR_BUFFER_BIT);
        //
        //ここで画像処理を行う
        //
        
        //カラーバッファを入れ替える
        glfwSwapBuffers(window);
        
        //イベントを取り出す
        glfwWaitEvents();
    }
}

 projectの main.ccp をクリックして、上部にある実行アイコン▶️をクリックします。白色に描画されたウインドウがポップアップします。このウインドウの中に描画していきます。描画を行うためには、GLSLという言語(シェーダ言語)で書く必要があります。これが結構大変なのです。

 OpenGL 2.0以前では glBegin() で描画する基本図形を指定し, glEnd() までの間で glVertex*() や glNormal*(), glTexCoord*() などで頂点情報を送ることができました。 OpenGL 3.0 以降バージョンにおいても、このコマンドは使用可能ですが、OpenGL Version 3.2 Core Profileを選択すると、つまり、前方互換を指定すると, これらは使えなくなります。代わりに, 図形の描画には頂点配列 (vertex array) や頂点配列オブジェクト (vertex array object, VAO), あるいは頂点バッファオブジェクト (vertex buffer object, VBO) を使用します。描画する基本図形 (primitive) の種類を指定した後, 図形を構成する頂点情報をGPUに送って図形を描画します。

 最も簡単で可能な設定では、2つのシェーダが必要となります。一つは頂点シェーダで、各頂点で実行されます。もう一つはフラグメントシェーダで書くサンプルで実行されます。シェーダファイルの作成とシェーダの実行に関する説明は以下で行います。

 OpenGLのコーデイングを説明した書籍では、床井浩平著『「グラフィックス・アプリ」制作のためのOpenGL入門 』(I・O BOOKS、工学社) がお薦めです。付属のサンプルコードはOpenGL3.2に準拠しています。Xcodeを用いて即実行可能なコードになっています。各stepごとに配置しているファイル'glfw3.xcodeproj'をクリックしてXcodeを立ち上げると、'run'できます。

 OpenGLの使用に関する一般的な説明やサンプルコードは、websiteに多数ありますが、OpenGL 3.3 に対応したopengl-tutorial.orgのサイトにあるサンプルは便利です。このソースコードをダウンロードします。解凍して、適当なディレクトリに展開します。
$ mkdir build && cd build                       # ビルド用のディレクトリを作成して、そこに移動
$ cmake ..                                      # CMakeを利用したビルドの準備
$ make                                          # ビルド

サブホルダーの構造が結構複雑なので、Xcode で実行するのは厄介ですが、この'cmake' でコンパイル済みのファイルが出力、作成されます。ターミナルで、出力ファイルのディレクトリに行って、例えば、(tutorial03_matrices)のディレクトリで
$ ./tutorial03_matrices
と実行するとグラフィックス画像が表示されます。OpenGLでgameを作成するコードの構築に関する説明は、沼田 哲史さんのQuitaのWebsiteが参考になります。ただし、アップされているサンプルコードやそれぞれの説明において、各サイトごとにそれぞれOpenGLのバージョンが異なっていますので、注意が必要です。

ーーーーーーー 続く (未完成です)ーーーーーーー

このページの先頭へ戻る
WebGLのページへ行く
トップ・ページに行く