グレインの備忘録

プログラミング関係とかをつらつらと。

補助ライブラリでサクッとOpenGL(GLFW,GLEW)

OpenGLはプラットフォーム独立のライブラリだが、ウィンドウ管理などの面倒は見てくれない。

また、シェーダーを使うのには拡張機能を有効化する必要がある。

ウィンドウ管理や拡張機能の取得はプラットフォームごとに違うので、生のGLを扱うとそれなりに労力がかかる。

そこで、補助ライブラリを使ってサクッとこの問題を解決する。

1.入力・ウィンドウ管理:GLFW

GLFWはOpenGLの補助ライブラリで、

  • ウィンドウ管理や入力デバイスの制御を担当
  • 出来る限りシンプルな機能構成(Teapotや文字出力なし)
  • 2017年8月現在もメンテナンスが続いている

といった特徴がある。

導入

linuxならaptで入れる。

sudo apt -y install libglfw3-dev

Windowsならバイナリを直接入手できるらしい。 VSならNuGetもアリ。

2.拡張機能管理:GLEW

GLEWはGLFWと名前が似ていて紛らわしいが、

という感じである。

ちなみにOpenGL拡張機能取得はそれ自体が結構面倒なので、それを簡略化してくれるGLEWはマルチプラットフォームを視野に入れていなくても十分有用である。

導入

こいつもaptで可。

sudo apt -y install libglew-dev

Windowsの場合はGLFWと同様バイナリを取ってくる。 やはりこいつもNuGetパッケージがある。


とりあえず初期化だけやってみる

C++で書くとこんな感じか。

#include <iostream>
#include <string>
#include <GL/glew.h>
#include <GLFW/glfw3.h>

class LogManager
{
public:
    LogManager(){};
    //Info out
    void LOGI(std::string msg)
    {
        std::cout << msg << std::endl;
        
    }

    //Error out
    void LOGE(std::string msg)
    {
        std::cerr << msg << std::endl;
    }
};

class App
{
public:
    App(LogManager *log) : m_log(log) {};
    bool run()
    {
        if(!init())
            return false;

        while(glfwWindowShouldClose(m_win) == GL_FALSE)
        {
            glClear(GL_COLOR_BUFFER_BIT);

            glfwSwapBuffers(m_win);
            glfwWaitEvents();
        }

        terminate();

        return true;
    }; 

private:
    LogManager *m_log;
    GLFWwindow *m_win;

    bool init()
    { 
        if(!init_glfw())
        {
            m_log->LOGE("Failed to initialize GLFW.");
            terminate();
            return false;
        }
        m_log->LOGI("GLFW Initialized.");

        if(!init_window(640, 480, "SAMPLE"))
        {
            m_log->LOGE("Failed to initialize window.");
            terminate();
            return false;
        }
        m_log->LOGI("Window Initialized.");

        //必ずGLEW初期化より前
        glfwMakeContextCurrent(m_win);
        
        if(!init_glew())
        {
            m_log->LOGE("Failed to initialize GLEW.");
            terminate();
            return false;
        }
        m_log->LOGI("GLEW Initialized.");


        glClearColor(1.0f, 1.0f, 1.0f, 0.0f);

        return true;
    }
    
    void terminate()
    {
        glfwTerminate();
    }

    bool init_glfw(void)
    {
        if(glfwInit() == GL_FALSE)
            return false;

        return true;
    }

    bool init_window(int width, int height, std::string title)
    {
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

        m_win = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
        
        if(m_win == nullptr)
            return false;

        return true;
    }

    bool init_glew(void)
    {
        GLenum err;
        err = glewInit();
        if(err != GLEW_OK)
        {
            m_log->LOGE(
                    "GLEW Error :"
                    + std::string((char*)glewGetErrorString(err))
            );
            return false;
        }

        return true;
    }
};

static LogManager s_logman;

int main(void)
{
    App app(&s_logman);
    
    return app.run() ? 0 : -1;
}

初期化の順としては

  1. GLFW初期化
  2. GLFWでウィンドウ初期化
  3. GLFWでコンテキスト有効化(glfwMakeContextCurrent)
  4. GLEW初期化

となる。

特に、GLEWの初期化にはコンテキストが有効である必要があるので注意する。

以降は、App.run()のようにループを組み、自由にOpenGLを利用する。

追記:コンパイルオプション

ライブラリ名の指定が迷いそうになるが、

$(LDFLAGS) = -lglfw -lGLEW -lGL

でOK。