====== OpenGLの修行 #5 - 続・三角形を描く (1) ====== {{ https://youtu.be/8sidpTb4mMk |YouTubeの動画ページ}} ===== ソースコード ===== {{ :youtube:hellotriangle-simpleshader.zip |ダウンロード}} ==== プロジェクトレイアウト ==== HelloTriangle-simpleshader ├── .clang-format ├── 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 ├── simpleshader.cpp └── simpleshader.h ==== .clang-format ==== BasedOnStyle: Chromium IndentWidth: 4 PointerAlignment: Right AccessModifierOffset: -2 ConstructorInitializerIndentWidth: 8 ==== 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(Boost REQUIRED CONFIG) include_directories(${Boost_INCLUDE_DIRS}) find_package(glfw3 REQUIRED) add_subdirectory(glad) add_executable( HelloTriangle src/main.cpp src/simpleshader.cpp ) target_link_libraries(HelloTriangle PRIVATE 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 float y = sqrt(3.0) / 2.0 / 2.0; const vec4 vertices[3] = vec4[3](vec4(-0.5, -y, 1.0, 1.0), vec4( 0.5, -y, 1.0, 1.0), vec4( 0.0, y, 1.0, 1.0)); gl_Position = vertices[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 "simpleshader.h" #include #include #include #include #include #include #include namespace fs = std::filesystem; using namespace simpleshader; std::optional setup_shaders() { auto const vert_path = "shaders/hello_triangle.vert"; auto const frag_path = "shaders/hello_triangle.frag"; auto vert_src = read_to_string(vert_path); if (!vert_src) { std::cerr << "Error: failed to read file " << vert_path << '\n'; return {}; } auto frag_src = read_to_string(frag_path); if (!frag_src) { std::cerr << "Error: failed to read file " << frag_path << '\n'; return {}; } ShaderObject vert{GL_VERTEX_SHADER, *vert_src}; if (!vert.ok()) { print_compilation_errors(vert, vert_path); return {}; } ShaderObject frag{GL_FRAGMENT_SHADER, *frag_src}; if (!frag.ok()) { print_compilation_errors(frag, frag_path); return {}; } ShaderProgram program{vert, frag}; if (!program.ok()) { print_link_errors(program); return {}; } 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, 800, "Hello Triangle!", nullptr, nullptr); if (!window) { std::cerr << "Error: GLFW failed to create window\n" << std::endl; glfwTerminate(); std::exit(1); } glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cerr << "Error: glad failed to initialize OpenGL context\n"; glfwTerminate(); std::exit(1); } auto const version = glGetString(GL_VERSION); std::cout << "OpenGL version: " << version << '\n'; auto shader_program = setup_shaders(); if (!shader_program) { std::cerr << "Error: failed to setup shader program\n"; glfwTerminate(); std::exit(1); } GLuint VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); while (!glfwWindowShouldClose(window)) { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shader_program->name()); glDrawArrays(GL_TRIANGLES, 0, 3); glfwSwapBuffers(window); glfwPollEvents(); } shader_program->clear(); glfwTerminate(); std::clog << "Program finished successfully\n"; } ==== src/simpleshader.h ==== #ifndef SIMPLESHADER_H #define SIMPLESHADER_H #include #include #include #include namespace simpleshader { class ShaderObject : boost::noncopyable { public: ShaderObject(GLenum type, std::string const &src); ~ShaderObject(); ShaderObject(ShaderObject &&other) noexcept; ShaderObject &operator=(ShaderObject &&other) noexcept; bool ok() const; GLuint name() const; private: GLenum type_; GLuint name_; }; class ShaderProgram : boost::noncopyable { public: ShaderProgram(ShaderObject const &vert, ShaderObject const &frag); ~ShaderProgram(); ShaderProgram(ShaderProgram &&other) noexcept; ShaderProgram &operator=(ShaderProgram &&other) noexcept; bool ok() const; GLuint name() const; void clear(); private: GLuint name_; }; std::optional read_to_string(std::filesystem::path const &path); void print_compilation_errors(ShaderObject const &shader, std::filesystem::path const &path); void print_link_errors(ShaderProgram const &program); } // namespace simpleshader #endif ==== src/simpleshader.cpp ==== #include "simpleshader.h" #include #include #include #include #include #include namespace fs = std::filesystem; namespace /*anon*/ { bool compile_shader(GLuint name, GLchar const *src) { GLchar const *src_arr[] = {src}; glShaderSource(name, 1, src_arr, nullptr); glCompileShader(name); GLint success; glGetShaderiv(name, GL_COMPILE_STATUS, &success); return success; } bool link_shaders(GLuint program, GLuint vert, GLuint frag) { glAttachShader(program, vert); glAttachShader(program, frag); glLinkProgram(program); glDetachShader(program, frag); glDetachShader(program, vert); GLint success; glGetProgramiv(program, GL_LINK_STATUS, &success); return success; } } // namespace namespace simpleshader { std::optional read_to_string(fs::path const &path) { if (std::ifstream ifs{path}) { return std::string(std::istreambuf_iterator{ifs}, {}); } else { return std::nullopt; } } void print_compilation_errors(ShaderObject const &shader, fs::path const &path) { GLint log_length; glGetShaderiv(shader.name(), GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { std::vector log(log_length); glGetShaderInfoLog(shader.name(), log.size(), nullptr, log.data()); std::cerr << path.string() << ':' << log.data() << '\n'; } } void print_link_errors(ShaderProgram const &program) { GLint log_length; glGetProgramiv(program.name(), GL_INFO_LOG_LENGTH, &log_length); if (log_length > 0) { std::vector log(log_length); glGetProgramInfoLog(program.name(), log.size(), nullptr, log.data()); std::cerr << log.data() << '\n'; } } ShaderObject::ShaderObject(GLenum type, std::string const &src) : type_{type}, name_{} { name_ = glCreateShader(type_); compile_shader(name_, src.c_str()); } ShaderObject::~ShaderObject() { glDeleteShader(name_); } ShaderObject::ShaderObject(ShaderObject &&other) noexcept : type_{other.type_}, name_{other.name_} { type_ = 0; // nothing? name_ = 0; } ShaderObject &ShaderObject::operator=(ShaderObject &&other) noexcept { using std::swap; swap(type_, other.type_); swap(name_, other.name_); return *this; } bool ShaderObject::ok() const { GLint success; glGetShaderiv(name_, GL_COMPILE_STATUS, &success); return success; } GLuint ShaderObject::name() const { return name_; } ShaderProgram::ShaderProgram(ShaderObject const &vert, ShaderObject const &frag) : name_{} { name_ = glCreateProgram(); link_shaders(name_, vert.name(), frag.name()); } ShaderProgram::~ShaderProgram() { glDeleteProgram(name_); } ShaderProgram::ShaderProgram(ShaderProgram &&other) noexcept : name_{other.name_} { other.name_ = 0; } ShaderProgram &ShaderProgram::operator=(ShaderProgram &&other) noexcept { using std::swap; swap(name_, other.name_); return *this; } bool ShaderProgram::ok() const { GLint success; glGetProgramiv(name_, GL_LINK_STATUS, &success); return success; } GLuint ShaderProgram::name() const { return name_; } void ShaderProgram::clear() { glDeleteProgram(name_); name_ = 0; } } // namespace simpleshader