🖥️

glmを使ってみた

に公開

はじめて、glmを使ってみました。
機能に圧倒されました・・・
というわけで、OpenGL1.0にあった、glPushMatrix, glPopMatrixもどきを作ってみたいと思います。

では、早速サンプルコード


static const char* vprog = R"(
#version 400 core
layout(location = 0) in vec3 position;

uniform mat4 projection; // 射影行列
uniform mat4 view;       // ビュー行列
uniform mat4 model;      // モデル行列

void main() {
    gl_Position = projection * view * model * vec4(position, 1.0);
}
)";

static const char* fprog = R"(
#version 400 core
out vec4 FragColor;

void main() {
    FragColor = vec4(1.0, 0.5, 0.2, 1.0); // オレンジ色
}
)";

#define GL_GLEXT_PROTOTYPES
/*このシンボルを定義すると、glext.h(OpenGLの拡張機能を宣言するヘッダファイル)に含まれている関数プロトタイプが有効になります。これにより、拡張されたOpenGL関数(例えばglCreateProgramなど)が自動的に利用可能となります。
この方法は特にLinuxやUnixベースのシステムで、GLEWのようなライブラリを使用せずに直接OpenGL関数を利用したい場合に便利です。ただし、GL_GLEXT_PROTOTYPESを定義すると、拡張機能の関数ポインタを手動で取得する必要がなくなる反面、ポータビリティに制約が生じる可能性があります。Windowsのような環境では、拡張機能の関数ポインタを取得する方法を採用することが一般的です。
*/

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

#include <iostream>
#include <vector>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>

class MatrixContext {
private:
    std::vector<glm::mat4> m_stack;
    glm::mat4 m_current = glm::mat4(1.0f); // 単位行列で初期化

public:
    static MatrixContext& instance() {
	static MatrixContext singleton;
	return singleton;
    }
    
    void push() {
	m_stack.push_back(m_current);
    }
    
    void pop() {
	if (!m_stack.empty()) {
	    m_current = m_stack.back();
	    m_stack.pop_back();
	} else {
	    std::cerr << "Matrix stack is empty! Cannot pop.\n";
	}
    }

    void identity() {
	m_stack.clear();
	m_current = glm::mat4(1.0f);
    }
    
    void apply(const glm::mat4& m) {
	m_current = m * m_current;
    }
    
    glm::mat4& current() { return m_current; }

    void translate(float x, float y, float z) {
	glm::mat4 m = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z));
	apply(m);    
    }

    void rotate(float degree, float ux, float uy, float uz) {
	glm::mat4 m = glm::rotate(glm::mat4(1.0f), glm::radians(degree), glm::vec3(ux, uy, uz));
	apply(m);
    }
};


// 四角形の頂点データ
float vertices[] = {
    -0.5f, -0.5f, 0.0f, // 左下
     0.5f, -0.5f, 0.0f, // 右下
     0.5f,  0.5f, 0.0f, // 右上
    -0.5f,  0.5f, 0.0f  // 左上
};

// インデックスデータ
unsigned int indices[] = {
    0, 1, 2, // 下三角形
    2, 3, 0  // 上三角形
};

int main() {
    // GLFW初期化
    if (!glfwInit()) {
        std::cerr << "Failed to initialize GLFW" << std::endl;
        return -1;
    }

    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Quad", NULL, NULL);
    if (!window) {
        std::cerr << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // GLEW初期化
    if (glewInit() != GLEW_OK) {
        std::cerr << "Failed to initialize GLEW" << std::endl;
        return -1;
    }

    // バッファオブジェクト生成
    unsigned int VAO, VBO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    // バッファ設定
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    // シェーダープログラムの作成とリンク
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    unsigned int shaderProgram = glCreateProgram();

    // バーテックスシェーダーのコンパイル
    const char* vertexShaderSource = vprog;
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    // フラグメントシェーダーのコンパイル
    const char* fragmentShaderSource = fprog;
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    // シェーダープログラムにアタッチ&リンク
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glUseProgram(shaderProgram);

    glViewport(0, 0, 800, 600);
    
    // メインループ
    while (!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT);

	glm::mat4 m;
	GLuint loc;

	// projection
	m = glm::perspective(30.0f, (float)(800.0/600.0), 1.0f, 100.0f);
	loc = glGetUniformLocation(shaderProgram, "projection");
	glUniformMatrix4fv(loc, 1, GL_FALSE, &m[0][0]);

	// view (camera)
	m = glm::lookAt(glm::vec3(0, 0, 3),
			 glm::vec3(0, 0, 0),
			 glm::vec3(0, 1, 0));
	loc = glGetUniformLocation(shaderProgram, "view");
	glUniformMatrix4fv(loc, 1, GL_FALSE, &m[0][0]);

	// model
    // ちなみに以下のidentityをコメントアウトすると、current行列がクリアされないので、
    // 毎回行列が再計算(20度ずつ回転)され、結果として矩形が回転し続けます。
	MatrixContext::instance().identity();
	MatrixContext::instance().rotate(20.0f, 0.0f, 1.0f, 0.0f);
	m = MatrixContext::instance().current();
	loc = glGetUniformLocation(shaderProgram, "model");
	glUniformMatrix4fv(loc, 1, GL_FALSE, &m[0][0]);
	
        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // 後処理
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

行列式を印字したい場合は

std::cout << glm::to_string(m) << std::endl;

で表示できるみたいです。

Discussion