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
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];
}
shaders/hello_triangle.vert
#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;
}
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";
}
