---
# System prepended metadata

title: 2024 電腦繪圖 - HW1

---

# 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

```