文書の過去の版を表示しています。
OpenGLの修行 #4 - ビルド環境の見直し
- 第3回からおよそ1年半の期間を経て再開する。
 - 環境の準備を実演するために、Windows(Vista、VirtualBox)でやってきた。
 - 次回からはLinuxでやっていく。
 - その前に、現在の雑なビルド環境に改善を加えておく。
 
メイン目標
CMakeLists.txtに、GLFWのインクルードパスとライブラリパスがベタ書きされているのを直したい。
メニュー
- (おまけ) GLFWと動的にリンクする (DLLを利用する)
 - GLFWをソースからビルドしてインストールする
 - システムの任意の場所にインストールされたGLFWを利用する
 - (おまけ) GLADをアプリケーションのソースから分離する
 
(おまけ) GLFWと動的にリンクする (DLLを利用する)
GLFWのWindows向けコンパイル済みバイナリには、DLLも含まれている。
GLFWをソースからビルドしてインストールする
インストール場所はCMAKE_INSTALL_PREFIXで指定する。
例:
$ cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:/somewhere ..
システムの任意の場所にインストールされたGLFWを利用する
find_packageの検索パスはCMAKE_PREFIX_PATHで指定する。
例:
$ cmake -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH=C:/somewhere ..
(おまけ) GLADをアプリケーションのソースから分離する
ソースコード
プロジェクトレイアウト
HelloTriangle_2
├── CMakeLists.txt
├── glad
│   ├── CMakeLists.txt
│   ├── include
│   │   ├── KHR
│   │   │   └── khrplatform.h
│   │   └── glad
│   │       └── glad.h
│   └── src
│       └── glad.c
├── shaders
│   ├── hello_triangle.frag
│   └── hello_triangle.vert
└── src
    └── main.cpp
CMakeLists.txt
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
project(HelloTriangle VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
find_package(glfw3 REQUIRED)
add_subdirectory(glad)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} glad glfw)
glad/CMakeLists.txt
enable_language(C)
add_library(glad OBJECT src/glad.c)
target_include_directories(
    glad
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
    )
glad/*
shaders/hello_triangle.frag
#version 410 core
in vec4 vert_color;
out vec4 color;
void main(void) {
    // color = vec4(1.0, 0.0, 0.0, 1.0);
    color = vert_color;
}
shaders/hello_triangle.vert
#version 410 core
out vec4 vert_color;
void main(void) {
    const vec4 positions[3] = vec4[3](vec4(-0.5, -0.5, 1.0, 1.0),
                                      vec4( 0.5, -0.5, 1.0, 1.0),
                                      vec4( 0.5,  0.5, 1.0, 1.0));
    gl_Position = positions[gl_VertexID];
    const vec4 colors[3] = vec4[3](vec4(1.0, 0.0, 0.0, 1.0),
                                   vec4(0.0, 1.0, 0.0, 1.0),
                                   vec4(0.0, 0.0, 1.0, 1.0));
    vert_color = colors[gl_VertexID];
}
src/main.cpp
#include <glad/glad.h>
// glad.h must be included before glfw3.h
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <optional>
#include <vector>
namespace fs = std::filesystem;
std::optional<std::string> read_to_string(fs::path const &path) {
    if (std::ifstream ifs{path}; ifs) {
        return std::string(std::istreambuf_iterator<char>{ifs}, {});
    } else {
        return std::nullopt;
    }
}
void print_shader_compilation_errors(GLuint shader, fs::path const &path) {
    GLint log_length;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
    if (log_length > 0) {
        std::vector<GLchar> log(log_length);
        glGetShaderInfoLog(shader, log.size(), &log_length, log.data());
        std::cerr << path.string() << ':' << log.data() << '\n';
    }
}
std::optional<GLuint> compile_shader(fs::path const &path, GLenum type) {
    if (auto shader_source = read_to_string(path); shader_source) {
        GLuint shader = glCreateShader(type);
        GLchar const *srcs[] = { shader_source->c_str() };
        glShaderSource(shader, 1, srcs, nullptr);
        glCompileShader(shader);
        
        GLint success;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (success) {
            return shader;
        } else {
            print_shader_compilation_errors(shader, path);
            return std::nullopt;
        }
    } else {
        std::cerr << "failed read file: " << path << '\n';
        return std::nullopt;
    }
}
std::optional<GLuint> link_shaders(GLuint vert, GLuint frag) {
    GLuint program = glCreateProgram();
    glAttachShader(program, vert);
    glAttachShader(program, frag);
    glLinkProgram(program);
    
    GLint success;
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (success) {
        return program;
    } else {
        return std::nullopt;
    }
}
std::optional<GLuint> shader_program_setup() {
    auto vert = compile_shader("shaders/hello_triangle.vert", GL_VERTEX_SHADER);
    auto frag = compile_shader("shaders/hello_triangle.frag", GL_FRAGMENT_SHADER);
    
    auto program = vert && frag ?
        link_shaders(*vert, *frag) : std::nullopt;
    
    if (vert) {
        glDeleteShader(*vert);
    }
    if (frag) {
        glDeleteShader(*frag);
    }
    
    return program;
}
int main() {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    
    GLFWwindow *window = glfwCreateWindow(800, 600, "Hello Triangle!", nullptr, nullptr);
    if (!window) {
        std::cerr << "GLFW failed to create window\n" << std::endl;
        return 1;
    }
    
    glfwMakeContextCurrent(window);
    
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
        std::cerr << "glad failed to initialize OpenGL context\n";
        return 1;
    }
    
    auto *ver_gl = glGetString(GL_VERSION);
    std::cout << "OpenGL version: " << ver_gl << '\n';
    
    auto shader_program = shader_program_setup();
    if (!shader_program) {
        std::cerr << "failed to setup shader program\n";
        return 1;
    }
    
    GLuint VAO;
    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);
    
    while (!glfwWindowShouldClose(window)) {
        glClearColor(0.0, 0.0, 1.0, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        glUseProgram(*shader_program);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    
    std::cerr << "Program finished successfully\n";
}
