OpenGL 2Dの雛形

opengl-2d-template.png
#define _USE_MATH_DEFINES
#include <cmath>
#include <fstream>
#include <cassert>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <sstream>
#include <numeric>
using namespace std;
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <GL/freeglut.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>
 
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <miffy/glfw/glfwlistener.h>
#include <miffy/gl/shaderutility.h>
using namespace miffy;
 
class MiffyOpengl :GLFWListener {
public:
    glm::ivec2 m_win_size;
    glm::mat4 m_proj_matrix;
    struct UniColorShader {
        GLuint id;
        GLuint vao;
        GLuint color;
        GLuint matrix;
    };
    UniColorShader mUnicolorShader;
    GLuint mVaoCoordinate;
    //文字描画
    struct TextShader {
        GLuint id;//シェーダプログラムのID
        GLuint vao;
        GLuint textureID;
        GLuint textureSampler;
        GLuint scale;
        GLuint matrix;
        GLuint uvBeginPos;
        GLuint beginPos;
        GLuint color;
    };
    TextShader mTextShader;
    const float COORDINATE_SIZE = 80;
    glm::vec2 mBitmapFontNum;
    const float DEFAULT_FONT_SIZE = 20;
    MiffyOpengl(void)
        {
    }
    void init(GLFWwindow* _glfw_window) {
        glfwSetWindowUserPointer(_glfw_window,this);
        glEnable(GL_MULTISAMPLE);
        glfwSetScrollCallback(_glfw_window, wheel);
        glfwSetWindowSizeCallback(_glfw_window, miffy::reshape);
        glfwSetMouseButtonCallback(_glfw_window, miffy::mousebutton);
        glfwSetCursorPosCallback(_glfw_window, miffy::mousemove);
        glfwSetKeyCallback(_glfw_window, keyCallback);
 
        InitGeometry();
        initCoordinateVertices(COORDINATE_SIZE);
        InitFonts();
    }
 
    void InitFonts() {
        mTextShader.id = miffy::CreateShaderFromFile("textShader.vert", "textShader.frag");
        glUseProgram(mTextShader.id);
        mTextShader.textureSampler = glGetUniformLocation(mTextShader.id, "uTex");
        mTextShader.scale = glGetUniformLocation(mTextShader.id, "uScale");
        mTextShader.matrix = glGetUniformLocation(mTextShader.id, "uModelViewProj");
        mTextShader.uvBeginPos = glGetUniformLocation(mTextShader.id, "uUvBegin");
        mTextShader.beginPos = glGetUniformLocation(mTextShader.id, "uBeginPosition");
        mTextShader.color = glGetUniformLocation(mTextShader.id, "uColor");
        //ビットマップフォントを読み込む
        cv::Mat image = cv::imread("agencyb.bmp");
        if (image.empty()) assert(!"OpenCV load image error");
        //フォント1個のサイズは64pixel
        mBitmapFontNum.x = image.cols / 64;
        mBitmapFontNum.y = image.rows / 64;
        glGenTextures(1,&mTextShader.textureID);
        glBindTexture(GL_TEXTURE_2D, mTextShader.textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.cols, image.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
        glGenVertexArrays(1, &mTextShader.vao);
        glBindVertexArray(mTextShader.vao);
 
        GLuint vbo;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        //16行x8列のビットマップフォントなので
        static const GLfloat quad_data[] =
        {
                         0.0f, 0.0f,
            DEFAULT_FONT_SIZE,0.0,
            DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE,
                         0.0f, DEFAULT_FONT_SIZE,
            //テクスチャ座標
            0.0f                  , 1.0 / mBitmapFontNum.y,
            1.0/ mBitmapFontNum.x , 1.0 / mBitmapFontNum.y,
            1.0/ mBitmapFontNum.x , 0.0f,
            0.0f                  , 0.0f
        };
 
        glBufferData(GL_ARRAY_BUFFER, sizeof(quad_data), quad_data, GL_STATIC_DRAW);
 
        int pos = glGetAttribLocation(mTextShader.id, "inPosition");
        int uv = glGetAttribLocation(mTextShader.id, "inUV");
 
        glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 0, 0);//位置
        glVertexAttribPointer(uv, 2, GL_FLOAT, GL_FALSE, 0, (const void*)(8 * sizeof(float)));//テクスチャ座標
        glEnableVertexAttribArray(pos);
        glEnableVertexAttribArray(uv);
        glUseProgram(0);
 
    }
    void InitGeometry() {
        mUnicolorShader.id = miffy::CreateShaderFromFile("shader.vert", "uniColorShader.frag");
        mUnicolorShader.matrix = glGetUniformLocation(mUnicolorShader.id, "uModelViewProj");
        mUnicolorShader.color  = glGetUniformLocation(mUnicolorShader.id, "uColor");
        glUseProgram(mUnicolorShader.id);
        glGenVertexArrays(1, &mUnicolorShader.vao);//新しいvaoが作られ、名前が割り振られる。
        glBindVertexArray(mUnicolorShader.vao);//vaoがアクティブになる
 
        //triangle
        float points[] = {
            0.0f,  50.0f,
            50.0f, -50.0f,
            -50.0f, -50.0f,
        };
        GLuint vbo = 0;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
 
        glUseProgram(mUnicolorShader.id);
        int vploc = glGetAttribLocation(mUnicolorShader.id, "vp");
        glEnableVertexAttribArray(vploc);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
    }
    void initCoordinateVertices(float _length) {
        glUseProgram(mUnicolorShader.id);
        float points[] = {
            -_length, 0.0,//x
            _length, 0.0,//x
            0.0, -_length,//y
            0.0, _length,//y
        };
        GLuint vbo = 0;
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
 
        glGenVertexArrays(1, &mVaoCoordinate);
        glBindVertexArray(mVaoCoordinate);
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER,vbo);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
 
        glBindVertexArray(mVaoCoordinate);
        glUseProgram(0);
    }
    glm::vec2 calcuv(char c) {
        char target_char = c - 32;//bitmap fontの表が32スタートだから
        float u = (target_char % 8) / mBitmapFontNum.x;
        float v = (target_char / 8) / mBitmapFontNum.y;
        return glm::vec2(u, v);
    }
    /**文字で情報を描く*/
    void drawText(const string message, const glm::vec4& color, const glm::vec2& _pos, const glm::mat4& _matrix, float _scale) {
        glUseProgram(mTextShader.id);
        //文字を描画する
        glBindVertexArray(mTextShader.vao);
        glBindTexture(GL_TEXTURE_2D, mTextShader.textureID);
 
        glUniform1i(mTextShader.textureSampler, 0);
        //文字のサイズを指定する
        glUniform2f(mTextShader.scale, _scale, _scale);
 
        glUniformMatrix4fv(mTextShader.matrix, 1, GL_FALSE, glm::value_ptr(_matrix));
        glUniform4f(mTextShader.color, color.r, color.g, color.b, color.a);
        const char* cpt = message.c_str();
        float between = DEFAULT_FONT_SIZE*_scale *0.8;
        glm::vec2 pos = _pos;
        while (*cpt != '\0') {
            glm::vec2 uv = calcuv(*cpt);
            glUniform2f(mTextShader.uvBeginPos, uv.x, uv.y);
            glUniform2f(mTextShader.beginPos, pos.x, pos.y);
            glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
            cpt++;
            pos.x += between;
        }
 
        glUseProgram(0);
    }
    //座標軸を描く
    void DrawCoordinate() {
        glUseProgram(mUnicolorShader.id);
 
        glUniformMatrix4fv(mUnicolorShader.matrix, 1, GL_FALSE, &m_proj_matrix[0][0]);
 
        glm::vec4 color_x = glm::vec4(255.0 / 255.0f, 76.0 / 255.0, 101.0 / 255.0, 1.0);
        glm::vec4 color_y = glm::vec4(244.0 / 255.0f, 154.0 / 255.0, 0.0, 1.0);
 
        glBindVertexArray(mVaoCoordinate);
        glUniform4f(mUnicolorShader.color,color_x.r, color_x.g, color_x.b, color_x.a);
        glDrawArrays(GL_LINES, 0,2);
        glUniform4f(mUnicolorShader.color, color_y.r, color_y.g, color_y.b, color_y.a);
        glDrawArrays(GL_LINES,2,2);
 
        drawText("x", color_x, glm::vec2(COORDINATE_SIZE, 0.0), m_proj_matrix, 1.0);
        drawText("y", color_y, glm::vec2(0.0, COORDINATE_SIZE), m_proj_matrix, 1.0);
        drawText("-x", color_x, glm::vec2(-COORDINATE_SIZE, 0.0), m_proj_matrix, 1.0);
        drawText("-y", color_y, glm::vec2(0.0,-COORDINATE_SIZE), m_proj_matrix, 1.0);
    }
    void DrawModel() {
        glUseProgram(mUnicolorShader.id);
        glUniformMatrix4fv(mUnicolorShader.matrix, 1, GL_FALSE, &m_proj_matrix[0][0]);
        glUniform4f(mUnicolorShader.color,165.0/255.0f,217.0/255.0,221.0/255,0.8f);
        glBindVertexArray(mUnicolorShader.vao);
        // draw points 0-3 from the currently bound VAO with current in-use shader
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glUseProgram(0);
    }
    static void GetGLError(const char* op) {
        for (GLint error = glGetError(); error; error = glGetError()) {
            printf("after %s() glErrorCode:%x=%s\n", op, error, gluErrorString(error));
            assert(!"!glError\n");
        }
    }
    void display() override {
        glClearColor(0.3, 0.3, 0.3, 0);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        DrawModel();
        DrawCoordinate();
        glDisable(GL_BLEND);
        glDisable(GL_LINE_SMOOTH);
    }
 
    /**
     * @brief 現在の画面の状態をキャプチャしてpngに保存する
     */
    static void capture(GLFWwindow *window)
    {
        int width, height;
        glfwGetWindowSize(window, &width, &height);
 
        cv::Mat cvmtx(cv::Size(width, height), CV_8UC4, cv::Scalar(0, 0, 0));//黒で初期化
                                                                             // 画像のキャプチャ
        glReadBuffer(GL_FRONT);// フロントを読み込む様に設定する
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 初期値は4
        glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, cvmtx.data);
        assert(cvmtx.data != NULL());
        unsigned char pixel[3];
        glReadPixels(0, 0, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel);
        //上下逆にする
        cv::flip(cvmtx, cvmtx, 0);
        /* 画像の書き出し */
        cv::imwrite("output.png", cvmtx);
    }
    void reshape(int _width, int _height)override {
        m_win_size.x = _width;
        m_win_size.y = _height;
        glViewport(0, 0, (GLsizei)m_win_size.x, (GLsizei)m_win_size.y);
        m_proj_matrix = glm::ortho(-_width / 2.0, _width / 2.0, -_height / 2.0, _height / 2.0);
    }
 
    void mousemove( double _mx, double _my)override {
 
    }
    void mousebutton(int _button, int _action, int _mods)override {
    }
    void  key(GLFWwindow *window, int key, int scancode, int action, int mods)override
    {
        if (action == GLFW_PRESS) {
            switch (key)
            {
            case GLFW_KEY_ESCAPE:// ESCキーでウィンドウのクローズフラグを設定。
                                 // Sets the close flag of the specified window.
                glfwSetWindowShouldClose(window, GL_TRUE);
                break;
            case GLFW_KEY_C:
                capture(window);
                break;
            }
        }
    }
    void zoom(int _direction)override {
    }
 
};
MiffyOpengl opengl;
void glfwErrorCallBack(int error,const char* description) {
    cout << "GLFW error code=" << error << " description:" << description << endl;
    assert(!"GLFW error");
}
static void __stdcall  debugCallbackFunc(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* user_param)
{
    if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) {
        return;
    }
    cout << "OpenGL debug message" << endl;
    cout << "source:" << source << endl;
    cout << "type:" << type << endl;
    cout << "id:" << id << endl;
    cout << "severity:0x" << std::hex <<severity << endl;
    cout << message << endl;
    if (severity == GL_DEBUG_SEVERITY_HIGH) {
        assert(!"!GL_DEBUG_SEVERITY_HIGH OpenGLError\n");
    }
}
int main(int argc, char **argv)
{
 
    glutInit(&argc, argv);
    glfwSetErrorCallback(glfwErrorCallBack);
    glfwInit();
 
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_RED_BITS, 8);
    glfwWindowHint(GLFW_GREEN_BITS, 8);
    glfwWindowHint(GLFW_BLUE_BITS, 8);
    glfwWindowHint(GLFW_ALPHA_BITS, 8);
    GLFWwindow* window = glfwCreateWindow(200, 200, "Hello OpenGL4", nullptr, nullptr);
 
    glfwMakeContextCurrent(window);
    glewInit();
    cout << "Vendor :" << glGetString(GL_VENDOR) << '\n';
    cout << "GPU : " << glGetString(GL_RENDERER) << '\n';
    const GLubyte* version_str = glGetString(GL_VERSION);
    cout << "OpenGL ver. " << version_str << '\n';
    int major_version = version_str[0] - 48;
    int minor_version = version_str[2] - 48;
    if (major_version >= 4 && minor_version >= 3) {
        glDebugMessageCallback(debugCallbackFunc, NULL);
    }
 
    opengl.init(window);
 
    while (!glfwWindowShouldClose(window))
    {
        //reshapeのコールバックが効かなかったので暫定対応
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);
        opengl.reshape(width,height);
        opengl.display();
        GetGLError("end frame");
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
 
    glfwTerminate();
    return 0;
}

サポートサイト Wikidot.com