2015-11-20 221 views
4

我想了解使用矩陣的opengl中的攝像頭。OpenGL模型,視圖,投影矩陣

我寫了一個簡單的着色器,看起來像這樣:

#version 330 core 

layout (location = 0) in vec3 a_pos; 
layout (location = 1) in vec4 a_col; 

uniform mat4 u_mvp_mat; 
uniform mat4 u_mod_mat; 
uniform mat4 u_view_mat; 
uniform mat4 u_proj_mat; 

out vec4 f_color; 

void main() 
{ 
    vec4 v = u_mvp_mat * vec4(0.0, 0.0, 1.0, 1.0); 
    gl_Position = u_mvp_mat * vec4(a_pos, 1.0); 
    //gl_Position = u_proj_mat * u_view_mat * u_mod_mat * vec4(a_pos, 1.0); 
    f_color = a_col; 
} 

這是一個有點冗長,但那是因爲我測試通過無論是在模型,視圖或投影矩陣,做乘法的GPU或者在cpu上進行乘法運算並傳入mvp矩陣,然後執行mvp *位置矩陣乘法。

據我所知,後者可以提供性能提升,但繪製1個quad我並沒有在這一點上看到任何性能問題。

現在我使用此代碼從我的着色器中獲取位置並創建模型視圖和投影矩陣。

pos_loc = get_attrib_location(ce_get_default_shader(), "a_pos"); 
col_loc = get_attrib_location(ce_get_default_shader(), "a_col"); 
mvp_matrix_loc = get_uniform_location(ce_get_default_shader(), "u_mvp_mat"); 
model_mat_loc = get_uniform_location(ce_get_default_shader(), "u_mod_mat"); 
view_mat_loc = get_uniform_location(ce_get_default_shader(), "u_view_mat"); 
proj_matrix_loc = 
    get_uniform_location(ce_get_default_shader(), "u_proj_mat"); 

float h_w = (float)ce_get_width() * 0.5f; //width = 320 
float h_h = (float)ce_get_height() * 0.5f; //height = 480 

model_mat = mat4_identity(); 
view_mat = mat4_identity(); 
proj_mat = mat4_identity(); 

point3* eye = point3_new(0, 0, 0); 
point3* center = point3_new(0, 0, -1); 
vec3* up = vec3_new(0, 1, 0); 

mat4_look_at(view_mat, eye, center, up); 
mat4_translate(view_mat, h_w, h_h, -20); 

mat4_ortho(proj_mat, 0, ce_get_width(), 0, ce_get_height(), 1, 100); 

mat4_scale(model_mat, 30, 30, 1); 

mvp_mat = mat4_identity(); 

之後我設置我的vao和vbo的然後準備做渲染。

glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
glClear(GL_COLOR_BUFFER_BIT); 
glUseProgram(ce_get_default_shader()->shader_program); 
glBindVertexArray(vao); 

mvp_mat = mat4_multi(mvp_mat, view_mat, model_mat); 
mvp_mat = mat4_multi(mvp_mat, proj_mat, mvp_mat); 

glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(mvp_mat)); 

glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat)); 
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(view_mat)); 
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(proj_mat)); 

glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0); 
glBindVertexArray(0); 

假設所有矩陣數學是正確的,我想抽象視圖和投影矩陣出到照相機結構以及模型矩陣爲精靈結構體,這樣我可以避免所有這些矩陣數學和使事情更容易使用。

該矩陣乘法的順序是:

Projection * View * Model * Vector 

所以當精靈保持模型矩陣相機將持有投影和視圖矩陣。

然後在將數據發送到GPU進行矩陣乘法之前,先進行所有相機轉換和精靈轉換。

如果我記得矩陣乘法不可交換所以 view * projection * model會導致錯誤的結果矩陣。

僞代碼

glClearxxx(....); 
glUseProgram(..); 
glBindVertexArray(..); 

mvp_mat = mat4_identity(); 
proj_mat = camera_get_proj_mat(); 
view_mat = camera_get_view_mat(); 
mod_mat = sprite_get_transform_mat(); 

mat4_multi(mvp_mat, view_mat, mod_mat); //mvp holds model * view 
mat4_multi(mvp_mat, proj_mat, mvp_mat); //mvp holds proj * model * view 

glUniformMatrix4fv(mvp_mat, 1, GL_FALSE, mat4_get_data(mvp_mat)); 

glDrawElements(...); 
glBindVertexArray(0); 

那是一個高性能的方式去這樣做是可擴展的?

回答

2

這是一個高性能的方式去做這個可擴展的嗎?

是的,除非你有一個非常特殊的用例,這與標準非常不一樣。

您應該經常擔心的最後一件事是關於從相機中檢索模型視圖和投影矩陣的性能。

這是因爲這些矩陣通常只需要每個視口每幀獲取一次。在scanline-rasterizing基元的時候,在一幀中可能會出現數百萬次的其他工作,並且將矩陣拉出相機只是一個簡單的恆定時間操作。

所以通常情況下,您只是想盡可能方便。在我的情況下,我通過一箇中央SDK中的函數指針的抽象接口,然後函數即時計算 proj/mv/ti_mv矩陣,從用戶定義的與攝像頭相關的屬性中。儘管如此,它從來沒有作爲一個熱點出現 - 它甚至都沒有出現在剖析器中。

有更昂貴的東西需要擔心。可伸縮性意味着規模 - 從相機中檢索矩陣的複雜性不會縮放。要渲染的三角形或四邊形或線條或其他基元的數量可以縮放,碎片着色器中處理的碎片數量可以縮放。相機通常不會縮放,除非相對於視口的數量而言,並且任何人都無法使用一百萬個視口。

+0

我明白了你的可擴展性,它確實有道理。還有一個問題。假設我喜歡你的建議,並讓相機返回一個視圖*投影矩陣,然後不會乘以模型矩陣產生錯誤的結果,因爲它會說'model * proj * view',這不是正確的方法做矩陣乘法。我想有可能將模型矩陣傳遞給相機,並讓它做正確的事情,但這樣會使精靈系列非常靠近相機,不是嗎? – user1610950

+0

或者我會建議,從相機界面的水平,返回投影和模型視圖矩陣。當對照相機進行渲染時,您可以在現場計算其餘部分(例如:用於正常計算的轉置逆模型視圖)。 –

+0

對於以正確的順序相乘矩陣,是的 - 你必須這樣做。但我不太清楚爲什麼這裏有一個問題 - 只需要按照獲得正確結果所需的順序將它們相乘即可。或者我錯過了一些難以做到的事情? –

2

我還沒有檢查位按位,但它通常看起來確定你在做什麼。

我想抽象視圖和投影矩陣伸到相機結構

這是一個最合適的想法;我幾乎無法想象一個沒有這種抽象的嚴肅的GL應用程序

這是一個高性能的方式去做這個可擴展的嗎?

伸縮性的一般約束是

  • 漫射和鏡面BRDFs(其也需要,順便說一句,光均勻,一個正常的屬性和計算正常矩陣if the scaling of the model is non-uniform的),並需要per-pixel illumination質量渲染。

  • 相同多重照明(例如太陽和密切的聚光燈)

  • 陰影貼圖!陰影貼圖? (每個光源?)

  • 透明度

  • 反射(鏡子,玻璃,水)

  • 紋理

正如你是否可以從列表中只有一個MVP統一體和一個頂點座標屬性,你不會走得太遠。

但制服的單純數量是迄今爲止不是性能的最關鍵點 - 看到你的代碼,我敢肯定,只有在需要時您將無法重新編譯着色器不必要的,更新的制服,使用Uniform Buffer Objects等。

問題是插入這些制服和維也納組織的數據。或不。


考慮人形網「愛麗絲」運行(這是一個網狀變形+翻譯)跨城市廣場上的大風(水會有漣漪)晚上(不止一個相關的光源),通過一個噴泉。

讓考慮到我們收集這一切由CPU和老派的一切手段只插即用數據呈現到着色器:

  • Alice的網格演變,因此她的維也納組織需要更新
  • 愛麗絲的網格會移動;因此所有受影響的陰影貼圖都需要更新(確定,假設它們是由GPU上的陰影照明循環生成的,但是如果你做錯了方式,你會在左右推送大量數據)
  • Alice在噴泉會來來去去
  • 翹的頭髮會被漩渦 - CPU可能有安靜的一個繁忙的時間,至少可以說是

(實際上後者是如此的困難,你將幾乎看不到任何中途逼真實時長時間開放的頭髮動畫,但令人驚訝(不,不是真的)許多小馬尾巴和短髮)

我們還沒有談到愛麗絲的裝束;讓我們只希望她穿着T恤和牛仔褲(不是寬襯衫和裙子,這需要摺疊和碰撞計算)。

正如你可能已經猜到,老派的做法並沒有把我們帶到很遠的地步,因此,在CPU和GPU之間找到適合的操作。

此外,應該考慮在早期階段對計算進行並行化。將數據儘可能地平整爲合理的大小是有利的,因此只需將指針和大小放入gl-call並且投標數據就可以在沒有任何複製,重新排列,循環或更進一步的情況下告別。 。

這是我今天對GL性能和可擴展性的2分錢智慧。

+0

你在這個答案中提出的要點實際上是最初讓我寫這個問題的。雖然看起來離我目前的實施還有很遠的距離,但我寧願避免把自己挖到溝裏去。有很多事情需要考慮,謝謝,我贊成這個答案。 – user1610950