# 電腦圖學補強班
[toc]
## 前言
第一堂課時間:2021/11/10 17:00
### Modern OpenGL
簡單說明 Legacy OpenGL 跟 Modern OpenGL 的差別,以及為什麼要學習。
## 建立視窗
### SDL2
教導 SDL2 初始化、開啟視窗、創建 OpenGL Context 、 Event 處理以及整體程式流程介紹。
重點:`update(float dt)` 以及 `render(float dt)` 的觀念。
## 繪製一個三角形
### 1. Modern OpenGL Graphics Pipeline
簡介現代 OpenGL 渲染流程:

### 2. 建立頂點
可以是 `std::vector<float>` 可以是 `struct` 也可以單純是 `float[]`
### 3. 將頂點資料傳入GPU
#### Vertex Buffer Object(宣告緩衝區塊並綁定資料)
介紹什麼是 VBO,以及其相關指令:
```cpp=
unsigned int vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data, GL_STATIC_DRAW);
```
#### Vertex Attributes Pointer(告訴GPU要怎麼讀取資料)

```cpp=
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAtrribArray(0);
```
#### Vertex Array Object(把剛剛上面的設定全部記錄起來)
Core OpenGL 會強制我們一定要使用 VAO
VAO 可以一次紀錄之前所設定的 VBO、頂點屬性指標設定等等,這樣每當繪畫一個物體只需要設定一次即可。
```cpp=
unsigned int vao, vbo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
// bind vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data, GL_STATIC_DRAW);
// setting vertex attributes pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAtrribArray(0);
glBindVertexArray(0);
```
### 4. 認識 GLSL 以及建立 Shader
#### Shader and GLSL
簡單介紹 Shader 是甚麼,GLSL(OpenGL Shading Language) 是甚麼,以及怎麼 Complier 和 Link。
```cpp=
const char *vertexShaderSource = "...";
unsigned int vertexShader_id;
vertexShader_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader_id, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader_id);
int success;
char infoLog[512];
glGetShaderiv(vertexShader_id, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader_id, 512, NULL, infoLog);
// Print the error message
}
```
#### Vertex Shader
頂點著色器
```glsl=
#version 330 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
```
#### Fragment Shader
片段著色器
```glsl=
#version 330 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
```
#### Shader Program
```cpp=
unsigned int shaderProgram_id;
shaderProgram_id = glCreateProgram();
glAttachShader(shaderProgram_id, vertexShader_id);
glAttachShader(shaderProgram_id, fragmentShader_id);
glLinkProgram(shaderProgram_id);
int success;
char infoLog[512];
glGetProgramiv(shaderProgram_id, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram_id, 512, NULL, infoLog);
// Print the error message
}
```
```cpp=
// 當要使用該 shader program 時必須先呼叫此函式才會切換到此 shader program
glUseProgram(shaderProgram_id);
// 通常把 shader 都 link 到 shader program 上之後就可以刪除了
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 當沒有要繼續使用該 shader program 時記得刪除
glDeleteProgram(shaderProgram_id);
```
### 5. 繪製三角形
```cpp=
// use the shader program
glUseProgram(shaderProgram_id);
glBindVertexArray(vao);
// Draw the triangle
glDrawArrays(GL_TRIANGLES, 0, 3);
```
## 繪製一個彩色三角形
### Interleaved attributes
交錯的方式儲存在同一個資料(vbo)中,並透過 Pointer 去交錯抓取對應的屬性資料(頂點位置、顏色、法向量、貼圖座標等)。
## 繪製一個四邊形
### 1. 定義正方形頂點資料
### Element Buffer Object(專門記錄索引的暫存區)
繪製四邊形時,會有寫入重複的頂點,此時就可以使用到所謂的 index 去紀錄。
```cpp=
unsigned int ebo;
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
```
### 2. 繪製四邊形(由兩個三角形組成)
```cpp=
glUseProgram(shaderProgram_id);
glBindVertexArray(vao);
// 繪製的時候 Draw a Rectangle
// ebo 也可以綁定在 vao 之內,所以只需要設定一次。
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
```
## 進入3D世界
### Basic Transformations
自己實作 Model Stack,push()、pop()、top() 等 function。
最基本的語法:
```cpp=
// 宣告向量以及矩陣
glm::vec2 a = glm::vec2(0.0f, 0.8f);
glm::vec3 b = glm::vec3(5.0f, 2.0f, 1.0f);
glm::vec4 c = glm::vec4(0.0f, 2.0f, 1.0f, 1.0f);
// 基本的位移操作
glm::mat3 d = glm::mat3(1.0f);
glm::mat4 trans = glm::mat4(1.0f);
trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
// 矩陣與向量相乘 4*4 與 4*1
c = trans * c;
std::cout << c.x << c.y << c.z << c.w << std::endl;
// 宣告 4*4 的矩陣並為單位矩陣
glm::mat4 hello = glm::mat4(1.0f);
// 相乘旋轉矩陣,90度(需要轉換為徑度),旋轉軸為正 z 軸。
hello = glm::rotate(hello, glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
// 相乘縮放矩陣,三個軸都縮小 0.5 倍。
hello = glm::scale(hello, glm::vec3(0.5f));
```
### Uniform 變數(將變數從程式丟入 Shader 中)
vertex shader 必須修改程式碼,裡面要有 uniform 變數。
```glsl=
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
void main() {
gl_Position = model * vec4(aPos, 1.0);
}
```
uniform 變數:可以從程式碼傳參數到 GPU Shader 內,進行控制等等。
```cpp=
unsigned int model = glGetUniformLocation(ourShader.ID, "model");
glUniformMatrix4fv(model, 1, GL_FALSE, glm::value_ptr(trans));
```
矩陣相乘的先後順序會有差,scale 都是由原點 (0, 0, 0) 進行縮放;先位移再旋轉跟先旋轉再位移兩者得到的結果是截然不同的。
### Coordinate Systems

1. 在地座標系【齊次座標系,4維】
2. 世界座標系【齊次座標系,4維】
3. 眼睛座標系【齊次座標系,4維】
4. 裁剪座標系【齊次座標系,4維】、標準設備座標【3維】
5. 螢幕座標系【3維】
> 特別記住,轉換成裁剪座標和標準設備座標這兩個轉換,在OpenGL中是融合為一且並在投影矩陣中達成。
> 齊次座標系跟這個OpenGL空間轉換是不樣的東西,不要搞混
OpenGL 採用的是右手座標系,正X軸朝右,正Y軸朝上,正Z軸朝自己,但是 DirectX 採用的是左手座標系;另外在 OpenGL 的攝影機設定上,看的方向是朝向負 Z 軸,而一樣攝影機本身的正 Z 軸方向是在正後方;在 OpenGL 中,NDC 是採用左手座標系,所以正 Z 軸會是相反的。
簡單的攝影機
```cpp=
glm::mat4 view;
// 這還不是一個稱職的攝影矩陣,這單純只是模仿攝影機向後
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
```
透視投影
```cpp=
glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);
```
### 將畫面變成 3D (將 2D 的平面稍微旋轉,可以看出 3D 的感覺)
## 繪製一個立方體
### 1. 頂點資料的設定(6 個面)
## 繪製一顆球體
## 製作攝影機
### Camera
定義一個攝影機,需要攝影機在世界座標中的位置、觀察的方向、以及一個相機向上和相機向右的方向即可,換句話說也就是攝影機的三個互相垂直的單位向量軸。

可以使用 glm 所提供的函式:
```cpp=
glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
```
或是自己實作移動以及旋轉攝影機,並實作計算攝影機的三軸。
將會使用到線性代數中的 Gram-Schmidt Orthogonalization。
## 投影矩陣介紹
### Perspective Projection
```cpp=
glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);
```
### Orthogonal Projection
```cpp=
glm::mat4 projection;
projection = glm::ortho(100.0f, -100.0f, 100.0f, -100.0f, 0.1f, 100.0f);
```
###### tags: `OpenGL`