OpenGl學習筆記

基本介紹

  • OpenGl是一個規範,他由顯卡製造商負責提供實現
  • 因此你使用的OpenGl出現了問題,升級對應的顯卡應該就能解決
  • OpenGl分爲立即渲染模式【固定渲染管道】和核心模式
  • 核心模式可以讓程序對圖形繪製過程擁有更多的控制,但同時也意味着更難,因爲他抽象了更多的細節
  • OpenGl的核心模式從3.3開始,剩下的版本都是在他的基礎上擴展的,核心架構沒有改變。
  • 所以當我們要使用OpenGl的某些高級特性的時候,需要知道對應的顯卡是否支持

擴展

  • 當顯卡公司提出某些新的特性的時候,會在驅動上以擴展的方式實現
  • 我們只要在代碼裏面詢問該顯卡是否支持該擴展就可以直接使用該特性
  • 而不必等待該特性發展成爲OpenGl的特性一部分,一般來說,流行的擴展最終會變成擴展

狀態機,對象

  • OpenGl本身是一個巨大的狀態機
  • 我們可以創建多個設置對象,通過將不同的設置對象和上下文綁定,來進行不同的操作

GLFW

  • 是一個提供給基礎操作的庫
  • 在每一個操作系統上面,創建窗口和處理用戶輸入等操作都是不同的,OpenGl從這些細節抽象了出來
  • 這意味着我們要自己處理這些操作,不過還好有一些c庫可以直接使用比如GLFW
  • 我們需要下載其源碼,然後自己進行編譯,這樣的目的是爲了保證和我們的cpu操作系統匹配
  • 但是每個人用的IDE都是不同的,要根據源碼構建對應的項目工程文件是一件很繁瑣的事情
  • 因此我們需要用到CMake

GLAD

  • 是一個用來提供查詢函數地址的庫
  • 因爲OpenGL只是一個標準/規範,具體的實現是由驅動開發商針對特定顯卡實現的。
  • 由於OpenGL驅動版本衆多,它大多數函數的位置都無法在編譯時確定下來,需要在運行時查詢。
  • 所以任務就落在了開發者身上,開發者需要在運行時獲取函數地址並將其保存在一個函數指針中供以後使用。
  • 取得地址的方法因平臺而異,在Windows上會是類似這樣:
// 定義函數原型
typedef void (*GL_GENBUFFERS) (GLsizei, GLuint*);
// 找到正確的函數並賦值給函數指針
GL_GENBUFFERS glGenBuffers  = (GL_GENBUFFERS)wglGetProcAddress("glGenBuffers");
// 現在函數可以被正常調用了
GLuint buffer;
glGenBuffers(1, &buffer);
  • 有些庫能簡化此過程,其中GLAD是目前最新,也是最流行的庫。

畫一個窗口

  • 首先對glfw進行初始化,設定gl版本和使用範圍
  • 然後創建窗口window
  • 再對glad進行初始化,因爲接下來要使用opengl的函數
  • 設置視口大小
  • 設置渲染循環,在渲染循環裏面接收輸入,交換渲染緩衝等
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	GLFWwindow* window = glfwCreateWindow(80, 600, "jiayajie", NULL, NULL);
	if (window==NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	//將窗口設置爲該線程上下文
	glfwMakeContextCurrent(window);

	//對glad進行初始化
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "failed to initialize GLAD" << std::endl;
		return -1;
	}

	//定義視口大小 不必和glfw相同
	glViewport(0, 0, 10, 10);

	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	//檢測glfw是否被要求退出 如果退出就直接返回false
	while (!glfwWindowShouldClose(window))
	{
		//處理輸入
		processInput(window);

		//渲染指令
		glClearColor(0.2, 1, 1, 1);  //狀態設置
		glClear(GL_COLOR_BUFFER_BIT); //使用指定顏色清空屏幕 使用狀態


		glfwSwapBuffers(window); //交換顏色緩衝
		glfwPollEvents(); //檢測輸入輸出事件

	}

	glfwTerminate();
	return 0;
}

//設置窗口改變時回調函數
void framebuffer_size_callback(GLFWwindow* window, int width, int height) {

	glViewport(0, 0, width, height);

}

void processInput(GLFWwindow *window) {

	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}

圖形渲染

  • 圖元裝配 將頂點渲染成指定的圖元形狀點,線還是面
  • 幾何着色器 它可以通過產生新頂點構造出新的(或是其它的)圖元來生成其他形狀
  • 光柵化這裏它會把圖元映射爲最終屏幕上相應的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)
  • 在片段着色器運行之前會進行裁切,丟棄超出視圖像素
  • 片段着色器則是生成屏幕對應像素的顏色
  • 最後一個階段則是測試和混合,進行深度測試,該像素能否被看到,如果看不到則拋棄。進行透明度測試,

VBO與VAO

  • VBO是頂點緩衝對象,vertex buffer object,而VAO是頂點數組對象,vertex array object
  • VAO更類似於VBO的升級版本,他負責記錄VBO的各種配置,比如glEnableVertexAttribArray和glDisableVertexAttribArray的調用
  • 通過glVertexAttribPointer 設置的頂點屬性配置
  • 通過glVertexAttribPointer調用與頂點屬性關聯的頂點緩衝對象
  • 所以當我們繪製物體的時候只要綁定對應的VAO對象即可,繪製完畢再解綁VAO
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();

總結

  • 重新複習了一遍OpenGl的知識,分爲以下部分
  • Glfw處理輸入以及創建窗口
  • Glad負責查找對應驅動函數的位置
  • VBOBuffer是頂點數據緩衝,內容是頂點的各種屬性,包括位置,法線,顏色,貼圖座標
  • VAO負責記錄VBO的各種指針和狀態
  • 當VBO裏面很多位置都重合的時候可以使用索引緩衝對象,將必要的頂點定義出來,然後用其在數組中的索引來定義頂點。這樣就不用重複定義頂點
  • 在渲染一個對象的時候,頂點數據由VAO負責,紋理數據則是放置在對應紋理單元中【記得將shader 材質與紋理對應】,其後是Shader程序使用這些數據渲染出來
	lightingShader.setInt("material.diffuse", 0);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, diffuseMap); //漫反射貼圖
  • 最後使用glDrawArrays的時候就會將對應頂點數據三個爲一組畫成三角形
glDrawArrays(GL_TRIANGLES, 0, 36); //s
//或者
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) //glDrawElements 是使用索引緩衝

碎片知識

  • 通過uniform數據來在代碼裏面動態的改變shader程序裏面的數據
  • 通過開啓深度測試,可以出現距離感,否則每一幀都會完全覆蓋上一幀的圖片

變換

  • 我們可以通過矩陣對一個頂點進行變換
  • 位移,旋轉,縮放
  • 因此通過構建一系列的矩陣,然後在頂點着色器裏面對每個頂點進行相應變換,就可以得到對應效果
  • 矩陣一般分爲三個,model矩陣對模型自身進行變換
  • view 矩陣將圖像變換到以某個觀察位置爲原點的觀察空間中
  • projection矩陣對該觀察空間中的矩陣進行投影變換,進行裁剪,將範圍以外的物體丟掉,具有透視感
  • 最後得到的就是二維座標了
  • 因此攝像機本質上就是一個view矩陣,我們移動的時候是相對改變view矩陣的參數
  • 需要注意法線

光照

  • 光源類型分爲三種,平行光, 聚光燈, 點光源
  • 平行光只有方向,聚光燈是一個圓錐,有一個角度,和照射距離,點光源則只有一個照射距離。
  • 對於聚光燈和點光源而言,都需要有一個光照衰減函數。平行光不需要
  • 光照對於物體的影響可以分爲三個部分,環境光,高光,物體自身顏色。將每個燈光對於該物體影響疊加後輸出就是該物體最終的顏色
  • 然後就是各部分光照的計算方法了
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章