OpenGL 圖形庫的使用(八)—— 坐標系統之3D效果(二)

版本記錄

版本號 時間
V1.0 2017.09.05

前言

OpenGL 圖形庫項目中一直也沒用過,最近也想學著使用這個圖形庫,感覺還是很有意思,也就自然想著好好的總結一下,希望對大家能有所幫助。
1. OpenGL 圖形庫使用(一) —— 概念基礎
2. OpenGL 圖形庫使用(二) —— 渲染模式、對象、擴展和狀態機
3. OpenGL 圖形庫使用(三) —— 著色器、數據類型與輸入輸出
4. OpenGL 圖形庫使用(四) —— Uniform及更多屬性
5. OpenGL 圖形庫使用(五) —— 紋理
6. OpenGL 圖形庫使用(六) —— 變換
7. OpenGL 圖形庫的使用(七)—— 坐標系統之五種不同的坐標系統(一)

變換矩陣的組合

以下內容來自LearnOpenGL CN

上一篇文章中,每一個步驟都創建了一個變換矩陣:模型矩陣、觀察矩陣和投影矩陣。一個頂點坐標將會根據以下過程被變換到裁剪坐標:

Vclip = Mprojection ? Mview ? Mmodel ? Vlocal

注意矩陣運算的順序是相反的(記住我們需要從右往左閱讀矩陣的乘法)。最后的頂點應該被賦值到頂點著色器中的gl_PositionOpenGL將會自動進行透視除法和裁剪。

頂點著色器的輸出要求所有的頂點都在裁剪空間內,這正是我們剛才使用變換矩陣所做的。OpenGL然后對裁剪坐標執行透視除法從而將它們變換到標準化設備坐標。OpenGL會使用glViewPort內部的參數來將標準化設備坐標映射到屏幕坐標,每個坐標都關聯了一個屏幕上的點。這個過程稱為視口變換。


3D

既然我們知道了如何將3D坐標變換為2D坐標,我們可以開始使用真正的3D物體,而不是枯燥的2D平面了。

在開始進行3D繪圖時,我們首先創建一個模型矩陣。這個模型矩陣包含了位移、縮放與旋轉操作,它們會被應用到所有物體的頂點上,以變換它們到全局的世界空間。讓我們變換一下我們的平面,將其繞著x軸旋轉,使它看起來像放在地上一樣。這個模型矩陣看起來是這樣的:

glm::mat4 model;
model = glm::rotate(model, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));

通過將頂點坐標乘以這個模型矩陣,我們將該頂點坐標變換到世界坐標。我們的平面看起來就是在地板上,代表全局世界里的平面。

接下來我們需要創建一個觀察矩陣。我們想要在場景里面稍微往后移動,以使得物體變成可見的(當在世界空間時,我們位于原點(0,0,0))。要想在場景里面移動,先仔細想一想下面這個句子:

將攝像機向后移動,和將整個場景向前移動是一樣的。

這正是觀察矩陣所做的,我們以相反于攝像機移動的方向移動整個場景。因為我們想要往后移動,并且OpenGL是一個右手坐標系(Right-handed System),所以我們需要沿著z軸的正方向移動。我們會通過將場景沿著z軸負方向平移來實現。它會給我們一種我們在往后移動的感覺。

下圖就是一個右手坐標系的示例圖,這里是回顧了,不是新的知識。

目前來說,觀察矩陣是這樣的:

glm::mat4 view;
// 注意,我們將矩陣向我們要進行移動場景的反方向移動。
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));

最后我們需要做的是定義一個投影矩陣。我們希望在場景中使用透視投影,所以像這樣聲明一個投影矩陣:

glm::mat4 projection;
projection = glm::perspective(glm::radians(45.0f), screenWidth / screenHeight, 0.1f, 100.0f);

既然我們已經創建了變換矩陣,我們應該將它們傳入著色器。首先,讓我們在頂點著色器中聲明一個uniform變換矩陣然后將它乘以頂點坐標:

#version 330 core
layout (location = 0) in vec3 aPos;
...
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    // 注意乘法要從右向左讀
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    ...
}

我們還應該將矩陣傳入著色器(這通常在每次的渲染迭代中進行,因為變換矩陣會經常變動):

int modelLoc = glGetUniformLocation(ourShader.ID, "model"));
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
... // 觀察矩陣和投影矩陣與之類似

我們的頂點坐標已經使用模型、觀察和投影矩陣進行變換了,最終的物體應該會:

  • 稍微向后傾斜至地板方向。
  • 離我們有一些距離。
  • 有透視效果(頂點越遠,變得越小)

下面看一下效果圖。

它看起來就像是一個3D的平面,靜止在一個虛構的地板上。如果你得到的不是相同的結果,請檢查下完整的源代碼


更多的3D

到目前為止,我們一直都在使用一個2D平面,而且甚至是在3D空間里!所以,讓我們大膽地拓展我們的2D平面為一個3D立方體。要想渲染一個立方體,我們一共需要36個頂點(6個面 x 每個面有2個三角形組成 x 每個三角形有3個頂點),這36個頂點的位置你可以從這里獲取。

為了有趣一點,我們將讓立方體隨著時間旋轉:

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));

然后我們使用glDrawArrays來繪制立方體,但這一次總共有36個頂點。

glDrawArrays(GL_TRIANGLES, 0, 36);

應該能得到下面這樣的效果:

這的確有點像是一個立方體,但又有種說不出的奇怪。立方體的某些本應被遮擋住的面被繪制在了這個立方體其他面之上。之所以這樣是因為OpenGL是一個三角形一個三角形地來繪制你的立方體的,所以即便之前那里有東西它也會覆蓋之前的像素。因為這個原因,有些三角形會被繪制在其它三角形上面,雖然它們本不應該是被覆蓋的。

幸運的是,OpenGL存儲深度信息在一個叫做Z緩沖(Z-buffer)的緩沖中,它允許OpenGL決定何時覆蓋一個像素而何時不覆蓋。通過使用Z緩沖,我們可以配置OpenGL來進行深度測試。

1. Z緩沖

OpenGL存儲它的所有深度信息于一個Z緩沖(Z-buffer)中,也被稱為深度緩沖(Depth Buffer)。GLFW會自動為你生成這樣一個緩沖(就像它也有一個顏色緩沖來存儲輸出圖像的顏色)。深度值存儲在每個片段里面(作為片段的z值),當片段想要輸出它的顏色時,OpenGL會將它的深度值和z緩沖進行比較,如果當前的片段在其它片段之后,它將會被丟棄,否則將會覆蓋。這個過程稱為深度測試(Depth Testing),它是由OpenGL自動完成的。

然而,如果我們想要確定OpenGL真的執行了深度測試,首先我們要告訴OpenGL我們想要啟用深度測試;它默認是關閉的。我們可以通過glEnable函數來開啟深度測試。glEnableglDisable函數允許我們啟用或禁用某個OpenGL功能。這個功能會一直保持啟用/禁用狀態,直到另一個調用來禁用/啟用它。現在我們想啟用深度測試,需要開啟GL_DEPTH_TEST

glEnable(GL_DEPTH_TEST);

因為我們使用了深度測試,我們也想要在每次渲染迭代之前清除深度緩沖(否則前一幀的深度信息仍然保存在緩沖中)。就像清除顏色緩沖一樣,我們可以通過在glClear函數中指定DEPTH_BUFFER_BIT位來清除深度緩沖:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

重新運行下程序看看OpenGL是否執行了深度測試:

就是這樣!一個開啟了深度測試,各個面都是紋理,并且還在旋轉的立方體!如果你的程序有問題可以到這里下載源碼進行比對。

2. 更多的立方體

現在我們想在屏幕上顯示10個立方體。每個立方體看起來都是一樣的,區別在于它們在世界的位置及旋轉角度不同。立方體的圖形布局已經定義好了,所以當渲染更多物體的時候我們不需要改變我們的緩沖數組和屬性數組,我們唯一需要做的只是改變每個對象的模型矩陣來將立方體變換到世界坐標系中。

首先,讓我們為每個立方體定義一個位移向量來指定它在世界空間的位置。我們將在一個glm::vec3數組中定義10個立方體位置:

glm::vec3 cubePositions[] = {
  glm::vec3( 0.0f,  0.0f,  0.0f), 
  glm::vec3( 2.0f,  5.0f, -15.0f), 
  glm::vec3(-1.5f, -2.2f, -2.5f),  
  glm::vec3(-3.8f, -2.0f, -12.3f),  
  glm::vec3( 2.4f, -0.4f, -3.5f),  
  glm::vec3(-1.7f,  3.0f, -7.5f),  
  glm::vec3( 1.3f, -2.0f, -2.5f),  
  glm::vec3( 1.5f,  2.0f, -2.5f), 
  glm::vec3( 1.5f,  0.2f, -1.5f), 
  glm::vec3(-1.3f,  1.0f, -1.5f)  
};

現在,在游戲循環中,我們調用glDrawArrays 10次,但這次在我們渲染之前每次傳入一個不同的模型矩陣到頂點著色器中。我們將會在游戲循環中創建一個小的循環用不同的模型矩陣渲染我們的物體10次。注意我們也對每個箱子加了一點旋轉:

glBindVertexArray(VAO);
for(unsigned int i = 0; i < 10; i++)
{
  glm::mat4 model;
  model = glm::translate(model, cubePositions[i]);
  float angle = 20.0f * i; 
  model = glm::rotate(model, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
  ourShader.setMat4("model", model);

  glDrawArrays(GL_TRIANGLES, 0, 36);
}

這段代碼將會在每次新立方體繪制出來的時候更新模型矩陣,如此總共重復10次。然后我們應該就能看到一個擁有10個正在奇葩地旋轉著的立方體的世界。

你可以對照一下源代碼

后記

未完,待續~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容