bilibili視頻鏈接:
【最好的OpenGL教程之一】https://www.bilibili.com/video/BV1MJ411u7Bc?p=5&vd_source=44b77bde056381262ee55e448b9b1973
函數網站:
docs.gl
說明:
1.之后就不再單獨整理網站具體函數了,網站直接翻譯會更直觀也會有更多注意點。直接通過csdn索引查找反而會慢。
2.代碼區域會單獨注釋功能參數返回值和相關注意事項。
3.課程學習從4-本節,如果有些函數沒有注釋可以看專欄里面的前面發表的文章,一般有解釋。
4.如果覺得代碼注釋白色字體不太直觀可以直接copy到相應軟件看。
5.有兩種版本的可供查看:注釋全面的和注釋簡潔版的,可以在索引里面找到相關代碼查看。
6.希望能幫到你。
7.有錯誤請跟我說明一下,可能整理的時候沒有檢查好。
一、知識點整理
1.1 畫方形
1.1.1方法一
1.1.1.1代碼
//準備數據
float position[] = {0.5f, 0.5f,-0.5f, -0.5f,0.5f, -0.5f,-0.5f,0.5f,-0.5f,-0.5f,0.5f,-0.5f};//定義緩沖區對象
unsigned int buffer;
//功能:生成緩沖區對象,并將數據寫入緩沖區
glGenBuffers(1, &buffer);
//功能:將緩沖區對象綁定到目標
glBindBuffer(GL_ARRAY_BUFFER, buffer);
//功能:將數據寫入緩沖區
glBufferData(GL_ARRAY_BUFFER, 2 * 6 * sizeof(float), position, GL_STATIC_DRAW);//功能:配置頂點屬性指針
glEnableVertexAttribArray(0);
//功能:指定頂點屬性數組的索引、大小、數據類型、是否歸一化、偏移量、數據指針
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);
1.1.1.2運行結果
1.1.1.3解釋
在float數組里面直接添加組成繪制方形的兩個三角形所需的六個頂點。
1.1.1.4缺點
頂點冗余
1.1.2方法二——索引緩沖
1.1.1.1代碼
//準備數據
float position[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f,0.5f,-0.5f,0.5f,
};//定義頂點索引緩存,用于標定頂點順序
unsigned int indices[] = {0,1,2,2,3,0
};//定義緩沖區對象
unsigned int buffer;
//功能:生成緩沖區對象,并將數據寫入緩沖區
glGenBuffers(1, &buffer);
//功能:將緩沖區對象綁定到目標
glBindBuffer(GL_ARRAY_BUFFER, buffer);
//功能:將數據寫入緩沖區
glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);//功能:配置頂點屬性指針
glEnableVertexAttribArray(0);
//功能:指定頂點屬性數組的索引、大小、數據類型、是否歸一化、偏移量、數據指針
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);//索引緩沖區對象
unsigned int ibo;
//功能:生成緩沖區對象,并將數據寫入緩沖區
glGenBuffers(1, &ibo);
//功能:將緩沖區對象綁定到目標.沒有綁定為數組緩沖區
//參數:1.GL_ELEMENT_ARRAY_BUFFER:指定目標為索引緩沖區對象
//2.ibo:索引緩沖區對象ID
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
//功能:將數據寫入緩沖區
//參數:1.GL_ELEMENT_ARRAY_BUFFER:指定目標為索引緩沖區對象
//2.6*sizeof(unsigned int):索引數據大小
//3.indices:索引數據指針
//4.GL_STATIC_DRAW:指定數據不會改變
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);
while繪制時改動
//glDrawArrays(GL_TRIANGLES, 0, 6);
//功能:繪制三角形
//參數:1.GL_TRIANGLES:繪制三角形
//2.6:頂點數量
//3.GL_UNSIGNED_INT:索引數據類型
//4.nullptr:索引數據指針
//因為索引緩沖區已經綁定到GL_ELEMENT_ARRAY_BUFFER目標,所以這里不需要再傳入索引數據指針
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
1.1.1.2運行結果
如果不用unsigned,如:
glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);
會繪制錯誤
1.1.1.3解釋
利用索引緩沖。避免方法一的重復撰寫,減少內存占用(在實際應用中頂點還包含了顏色,法線等信息)。頂點數從6->4。
QS:position的坐標點怎么和indices聯系起來的
1. 頂點數據 (
position
?數組)float position[] = {-0.5f, -0.5f, // 頂點 00.5f, -0.5f, // 頂點 10.5f, 0.5f, // 頂點 2-0.5f, 0.5f // 頂點 3 };
position
數組存儲了四個頂點的坐標。- 每個頂點由兩個浮點數表示,分別是x和y坐標。
- 頂點的順序是:左下、右下、右上、左上。
2. 索引數據 (
indices
?數組)unsigned int indices[] = {0, 1, 2, // 三角形 1: 左下 -> 右下 -> 右上2, 3, 0 // 三角形 2: 右上 -> 左上 -> 左下 };
indices
數組存儲了繪制兩個三角形所需的頂點索引。- 索引值對應
position
數組中頂點的順序。- 例如:
- 第一個三角形的頂點索引是?
0, 1, 2
,這意味著使用?position
?數組中的第0個、第1個和第2個頂點來繪制一個三角形。- 第二個三角形的頂點索引是?
2, 3, 0
,這意味著使用?position
?數組中的第2個、第3個和第0個頂點來繪制另一個三角形。3. 頂點緩沖區對象 (
buffer
)unsigned int buffer; glGenBuffers(1, &buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);
- 生成一個頂點緩沖區對象(VBO),用于存儲頂點數據。
- 將頂點數據綁定到
GL_ARRAY_BUFFER
,這是一個用于存儲頂點屬性(如位置、顏色等)的緩沖區目標。- 使用
glBufferData
將頂點數據傳輸到GPU的緩沖區中。4. 索引緩沖區對象 (
ibo
)unsigned int ibo; glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);
- 生成一個索引緩沖區對象(IBO),用于存儲索引數據。
- 將索引數據綁定到
GL_ELEMENT_ARRAY_BUFFER
,這是一個用于存儲頂點索引的緩沖區目標。- 使用
glBufferData
將索引數據傳輸到GPU的緩沖區中。如何聯系起來
- 頂點緩沖區對象 (
buffer
)?存儲了頂點的位置數據。- 索引緩沖區對象 (
ibo
)?存儲了頂點的索引數據,這些索引數據告訴OpenGL如何使用頂點緩沖區中的頂點來繪制幾何圖形。在
main
函數中,使用glDrawElements
函數來繪制幾何圖形:glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
GL_TRIANGLES
:指定繪制的圖形類型為三角形。6
:指定索引的數量(即indices
數組的長度)。GL_UNSIGNED_INT
:指定索引數據的類型為無符號整數。nullptr
:指定索引數據在緩沖區中的偏移量為0。因為索引緩沖區已經綁定到GL_ELEMENT_ARRAY_BUFFER
,OpenGL會自動從該緩沖區中讀取索引數據。詳細聯系過程
頂點數據綁定:
position
數組中存儲了四個頂點的坐標。- 使用
glVertexAttribPointer
配置頂點屬性指針:glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);
- 這里,
0
表示頂點屬性的索引,2
表示每個頂點有兩個分量(x和y坐標),GL_FLOAT
表示數據類型為浮點數。索引數據綁定:
indices
數組中存儲了繪制兩個三角形所需的頂點索引。- 使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
將索引數據綁定到GL_ELEMENT_ARRAY_BUFFER
。繪制過程:
- 當調用
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr)
時,OpenGL會按照indices
數組中的索引順序從position
數組中獲取頂點數據。- 例如:
- 第一個三角形的索引是?
0, 1, 2
,OpenGL會獲取position
數組中的第0個、第1個和第2個頂點來繪制第一個三角形。- 第二個三角形的索引是?
2, 3, 0
,OpenGL會獲取position
數組中的第2個、第3個和第0個頂點來繪制第二個三角形。總結
- 頂點緩沖區對象 (
buffer
)?和?索引緩沖區對象 (ibo
)?分別存儲頂點數據和索引數據。glDrawElements
?函數使用索引數據來決定如何從頂點緩沖區中提取頂點數據來繪制幾何圖形。- 這種方法可以減少頂點數據的冗余存儲,提高內存效率。
二、完整代碼
2.1完全注釋代碼
#include <GL/glew.h>
#include <GLFW/glfw3.h>#include<iostream>
#include<fstream>
#include<string>
#include<sstream>//功能:定義ShaderProgramSource結構體,用于存儲著色器代碼
struct ShaderProgramSource
{std::string VertexSource;std::string FragmentSource;
};//功能:解析著色器代碼文件。
static ShaderProgramSource ParseShader(const std::string& filepath)
{//功能:打開文件流std::ifstream stream(filepath);//定義著色器類型enum class ShaderType{NONE=-1,VERTEX=0,FRAGMENT=1};//該變量用于存儲著色器代碼std::string line;//該變量用于存儲著色器類型std::stringstream ss[2];//該變量是當前著色器類型ShaderType type = ShaderType::NONE;//功能:讀取文件中的每一行內容,直到文件結束while (getline(stream, line)){//如果當前行包含#shader,則說明接下來是著色器代碼if (line.find("#shader") != std::string::npos){//如果當前行包含vertex,則說明接下來是頂點著色器代碼if (line.find("vertex") != std::string::npos){type = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos){type = ShaderType::FRAGMENT;}}else{//否則,將當前行添加到對應著色器代碼的stringstream中ss[(int)type] << line << '\n';}}//返回ShaderProgramSource結構體return { ss[0].str(), ss[1].str() };
}//功能:編譯著色器代碼
static unsigned int CompilesShader(unsigned int type, const std::string& source)
{//功能:創建著色器對象unsigned int id = glCreateShader(type);//功能:設置著色器源代碼.const char* src = source.c_str();//功能:替換著色器對象中的源代碼。將該id的指定著色器的源代碼設置為src指針指向的字符串glShaderSource(id, 1, &src, nullptr);//功能:編譯著色器對象的源代碼glCompileShader(id);//設置返回著色器的對象IDint result;//功能:從著色器對象返回一個參數,表示編譯是否成功。glGetShaderiv(id, GL_COMPILE_STATUS, &result);//如果編譯失敗,則輸出錯誤信息if (result == GL_FALSE){int length;//功能:獲取編譯錯誤信息的長度glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);//分配內存,用于存儲編譯錯誤信息char* message = (char*)alloca(length*sizeof(char));//功能:獲取編譯錯誤信息glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile shader!" << (type == GL_VERTEX_SHADER? "Vertex" : "Fragment") << "shader!" << std::endl;std::cout << message << std::endl;//刪除著色器對象glDeleteShader(id);return 0;}//TODO:錯誤處理ingreturn id;
}//功能:創建著色器程序
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{//創建程序對象unsigned int program = glCreateProgram();//編譯頂點著色器對象unsigned int vs = CompilesShader(GL_VERTEX_SHADER, vertexShader);//編譯片段著色器對象unsigned int fs = CompilesShader(GL_FRAGMENT_SHADER, fragmentShader);//功能:將編譯好的著色器對象附加到程序對象中glAttachShader(program, vs);glAttachShader(program, fs);//功能:鏈接程序對象glLinkProgram(program);//功能:驗證著色器程序對象是否可以在當前OpenGL狀態中執行。檢查著色器程序的完整性和可執行性。glValidateProgram(program);//刪除著色器對象,因為它們已經被鏈接到程序對象中glDeleteShader(vs);glDeleteShader(fs);//返回著色器程序return program;
}int main(void)
{GLFWwindow* window;//初始化glfwif (!glfwInit())return -1;//創建一個窗口模式的窗口并設置OpenGL上下文window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window)//如果窗口創建失敗,則終止程序{glfwTerminate();//釋放glfw資源return -1;}//設置當前窗口的上下文,之后所有的OpenGL調用都會在這個上下文中進行glfwMakeContextCurrent(window);//初始化GLEWif (glewInit() != GLEW_OK)std::cout << "Error!" << std::endl;//打印OpenGL版本信息std::cout << glGetString(GL_VERSION) << std::endl;//準備數據float position[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f,0.5f,-0.5f,0.5f,};//定義頂點索引緩存,用于標定頂點順序unsigned int indices[] = {0,1,2,2,3,0};//定義緩沖區對象unsigned int buffer;//功能:生成緩沖區對象,并將數據寫入緩沖區glGenBuffers(1, &buffer);//功能:將緩沖區對象綁定到目標glBindBuffer(GL_ARRAY_BUFFER, buffer);//功能:將數據寫入緩沖區glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);//功能:配置頂點屬性指針glEnableVertexAttribArray(0);//功能:指定頂點屬性數組的索引、大小、數據類型、是否歸一化、偏移量、數據指針glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);//索引緩沖區對象unsigned int ibo;//功能:生成緩沖區對象,并將數據寫入緩沖區glGenBuffers(1, &ibo);//功能:將緩沖區對象綁定到目標.沒有綁定為數組緩沖區//參數:1.GL_ELEMENT_ARRAY_BUFFER:指定目標為索引緩沖區對象//2.ibo:索引緩沖區對象IDglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);//功能:將數據寫入緩沖區//參數:1.GL_ELEMENT_ARRAY_BUFFER:指定目標為索引緩沖區對象//2.6*sizeof(unsigned int):索引數據大小//3.indices:索引數據指針//4.GL_STATIC_DRAW:指定數據不會改變glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);//解析著色器代碼文件ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;//創建著色器程序unsigned int shader = CreateShader(vertexShader, fragmentShader);//使用著色器程序glUseProgram(shader);//渲染循環,直到窗口被關閉while (!glfwWindowShouldClose(window)){//清除顏色緩沖區glClear(GL_COLOR_BUFFER_BIT);//glDrawArrays(GL_TRIANGLES, 0, 6);//功能:繪制三角形//參數:1.GL_TRIANGLES:繪制三角形//2.6:頂點數量//3.GL_UNSIGNED_INT:索引數據類型//4.nullptr:索引數據指針//因為索引緩沖區已經綁定到GL_ELEMENT_ARRAY_BUFFER目標,所以這里不需要再傳入索引數據指針glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);//刷新緩沖區并交換窗口glfwSwapBuffers(window);//處理窗口事件,如鍵盤輸入、鼠標移動等glfwPollEvents();}//刪除著色器程序//glDeleteProgram(shader);//釋放 GLFW 庫占用的所有資源。glfwTerminate();return 0;
}
2.2簡潔注釋代碼
#include <GL/glew.h>
#include <GLFW/glfw3.h>#include<iostream>
#include<fstream>
#include<string>
#include<sstream>//功能:定義ShaderProgramSource結構體,用于存儲著色器代碼
struct ShaderProgramSource
{std::string VertexSource;std::string FragmentSource;
};//功能:解析著色器代碼文件。
static ShaderProgramSource ParseShader(const std::string& filepath)
{//功能:打開文件流std::ifstream stream(filepath);//定義著色器類型enum class ShaderType{NONE=-1,VERTEX=0,FRAGMENT=1};//該變量用于存儲著色器代碼std::string line;//該變量用于存儲著色器類型std::stringstream ss[2];//該變量是當前著色器類型ShaderType type = ShaderType::NONE;//功能:讀取文件中的每一行內容,直到文件結束while (getline(stream, line)){//如果當前行包含#shader,則說明接下來是著色器代碼if (line.find("#shader") != std::string::npos){//如果當前行包含vertex,則說明接下來是頂點著色器代碼if (line.find("vertex") != std::string::npos){type = ShaderType::VERTEX;}else if (line.find("fragment") != std::string::npos){type = ShaderType::FRAGMENT;}}else{//否則,將當前行添加到對應著色器代碼的stringstream中ss[(int)type] << line << '\n';}}//返回ShaderProgramSource結構體return { ss[0].str(), ss[1].str() };
}//功能:編譯著色器代碼
static unsigned int CompilesShader(unsigned int type, const std::string& source)
{//功能:創建著色器對象unsigned int id = glCreateShader(type);//功能:設置著色器源代碼.const char* src = source.c_str();//功能:替換著色器對象中的源代碼。將該id的指定著色器的源代碼設置為src指針指向的字符串glShaderSource(id, 1, &src, nullptr);//功能:編譯著色器對象的源代碼glCompileShader(id);//設置返回著色器的對象IDint result;//功能:從著色器對象返回一個參數,表示編譯是否成功。glGetShaderiv(id, GL_COMPILE_STATUS, &result);//如果編譯失敗,則輸出錯誤信息if (result == GL_FALSE){int length;//功能:獲取編譯錯誤信息的長度glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);//分配內存,用于存儲編譯錯誤信息char* message = (char*)alloca(length*sizeof(char));//功能:獲取編譯錯誤信息glGetShaderInfoLog(id, length, &length, message);std::cout << "Failed to compile shader!" << (type == GL_VERTEX_SHADER? "Vertex" : "Fragment") << "shader!" << std::endl;std::cout << message << std::endl;//刪除著色器對象glDeleteShader(id);return 0;}//TODO:錯誤處理ingreturn id;
}//功能:創建著色器程序
static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader)
{//創建程序對象unsigned int program = glCreateProgram();//編譯頂點著色器對象unsigned int vs = CompilesShader(GL_VERTEX_SHADER, vertexShader);//編譯片段著色器對象unsigned int fs = CompilesShader(GL_FRAGMENT_SHADER, fragmentShader);//功能:將編譯好的著色器對象附加到程序對象中glAttachShader(program, vs);glAttachShader(program, fs);//功能:鏈接程序對象glLinkProgram(program);//功能:驗證著色器程序對象是否可以在當前OpenGL狀態中執行。檢查著色器程序的完整性和可執行性。glValidateProgram(program);//刪除著色器對象,因為它們已經被鏈接到程序對象中glDeleteShader(vs);glDeleteShader(fs);//返回著色器程序return program;
}int main(void)
{GLFWwindow* window;//初始化glfwif (!glfwInit())return -1;//創建一個窗口模式的窗口并設置OpenGL上下文window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);if (!window)//如果窗口創建失敗,則終止程序{glfwTerminate();//釋放glfw資源return -1;}//設置當前窗口的上下文,之后所有的OpenGL調用都會在這個上下文中進行glfwMakeContextCurrent(window);//初始化GLEWif (glewInit() != GLEW_OK)std::cout << "Error!" << std::endl;//打印OpenGL版本信息std::cout << glGetString(GL_VERSION) << std::endl;//準備數據float position[] = {-0.5f, -0.5f,0.5f, -0.5f,0.5f,0.5f,-0.5f,0.5f,};//定義頂點索引緩存,用于標定頂點順序unsigned int indices[] = {0,1,2,2,3,0};//定義緩沖區對象unsigned int buffer;//功能:生成緩沖區對象,并將數據寫入緩沖區glGenBuffers(1, &buffer);//功能:將緩沖區對象綁定到目標glBindBuffer(GL_ARRAY_BUFFER, buffer);//功能:將數據寫入緩沖區glBufferData(GL_ARRAY_BUFFER, 2 * 4 * sizeof(float), position, GL_STATIC_DRAW);//功能:配置頂點屬性指針glEnableVertexAttribArray(0);//功能:指定頂點屬性數組的索引、大小、數據類型、是否歸一化、偏移量、數據指針glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);//索引緩沖區對象unsigned int ibo;//功能:生成緩沖區對象,并將數據寫入緩沖區glGenBuffers(1, &ibo);//功能:將緩沖區對象綁定到目標.沒有綁定為數組緩沖區//參數:1.GL_ELEMENT_ARRAY_BUFFER:指定目標為索引緩沖區對象//2.ibo:索引緩沖區對象IDglBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);//功能:將數據寫入緩沖區glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);//解析著色器代碼文件ShaderProgramSource source = ParseShader("res/shaders/Basic.shader");std::string vertexShader = source.VertexSource;std::string fragmentShader = source.FragmentSource;//創建著色器程序unsigned int shader = CreateShader(vertexShader, fragmentShader);//使用著色器程序glUseProgram(shader);//渲染循環,直到窗口被關閉while (!glfwWindowShouldClose(window)){//清除顏色緩沖區glClear(GL_COLOR_BUFFER_BIT);//glDrawArrays(GL_TRIANGLES, 0, 6);//功能:繪制三角形//參數:1.GL_TRIANGLES:繪制三角形glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);//刷新緩沖區并交換窗口glfwSwapBuffers(window);//處理窗口事件,如鍵盤輸入、鼠標移動等glfwPollEvents();}//刪除著色器程序//glDeleteProgram(shader);//釋放 GLFW 庫占用的所有資源。glfwTerminate();return 0;
}