# OpenGL NCKU HW1
# 作業環境
vscode / glfw version 3.3.6 / cmake version 3.31.4 / GNU Make 4.3 / g++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3. / Linux 6.11.0-19-generic #19~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Feb 17 11:51:52 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
# 方法說明
## OpenGLBufferObject
針對 OpenGL Buffer 進行操作,包含創建名稱、類型綁定、空間分配與資料複製、刪除和解除綁定。
根據 [官方文檔](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glBufferData.xhtml)
```cpp
void glBufferData(GLenum target,
GLsizeiptr size,
const void * data,
GLenum usage);
```
> While creating the new storage, any pre-existing data store is deleted. The new data store is created with the specified size in bytes and usage.
> data -> Specifies a pointer to data that will be copied into the data store for initialization, or NULL if no data is to be copied.
## OpenGLShader
管理 shader 創建、刪除和編譯。
[glCreateShader](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glCreateShader.xhtml) 創建空的 shader 物件,並回傳一個非零的值以供引用。
[glDeleteShader](https://registry.khronos.org/OpenGL-Refpages/gl4/html/glDeleteShader.xhtml)釋放記憶體並使與 shader 指定的著色器物件關聯的名稱無效。
編譯的部分:先指定來源,並編譯
```cpp
bool OpenGLShader::compileFromSource(const char *source) noexcept
{
PROGRAM_ASSERT(Detail::isCreated(id_));
glShaderSource(id_, 1, &source, nullptr);
glCompileShader(id_);
return Detail::compileStatus(id_);
}
```
## OpenGLShaderProgram
OpenGL中一個 program 物件的包裝,用於管理著色器對象,並將它們連結在一起形成一個完整的可用於渲染的著色器程式。藉由此物件可以新增、連結和執行著色器渲染。
- 使用 `glAttachShader` 將 shader 加入到 program。
- `glLinkProgram` 將這些著色器組合成一個統一的可執行程序,可用來渲染。當使用 `glAttachShader` 附加著色器物件(如頂點和片段著色器)到 program 物件時,它們仍然是分開的並且尚不可執行。著色器是單獨編譯的,`glLinkProgram` 確保它們作為完整的管道一起工作。
- 使用 `glVertexAttribPointer` 在渲染前指定OpenGL該如何解釋頂點資料,所以以此函式手動指定輸入資料的哪一個部分對應頂點著色器的哪一個頂點屬性。
## OpenGLTexture
創建 texture 與其屬性如 wrapOption 和 filter 並且使用 `bindBuffer` 將 2D 圖片與 texture 物件綁定並且產生 mipmap
```cpp
void OpenGLTexture::bindBuffer(const std::vector<unsigned char> &buffer) const
{
glTexImage2D(GL_TEXTURE_2D, 0, format_, width_, height_, 0, format_,
GL_UNSIGNED_BYTE, buffer.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minificationFilter_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnificationFilter_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOption_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOption_);
glGenerateMipmap(GL_TEXTURE_2D);
}
```
## OpenGLVertexArrayObject
VAO 的物件利用 `glGenVertexArrays` 創建、 `glBindVertexArray` 啟用與停用
## Bonus
### Mesh 物件移動
```cpp
glm::vec3 getPosition();
void setPosition(glm::vec3 &position);
void translate(const glm::vec3 &offset);
```
設定物件座標屬性,利用 glm 計算 model 矩陣。
```cpp
model_ = glm::mat4(1.0f);
model_ = glm::translate(model_, position_);
```
### Mesh 物件旋轉
```cpp
glm::quat getRotation();
glm::vec3 getRotationEuler();
void setRotation(const glm::quat &rot);
// euler
void setRotation(const glm::vec3 &rot);
void setRotationDeg(const glm::vec3 &rot);
void rotate(float angleDegrees, const glm::vec3 &axis);
```
利用 glm 使用四元數計算 model 矩陣。
```cpp
glm::quat deltaRotation = glm::angleAxis(glm::radians(angleDegrees), glm::normalize(axis));
rotation_ = deltaRotation * rotation_;
```
```cpp
model_ *=
glm::mat4_cast(rotation_); // Convert quaternion to rotation matrix
```
### Mesh 物件縮放
```cpp
glm::vec3 getScale();
void setScale(glm::vec3 &scale);
```
利用 glm 針對 model 矩陣做縮放運算
```c
model_ = glm::scale(model_, scale_);
```
### Free camera WASD移動
```cpp
bool wasFreeCamera_ = true;
bool isFirstFreeCamera_ = true;
bool isFreeCamera_ = false;
glm::vec3 lookAt_;
glm::vec3 cameraPosition_ = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 cameraFront_ = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp_ = glm::vec3(0.0f, 1.0f, 0.0f);
float cameraSpeedFactor_ = 2.5f;
float deltaTime_ = 0.0f; // time between current frame and last frame
float lastFrame_ = 0.0f;
static bool mouseCaptured_;
float mouse_lastX_ = 400, mouse_lastY_ = 300;
float mouse_yaw_ = -90.0f;
float mouse_pitch_ = 0.0f;
float mouse_fov_ = 45.0f;
float sensitivity_ = 1.0f;
```
紀錄相機座標、速度、視角目標與紀錄時間變化讓移動運算合理可行,使用 `glfwGetKey` 接受按鍵事件,並針對其控制相機位置
### Free camera 滑鼠控制視角
增加滑鼠位置之 callback 函式,使用 mouseCaptured 與按鍵 c 確保滑鼠指標能夠隨時使用與拖離控制視角滑鼠鎖定 `GLFW_CURSOR_DISABLED` 並確定事件觸發為一組按壓與施放操作,以免在按下後取消與進入控制視角模式不斷切換。
```c
if (glfwGetKey(window_, GLFW_KEY_C) == GLFW_PRESS)
{
if (!mouseCaptured)
{
mouseCaptured = true;
if (glfwGetInputMode(window_, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
{
glfwSetInputMode(window_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwSetCursorPosCallback(window_, nullptr);
wasFreeCamera_ = true;
}
else
{
glfwSetInputMode(window_, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPosCallback(window_, Detail::mouseCallback);
}
}
}
else
{
mouseCaptured = false;
}
firstEnterMouseCallback(window_, mouse_lastX_, mouse_lastY_,
isFirstFreeCamera_);
cameraDirectionLoop(window_, mouse_lastX_, mouse_lastY_);
}
```
`firstEnterMouseCallback` 確保相機視角不會因為第一次進入自由相機模式或者第一次移動滑鼠而造成相機大幅度改變視角
使用 `cameraDirectionLoop` 進行滑鼠移動運算。
```cpp
void OpenGLWindow::cameraDirectionLoop(GLFWwindow *window, double xpos,
double ypos)
{
float xoffset = xpos - mouse_lastX_;
float yoffset = mouse_lastY_ - ypos;
mouse_lastX_ = xpos;
mouse_lastY_ = ypos;
xoffset *= sensitivity_;
yoffset *= sensitivity_;
mouse_yaw_ += xoffset;
mouse_pitch_ += yoffset;
// Add constraints to prevent camera flipping
if (mouse_pitch_ > 89.0f)
mouse_pitch_ = 89.0f;
if (mouse_pitch_ < -89.0f)
mouse_pitch_ = -89.0f;
glm::vec3 front;
front.x = cos(glm::radians(mouse_pitch_)) * cos(glm::radians(mouse_yaw_));
front.y = sin(glm::radians(mouse_pitch_));
front.z = cos(glm::radians(mouse_pitch_)) * sin(glm::radians(mouse_yaw_));
cameraFront_ = glm::normalize(front);
lookAt_ = cameraPosition_ + cameraFront_;
}
```
# 程式如何執行
```bash
./bin/Homework01 ../resources/model/Utah_teapot_\(solid\)_texture.obj ../resources/texture/uv.png ../src/Shader/BasicVertexShader.vs.glsl ../src/Shader/BasicFragmentShader.fs.glsl
```
程式一開始會先建立 shader program 與 shader ,設定好 source 和 bind 程序後,將 shader 都連接到 shaerprogram 上,接著建立設定 texture 相關訊息, VAO 、 VBO 也設置好後,即可透過 Window 將貼好 texture 的 model 顯示出來。