差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

次のリビジョン
前のリビジョン
youtube:opengl-training-005 [2025/12/06 04:57] – 作成 freemikanyoutube:opengl-training-005 [2025/12/07 00:55] (現在) – Fix worng boost include directories freemikan
行 1: 行 1:
 ====== OpenGLの修行 #5 - 続・三角形を描く (1) ====== ====== OpenGLの修行 #5 - 続・三角形を描く (1) ======
 +{{ https://youtu.be/8sidpTb4mMk |YouTubeの動画ページ}}
  
 +===== ソースコード =====
 {{ :youtube:hellotriangle-simpleshader.zip |ダウンロード}} {{ :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 ====
 +<file>
 +BasedOnStyle: Chromium
 +IndentWidth: 4
 +PointerAlignment: Right
 +AccessModifierOffset: -2
 +ConstructorInitializerIndentWidth: 8
 +</file>
 +
 +
 +==== CMakeLists.txt ====
 +<file cmake>
 +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)
 +</file>
 +
 +
 +==== glad/CMakeLists.txt ====
 +<file cmake>
 +enable_language(C)
 +
 +add_library(glad OBJECT src/glad.c)
 +
 +target_include_directories(
 +    glad
 +    PUBLIC
 +        ${CMAKE_CURRENT_SOURCE_DIR}/include
 +    )
 +</file>
 +
 +
 +=== 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 ====
 +
 +<file glsl>
 +#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;
 +}
 +</file>
 +
 +==== shaders/hello_triangle.vert ====
 +
 +<file glsl>
 +#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];
 +}
 +</file>
 +
 +
 +==== src/main.cpp ====
 +<file cpp>
 +#include <glad/glad.h>
 +// glad.h must be included before glfw3.h
 +#include <GLFW/glfw3.h>
 +
 +#include "simpleshader.h"
 +
 +#include <iostream>
 +#include <string>
 +#include <filesystem>
 +#include <fstream>
 +#include <iterator>
 +#include <optional>
 +#include <vector>
 +
 +namespace fs = std::filesystem;
 +using namespace simpleshader;
 +
 +std::optional<ShaderProgram> 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";
 +}
 +</file>
 +
 +
 +==== src/simpleshader.h ====
 +<file cpp>
 +#ifndef SIMPLESHADER_H
 +#define SIMPLESHADER_H
 +
 +#include <glad/glad.h>
 +
 +#include <filesystem>
 +#include <optional>
 +#include <boost/noncopyable.hpp>
 +
 +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<std::string> 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
 +</file>
 +
 +
 +==== src/simpleshader.cpp ====
 +<file cpp>
 +#include "simpleshader.h"
 +
 +#include <filesystem>
 +#include <fstream>
 +#include <iostream>
 +#include <optional>
 +#include <string>
 +#include <vector>
 +
 +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<std::string> read_to_string(fs::path const &path) {
 +    if (std::ifstream ifs{path}) {
 +        return std::string(std::istreambuf_iterator<char>{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<GLchar> 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<GLchar> 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
 +</file>
  
文書の先頭へ