# 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


3. 點選 configure(會出現找不到對應.cmake file,這個會根據不同編譯器使用mingw / msvc有不同的方式,這裡是vscode可行的方案)

注意error message,他說找不到2個相關 .cmake,所以我們要做對應處理

4. 另外再開一個 CMake

5. 選擇你設定的資料夾(這裡是msvs2024),把glfw下面創建一個build floder,記得勾選BUILD_SHARED_LIB,configure 後 generate。

6. 前往 glfw/build

7. 打開terminal 依序輸入
```
cmake -G "Visual Studio 17 2022" -A x64 . // 根據自己IDE 的配置
cmake --build . --config Release
cmake --install .
```
成功的話會在msvs2024看到3個folder(bin/include/lib)

8. 回CMake,剛剛 HW1/samplecode,有疑慮就先Delete cache一次,整個重跑


看到generate 成功就大致上沒問題->open project

9. 上方選擇 release + x64 -> Homework01 右鍵 -> 建置

10. 在project->set a startup project

11. 設定輸入引數的值 Homework01->屬性->偵錯->命令引數->注意一定要套用->確認



### **程式碼**
---
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
```