開始學習OpenGL,參考的是著名的LearnOpenGL這個網站,在這里做一些總結性的記錄,只是方便自己日后查找或者記錄自己的一些拓展思考,關于OpenGL的具體內容請移步:
https://learnopengl-cn.github.io/
或英文原版:https://learnopengl.com/
配置環境
LearnOpenGL中使用了GLFW和GLAD兩個庫來配置環境,原文已經很詳細地列出了所有步驟,就不再多說了,獲取兩個庫之后在Visual Studio的專案屬性中的VC++目錄中,將兩個庫的include檔案夾加入包含目錄,將GLFW的lib檔案夾加入庫目錄,然后在聯結器的輸入中把glfw3.lib這個檔案加入附加依賴項,最后別忘記把glad.c這個檔案加入到工程中,


初始化OpenGL
首先包含之前引入的兩個庫的頭檔案,注意一定要先引入glad.h
#include <glad/glad.h>
#include <GLFW/glfw3.h>
開始使用OpenGL繪圖之前,要先初始化OpenGL環境,
glfwInit();//初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//告訴glfw使用OpenGL3.3版本
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//告訴glfw使用OpenGL核心(core)模式
創建視窗
使用glfwCreateWindow創建一個視窗物件,傳入視窗的寬度、高度和視窗名字,
創建視窗成功之后就將視窗設定為當前執行緒主背景關系,
GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);//創建視窗物件
if (window == NULL)//若視窗生成失敗則退出程式
{
std::cout << "Create Window failed" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);//通知GLFW將我們視窗的背景關系設定為當前執行緒的主背景關系
初始化GLAD
呼叫OpenGL的函式之前需初始化GLAD用于管理函式指標,
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
視口設定
最后還需要設定OpenGL的視口大小,并撰寫用戶改變視窗大小時的回呼函式,
glViewport(0, 0, 800, 600);//設定視口,前兩個引數為視口左下角位置,后兩個為視口寬高
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//設定回呼函式,用戶改變視窗大小時呼叫
回呼函式:
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
渲染回圈
做好這些準備作業之后就可以開始通過回圈來持續渲染我們要顯示的內容了,在回圈體中我們需要回應用戶的按鍵,渲染并交換緩沖和檢查事件,此處使用glClearColor來設定背景色并清空螢屏緩沖,這樣螢屏就會一直渲染為我們設定的背景色,
while (!glfwWindowShouldClose(window))//render loop
{
processInput(window);//按鍵回應
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//設定顏色
glClear(GL_COLOR_BUFFER_BIT);//清空螢屏的顏色緩沖
glfwSwapBuffers(window);//交換前緩沖和后緩沖
glfwPollEvents();//檢查有沒有觸發什么事件
}
glfwTerminate();//清理所有的資源并正確地退出應用程式
return 0;
按鍵回應函式,當用戶按下ESC鍵時關閉視窗:
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window,true);
}
}
繪制三角形
準備作業就緒,終于可以開始繪制了,繪制第一個三角形被LearnOpenGL的作者稱之為入門現代OpenGL的最難部分,舊版本的OpenGL使用立即渲染模式,只需幾行代碼就能繪制出一個簡單圖形,但現代OpenGL使用核心渲染模式,繪圖要基于著色器和緩沖區來進行,導致要畫出一個三角形之前,需要掌味訓本的著色器撰寫和控制OpenGL中各緩沖區的知識,對我這個沒有圖形編程基礎的人來說第一次看完這部分內容的時候說實話是很蒙圈的,但理解了之后也會發現這樣的設計在渲染大量復雜物體的時候相比于舊的立即渲染模式是非常優越的,
繪制一個三角形(或者說任何圖形)大概分為這幾個步驟:
準備頂點資料
首先當然要知道這個圖形的所有頂點,OpenGL的坐標系取值在-1.0到1.0之間,所以首先要準備好包含頂點資料的陣列:
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
撰寫頂點著色器和片元著色器
OpenGL使用GLSL語言撰寫著色器,語法類似于C語言,關于這兩個著色器的具體原理需要了解計算機圖形渲染管線的知識,LearnOpenGL里說的也不是很詳細,目前只需要知道頂點著色器用于處理每個頂點,片元著色器用于處理光柵化之后的每個像素即可,這些代碼的具體含義LearnOpenGL里有詳細解釋,不再詳述,
const char* vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char* fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
創建并編譯Shader
unsigned int VertexShader;
VertexShader = glCreateShader(GL_VERTEX_SHADER);//創建Shader
glShaderSource(VertexShader, 1, &vertexShaderSource, NULL);//把著色器原始碼附加到著色器物件上
glCompileShader(VertexShader);//編譯Shader
檢查Shader是否編譯成功,若失敗則列印日志:
int success;
char infoLog[512];
glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &success);//檢測編譯時錯誤
if (!success)
{
glGetShaderInfoLog(VertexShader, 512, NULL, infoLog);//如果編譯失敗,用glGetShaderInfoLog獲取錯誤訊息
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
對片元著色器也做相同操作:
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
鏈接著色器程式
創建一個總著色器程式,并將頂點和片元著色器鏈接上去,后續就可以使用這個ShaderProgram來直接渲染物體,
//創建著色器程式
unsigned int ShaderProgram;
ShaderProgram = glCreateProgram();
glAttachShader(ShaderProgram, VertexShader);
glAttachShader(ShaderProgram, fragmentShader);
glLinkProgram(ShaderProgram);//把之前編譯的著色器附加到程式物件上,然后用glLinkProgram鏈接它們
//檢測鏈接著色器程式是否失敗,并獲取相應的日志
glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(ShaderProgram, 512, NULL, infoLog);
//cout...
}
鏈接完畢后就可以洗掉掉之前創建的Shader物件了:
glDeleteShader(VertexShader);
glDeleteShader(fragmentShader);
創建緩沖物件
OpenGL中有多種緩沖物件用于存放各種資料,在繪制之前要將資料先放入緩沖區,

對每個緩沖物件都有如下步驟:
- 創建緩沖物件
- 系結緩沖區
- 將資料放入緩沖區
我們先來創建VAO和VBO物件:
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);//使用glGenBuffers函式和一個緩沖ID生成一個VBO物件
glBindBuffer(GL_ARRAY_BUFFER, VBO);//使用glBindBuffer函式把新創建的緩沖系結到GL_ARRAY_BUFFER目標上
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把之前定義的頂點資料復制到緩沖的記憶體
然后告訴OpenGL如何決議頂點資料并啟用頂點屬性:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//告訴OpenGL該如何決議頂點資料(應用到逐個頂點屬性上)
glEnableVertexAttribArray(0);//以頂點屬性位置值作為引數,啟用頂點屬性
VAO用于存盤隨后的頂點屬性呼叫,這樣的好處就是,當配置頂點屬性指標時,你只需要將那些呼叫執行一次,之后再繪制物體的時候只需要系結相應的VAO就行了 ,VBO用于存放頂點資料并發送到GPU上,
繪制物體
接下來只需要在渲染回圈中呼叫繪制函式即可,
while (!glfwWindowShouldClose(window))//render loop
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//設定顏色
glClear(GL_COLOR_BUFFER_BIT);//清空螢屏的顏色緩沖
glUseProgram(ShaderProgram);//激活程式物件
glBindVertexArray(VAO);//這里由于只有一個物件所以其實不需要每次都系結VAO,但若有多個需要繪制的物件則需要切換系結不同的VAO
glDrawArrays(GL_TRIANGLES, 0, 3);//繪制
glfwSwapBuffers(window);//交換前緩沖和后緩沖
glfwPollEvents();//檢查有沒有觸發什么事件
}
繪制三角形完成!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/2105.html
標籤:其他
