# 電腦圖學補強班 [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 渲染流程: ![](https://i.imgur.com/aX3cqfW.png) ### 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要怎麼讀取資料) ![](https://i.imgur.com/kB1Qoux.png) ```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 ![](https://i.imgur.com/I1uUx13.png) 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 定義一個攝影機,需要攝影機在世界座標中的位置、觀察的方向、以及一個相機向上和相機向右的方向即可,換句話說也就是攝影機的三個互相垂直的單位向量軸。 ![](https://i.imgur.com/Tt7luMk.png) 可以使用 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`