# 2024 電腦繪圖 - HW1 ### **開發環境建置** --- - Operating system : windows 10 - C++ & Cmake (Cross Platform) - OpenGL 3.3 - Libraries - **glfw (根據當前發布版本,個人使用3.4)**: Creating windows, contexts and surfaces, handling events, etc. - **glad**: OpenGL loading library. - **glm (v0.9.9.8)**: Mathematic library. - **stb_image (v2.25)**: Image loader. - **tinyobjloader (v0.9.20)**: Obj file (3D model) loader. - **dear imgui (v1.77)**: Graphical user interface. 1. 在作業目錄下面新建一個build資料夾 2. 開啟CMake ![image](https://hackmd.io/_uploads/SyN3T8zRR.png) ![image](https://hackmd.io/_uploads/Bk_iTUf0C.png) 3. 點選 configure(會出現找不到對應.cmake file,這個會根據不同編譯器使用mingw / msvc有不同的方式,這裡是vscode可行的方案) ![image](https://hackmd.io/_uploads/SyL5HPGRC.png) 注意error message,他說找不到2個相關 .cmake,所以我們要做對應處理 ![image](https://hackmd.io/_uploads/HJDnkvzR0.png) 4. 另外再開一個 CMake ![image](https://hackmd.io/_uploads/SyfOJPz0R.png) 5. 選擇你設定的資料夾(這裡是msvs2024),把glfw下面創建一個build floder,記得勾選BUILD_SHARED_LIB,configure 後 generate。 ![image](https://hackmd.io/_uploads/rymGVPM00.png) 6. 前往 glfw/build ![image](https://hackmd.io/_uploads/ByvYxPfR0.png) 7. 打開terminal 依序輸入 ``` cmake -G "Visual Studio 17 2022" -A x64 . // 根據自己IDE 的配置 cmake --build . --config Release cmake --install . ``` 成功的話會在msvs2024看到3個folder(bin/include/lib) ![image](https://hackmd.io/_uploads/ByzcVDGC0.png) 8. 回CMake,剛剛 HW1/samplecode,有疑慮就先Delete cache一次,整個重跑 ![image](https://hackmd.io/_uploads/SkVOMvGAR.png) ![image](https://hackmd.io/_uploads/B1W0SvGCR.png) 看到generate 成功就大致上沒問題->open project ![image](https://hackmd.io/_uploads/BkrILPGRR.png) 9. 上方選擇 release + x64 -> Homework01 右鍵 -> 建置 ![image](https://hackmd.io/_uploads/SJnODPzRC.png) 10. 在project->set a startup project ![image](https://hackmd.io/_uploads/ry4mHqK0C.png) 11. 設定輸入引數的值 Homework01->屬性->偵錯->命令引數->注意一定要套用->確認 ![image](https://hackmd.io/_uploads/rynIPcFRR.png) ![image](https://hackmd.io/_uploads/SkQYHctCR.png) ![image](https://hackmd.io/_uploads/rksML9KA0.png) ### **程式碼** --- 1. OpenGLBufferObject.cpp ```c++ #include "OpenGLBufferObject.hpp" #include "OpenGLException.hpp" #include "Utils/Global.hpp" namespace OpenGL { namespace Detail { constexpr GLuint noId{0}; bool isCreated(GLuint id) noexcept; inline bool isCreated(GLuint id) noexcept { return static_cast<bool>(id); } } // namespace Detail OpenGLBufferObject::OpenGLBufferObject( OpenGLBufferObject::Type type, OpenGLBufferObject::UsagePattern usagePattern) : id_{Detail::noId}, type_{type}, usagePattern_{usagePattern} { create(); } OpenGLBufferObject::OpenGLBufferObject(OpenGLBufferObject &&other) noexcept : id_{std::move(other.id_)}, type_{std::move(other.type_)}, usagePattern_{std::move(other.usagePattern_)} { other.id_ = Detail::noId; // Avoid double deletion } OpenGLBufferObject & OpenGLBufferObject::operator=(OpenGLBufferObject &&other) noexcept { if (this != &other) { if (Detail::isCreated(id_)) { tidy(); } id_ = std::move(other.id_); type_ = std::move(other.type_); usagePattern_ = std::move(other.usagePattern_); other.id_ = Detail::noId; // Avoid double deletion } return *this; } OpenGLBufferObject::~OpenGLBufferObject() { if (Detail::isCreated(id_)) { tidy(); } } void OpenGLBufferObject::allocateBufferData(const void *data, GLsizeiptr size) noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 綁定當前的緩衝區對象 glBindBuffer(static_cast<GLenum>(type_), id_); // 分配緩衝區數據 glBufferData(static_cast<GLenum>(type_), size, data, static_cast<GLenum>(usagePattern_)); } void OpenGLBufferObject::bind() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 綁定當前的緩衝區對象 glBindBuffer(static_cast<GLenum>(type_), id_); } void OpenGLBufferObject::create() { PROGRAM_ASSERT(!Detail::isCreated(id_)); // Fill in the Blank // 創建緩衝區對象 glGenBuffers(1, &id_); if (!Detail::isCreated(id_)) { throw OpenGLException("OpenGLBufferObject failed to instantiate."); } } GLuint OpenGLBufferObject::id() const noexcept { return id_; } void OpenGLBufferObject::release() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 解除當前綁定的緩衝區對象 glBindBuffer(static_cast<GLenum>(type_), 0); } void OpenGLBufferObject::tidy() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 刪除緩衝區對象 glDeleteBuffers(1, &id_); id_ = Detail::noId; } } // namespace OpenGL ``` 2. OpenGLShader.cpp ```c++ #include "OpenGLShader.hpp" #include "OpenGLException.hpp" #include "Utils/FileIO/FileIn.hpp" #include "Utils/Global.hpp" #include <cstring> #include <iostream> #include <string> namespace OpenGL { namespace Detail { constexpr GLuint noId{0}; bool compileStatus(GLuint id) noexcept; bool isCreated(GLuint id) noexcept; inline bool compileStatus(GLuint id) noexcept { GLint status; glGetShaderiv(id, GL_COMPILE_STATUS, &status); // 檢查編譯狀態 return (status == GL_TRUE); // 如果狀態為 GL_TRUE,表示編譯成功 } inline bool isCreated(GLuint id) noexcept { return static_cast<bool>(id); } } // namespace Detail OpenGLShader::OpenGLShader(OpenGLShader::Type type) : id_{Detail::noId}, type_{type} { create(); } OpenGLShader::OpenGLShader(OpenGLShader &&other) noexcept : id_{std::move(other.id_)}, type_{std::move(other.type_)} { other.id_ = Detail::noId; // Avoid double deletion } OpenGLShader &OpenGLShader::operator=(OpenGLShader &&other) noexcept { if (this != &other) { if (Detail::isCreated(id_)) { tidy(); } id_ = std::move(other.id_); type_ = std::move(other.type_); other.id_ = Detail::noId; // Avoid double deletion } return *this; } OpenGLShader::~OpenGLShader() { if (Detail::isCreated(id_)) { tidy(); } } bool OpenGLShader::compileFromFile(const char *fileName) noexcept { std::string source{FileIO::ReadFileFullText(fileName)}; return compileFromSource(source.c_str()); } bool OpenGLShader::compileFromSource(const char *source) noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank glShaderSource(id_, 1, &source, nullptr);// 提供著色器源代碼給 OpenGL // Fill in the Blank glCompileShader(id_); // 編譯著色器 // 檢查編譯狀態,並返回是否成功 if (!Detail::compileStatus(id_)) { GLint logLength = 0; // 獲取錯誤日誌長度 glGetShaderiv(id_, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { // 儲存錯誤日誌 std::string log(logLength, ' '); glGetShaderInfoLog(id_, logLength, nullptr, &log[0]); std::cerr << "[Error] Shader compilation failed:\n" << log << std::endl; } return false; // 編譯失敗,返回 false } return true; // 編譯成功 } void OpenGLShader::create() { PROGRAM_ASSERT(!Detail::isCreated(id_)); // Fill in the Blank id_ = glCreateShader(static_cast<GLenum>(type_)); // 根據著色器類型創建對應的著色器 if (!Detail::isCreated(id_)) { throw OpenGLException("OpenGLShader failed to instantiate."); } } GLuint OpenGLShader::id() const noexcept { return id_; } void OpenGLShader::tidy() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank glDeleteShader(id_); // 刪除著色器 id_ = Detail::noId; } OpenGLShader::Type OpenGLShader::type() const noexcept { return type_; } } // namespace OpenGL ``` 3. OpenGLShaderProgram.cpp ```c++ #include "OpenGLShaderProgram.hpp" #include "OpenGLException.hpp" #include "Utils/Global.hpp" #include <iostream> namespace OpenGL { namespace Detail { constexpr GLuint noId{0}; bool isCreated(GLuint id) noexcept; std::unique_ptr<OpenGLShader> makeShader(OpenGLShader::Type type); inline bool isCreated(GLuint id) noexcept { return static_cast<bool>(id); } std::unique_ptr<OpenGLShader> makeShader(OpenGLShader::Type type) { std::unique_ptr<OpenGLShader> shader{nullptr}; try { shader.reset(new OpenGLShader{type}); } catch (OpenGLException &e) { std::cerr << "[Error]" << e.what(); return std::unique_ptr<OpenGLShader>{nullptr}; } return shader; } } // namespace Detail OpenGLShaderProgram::OpenGLShaderProgram() : id_{Detail::noId} { create(); } OpenGLShaderProgram::OpenGLShaderProgram(OpenGLShaderProgram &&other) noexcept : id_{std::move(other.id_)} { other.id_ = 0; // Avoid double deletion } OpenGLShaderProgram & OpenGLShaderProgram::operator=(OpenGLShaderProgram &&other) noexcept { if (this != &other) { if (Detail::isCreated(id_)) { tidy(); } id_ = std::move(other.id_); shaders_ = std::move(other.shaders_); other.id_ = Detail::noId; // Avoid double deletion } return *this; } OpenGLShaderProgram::~OpenGLShaderProgram() { if (Detail::isCreated(id_)) { tidy(); } } bool OpenGLShaderProgram::addShaderFromFile(OpenGLShader::Type type, const char *fileName) noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); auto shader = Detail::makeShader(type); if (!(shader.get())) { return false; } if (!shader->compileFromFile(fileName)) { return false; } attachShader(std::move(shader)); return true; } bool OpenGLShaderProgram::addShaderFromSource(OpenGLShader::Type type, const char *source) noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); auto shader = Detail::makeShader(type); if (!(shader.get())) { return false; } if (!shader->compileFromSource(source)) { return false; } attachShader(std::move(shader)); return true; } void OpenGLShaderProgram::attachShader( std::unique_ptr<OpenGLShader> &&shader) noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank glAttachShader(id_, shader->id()); // shaders_.push_back(std::move(shader)); } void OpenGLShaderProgram::create() { PROGRAM_ASSERT(!Detail::isCreated(id_)); // Fill in the Blank id_ = glCreateProgram(); // 創建著色器程序 // if (!Detail::isCreated(id_)) { throw OpenGLException("OpenGLShaderProgram failed to instantiate."); } } void OpenGLShaderProgram::destroyProgram() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank glDeleteProgram(id_); // 刪除著色器程序 // id_ = Detail::noId; } void OpenGLShaderProgram::destroyShaders() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); for (auto &shader : shaders_) { // Fill in the Blank glDetachShader(id_, shader->id()); // 將著色器從程序中移除 // shader.reset(nullptr); } shaders_.clear(); } void OpenGLShaderProgram::disableAttributeArray(GLuint index) noexcept { // Fill in the Blank glDisableVertexAttribArray(index); // 禁用頂點屬性數組 // } void OpenGLShaderProgram::enableAttributeArray(GLuint index) noexcept { // Fill in the Blank glEnableVertexAttribArray(index); // 啟用頂點屬性數組 // } GLuint OpenGLShaderProgram::id() const noexcept { return id_; } void OpenGLShaderProgram::link() noexcept { // Fill in the Blank PROGRAM_ASSERT(Detail::isCreated(id_)); glLinkProgram(id_); // 鏈接著色器程序 // } bool OpenGLShaderProgram::linkStatus() const noexcept { GLint status; // Fill in the Blank glGetProgramiv(id_, GL_LINK_STATUS, &status); // 檢查鏈接狀態 // return (status == GL_TRUE);// 如果狀態為 GL_TRUE,鏈接成功 } void OpenGLShaderProgram::mapAttributePointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, int offset) noexcept { // Fill in the Blank glVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast<void *>(offset)); // 綁定頂點屬性指針 // } void OpenGLShaderProgram::tidy() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); destroyShaders(); destroyProgram(); } void OpenGLShaderProgram::use() noexcept { // Fill in the Blank glUseProgram(id_); // 啟用著色器程序 // } } // namespace OpenGL ``` 4.OpenGLTexture.cpp ```c++ #include "OpenGLTexture.hpp" #include "OpenGLException.hpp" #include "Utils/Global.hpp" namespace OpenGL { namespace Detail { constexpr GLuint noId{0}; bool isCreated(GLuint id) noexcept; inline bool isCreated(GLuint id) noexcept { return static_cast<bool>(id); } } // namespace Detail OpenGLTexture::OpenGLTexture() : id_{Detail::noId}, format_{0}, height_{0}, width_{0}, mipmapCount_{0}, minificationFilter_{Filter::Nearest}, magnificationFilter_{Filter::Linear}, wrapOption_{WrapOption::Repeat} { } OpenGLTexture::OpenGLTexture(GLsizei width, GLsizei height, GLenum format, const std::vector<unsigned char> &buffer, Filter minificationFilter, Filter magnificationFilter, WrapOption wrapOption) : id_{0}, format_{format}, height_{height}, width_{width}, minificationFilter_{minificationFilter}, magnificationFilter_{magnificationFilter}, wrapOption_{wrapOption} { create(); bind(); bindBuffer(buffer); } OpenGLTexture::OpenGLTexture(OpenGLTexture &&other) noexcept : id_{std::move(other.id_)}, format_{std::move(other.format_)}, height_{std::move(other.height_)}, width_{std::move(other.width_)}, minificationFilter_{std::move(other.minificationFilter_)}, magnificationFilter_{std::move(other.magnificationFilter_)}, wrapOption_{std::move(other.wrapOption_)} { other.id_ = Detail::noId; // Avoid double deletion } OpenGLTexture &OpenGLTexture::operator=(OpenGLTexture &&other) noexcept { if (this != &other) { if (Detail::isCreated(id_)) { tidy(); } id_ = std::move(other.id_); format_ = std::move(other.format_); height_ = std::move(other.height_); width_ = std::move(other.width_); mipmapCount_ = std::move(other.mipmapCount_); minificationFilter_ = std::move(other.minificationFilter_); magnificationFilter_ = std::move(other.magnificationFilter_); wrapOption_ = std::move(other.wrapOption_); other.id_ = Detail::noId; // Avoid double deletion } return *this; } OpenGLTexture::~OpenGLTexture() { if (Detail::isCreated(id_)) { tidy(); } } void OpenGLTexture::bind() { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank glBindTexture(GL_TEXTURE_2D, id_); // 綁定紋理 } void OpenGLTexture::bindBuffer(const std::vector<unsigned char> &buffer) const { // Fill in the Blank // 綁定當前的2D紋理 glBindTexture(GL_TEXTURE_2D, id_); // (bind) // 設置過濾器 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(minificationFilter_)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(magnificationFilter_)); // (parameter setup: filter and warpping method) // 設置包裝選項 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(wrapOption_)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(wrapOption_)); // (data specify) // 指定2D紋理圖像數據 glTexImage2D(GL_TEXTURE_2D, 0, format_, width_, height_, 0, format_, GL_UNSIGNED_BYTE, buffer.data()); // (generate mipmap) // 生成多級細化紋理 glGenerateMipmap(GL_TEXTURE_2D); } void OpenGLTexture::create() { PROGRAM_ASSERT(!Detail::isCreated(id_)); // Fill in the Blank // 創建紋理ID glGenTextures(1, &id_); if (!Detail::isCreated(id_)) { throw OpenGLException( "OpenGLTexture instantiate failed at 'glGenTextures'."); } } GLenum OpenGLTexture::format() const { return format_; } GLsizei OpenGLTexture::height() const { return height_; } GLuint OpenGLTexture::id() const { return id_; } OpenGLTexture::Filter OpenGLTexture::magnificationFilter() const { return magnificationFilter_; } OpenGLTexture::Filter OpenGLTexture::minificationFilter() const { return minificationFilter_; } void OpenGLTexture::release() { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 解除紋理綁定 glBindTexture(GL_TEXTURE_2D, 0); } void OpenGLTexture::setMagnificationFilter(Filter filter) { PROGRAM_ASSERT(Detail::isCreated(id_)); magnificationFilter_ = filter; bind(); // Fill in the Blank // 設置放大過濾器 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(filter)); release(); } void OpenGLTexture::setMinificationFilter(Filter filter) { PROGRAM_ASSERT(Detail::isCreated(id_)); minificationFilter_ = filter; bind(); // Fill in the Blank // 設置縮小過濾器 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(filter)); release(); } void OpenGLTexture::setWrapOption(WrapOption option) { PROGRAM_ASSERT(Detail::isCreated(id_)); wrapOption_ = option; bind(); // Fill in the Blank // 設置包裝方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(option)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(option)); release(); } void OpenGLTexture::tidy() { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 刪除紋理 glDeleteTextures(1, &id_); id_ = 0; } GLsizei OpenGLTexture::width() const { return width_; } OpenGLTexture::WrapOption OpenGLTexture::wrapOption() const { return wrapOption_; } } // namespace OpenGL ``` 5. OpenGLVertexArrayObject.cpp ```c++ #include "OpenGLVertexArrayObject.hpp" #include "OpenGLException.hpp" #include "Utils/Global.hpp" namespace OpenGL { namespace Detail { constexpr GLuint noId{0}; bool isCreated(GLuint id) noexcept; inline bool isCreated(GLuint id) noexcept { return static_cast<bool>(id); } } // namespace Detail OpenGLVertexArrayObject::OpenGLVertexArrayObject() : id_{Detail::noId} { create(); } OpenGLVertexArrayObject::OpenGLVertexArrayObject( OpenGLVertexArrayObject &&other) noexcept : id_{std::move(other.id_)} { other.id_ = 0; // Avoid double deletion } OpenGLVertexArrayObject & OpenGLVertexArrayObject::operator=(OpenGLVertexArrayObject &&other) noexcept { if (this != &other) { if (Detail::isCreated(id_)) { tidy(); } id_ = std::move(other.id_); other.id_ = Detail::noId; // Avoid double deletion } return *this; } OpenGLVertexArrayObject::~OpenGLVertexArrayObject() { if (Detail::isCreated(id_)) { tidy(); } } void OpenGLVertexArrayObject::bind() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 綁定 VAO glBindVertexArray(id_); } GLuint OpenGLVertexArrayObject::id() const noexcept { return id_; } void OpenGLVertexArrayObject::release() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 解除 VAO 綁定 glBindVertexArray(0); } void OpenGLVertexArrayObject::create() { PROGRAM_ASSERT(!Detail::isCreated(id_)); // Fill in the Blank // 創建 VAO glGenVertexArrays(1, &id_); if (!Detail::isCreated(id_)) { throw OpenGLException("OpenGLVertexArrayObject failed to instantiate."); } } void OpenGLVertexArrayObject::tidy() noexcept { PROGRAM_ASSERT(Detail::isCreated(id_)); // Fill in the Blank // 刪除 VAO glDeleteVertexArrays(1, &id_); id_ = Detail::noId; } } // namespace OpenGL ```