====== OpenGLの修行 #4 - ビルド環境の見直し ======
[[https://www.youtube.com/watch?v=NCKH1Hma55E|YouTubeの動画ページ]]
* 第3回からおよそ1年半の期間を経て再開する。
* 環境の準備を実演するために、Windows(Vista、VirtualBox)でやってきた。
* 次回からはLinuxでやっていく。
* その前に、現在の雑なビルド環境に改善を加えておく。
===== メイン目標 =====
CMakeLists.txtに、GLFWのインクルードパスとライブラリパスがベタ書きされているのを直したい。
===== メニュー =====
* (おまけ) GLFWと動的にリンクする (DLLを利用する)
* GLFWをソースからビルドしてインストールする
* システムの任意の場所にインストールされたGLFWを利用する
* (おまけ) GLADをアプリケーションのソースから分離する
===== (おまけ) GLFWと動的にリンクする (DLLを利用する) =====
GLFWのWindows向けコンパイル済みバイナリには、DLLも含まれている。
===== GLFWをソースからビルドしてインストールする =====
インストール場所は[[https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html|CMAKE_INSTALL_PREFIX]]で指定する。
例:
$ cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=C:/somewhere ..
===== システムの任意の場所にインストールされたGLFWを利用する =====
[[https://cmake.org/cmake/help/latest/command/find_package.html|find_package]]の検索パスは[[https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html|CMAKE_PREFIX_PATH]]で指定する。
例:
$ cmake -G "MinGW Makefiles" -DCMAKE_PREFIX_PATH=C:/somewhere ..
===== (おまけ) GLADをアプリケーションのソースから分離する =====
[[https://cmake.org/cmake/help/latest/command/add_subdirectory.html|add_subdirectory]]
====== ソースコード ======
{{ :youtube:hellotriangle_2.zip |ダウンロード}}
===== プロジェクトレイアウト =====
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/* =====
[[https://glad.dav1d.de/#language=c&specification=gl&api=gl%3D4.1&api=gles1%3Dnone&api=gles2%3Dnone&api=glsc2%3Dnone&profile=core&loader=on|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.h must be included before glfw3.h
#include
#include
#include
#include
#include
#include
#include
#include
namespace fs = std::filesystem;
std::optional read_to_string(fs::path const &path) {
if (std::ifstream ifs{path}; ifs) {
return std::string(std::istreambuf_iterator{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 log(log_length);
glGetShaderInfoLog(shader, log.size(), &log_length, log.data());
std::cerr << path.string() << ':' << log.data() << '\n';
}
}
std::optional 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 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 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";
}