1. 程式人生 > >圖形學複習要點

圖形學複習要點

這篇文章總結一下圖形學在遊戲開發工程師面試時常考(可能會考)的問題。雖然圖形學在面試中的比重比較小,但是還是要複習一下的。圖形學的考點分為三部分,一是渲染管線,二是數學尤其是線性代數和三維幾何。

一、渲染管線

1.1 有那幾個座標系(空間)?如何在空間之間轉換?

有五個座標系,分別是:
  1. 物體座標系(本地座標系)Local Space 或 Model Space
  2. 世界座標系 World Space 
  3. 觀察者座標系(攝像機座標系)View Space 
  4. 裁剪空間 Clipping Space 
  5. 螢幕空間 Screen Space 
其中前四個矩陣之間需要通過model, view , projection矩陣
變換,裁剪空間到螢幕空間通過視口變換進行;前三個是三維空間,後兩個是二維空間。
圖1.1 座標系(空間)的轉換 所有的變換都發生在頂點著色器,經過頂點著色器,所有頂點都變成了螢幕上的二維座標,下一步進行圖元裝配後進入幾何著色器。

1.2 三個重要的空間變換矩陣?作用?

需要注意這三個矩陣都在頂點著色器中應用。
  • Model matrix 模型矩陣。進行從物體座標到世界座標的轉換。控制了物體的平移、旋轉、縮放。在3D建模軟體中為模型座標,匯入遊戲後使用model matrix 進行大小、位置、角度的設定
  • View matrix 觀察矩陣。將世界座標系變換到觀察者座標系,通過一些列平移、旋轉的組合來移動整個場景(而不是移動攝像機,攝像機 是一個虛擬概念,事實上程式碼中並沒有攝像機camera,而是用view matrix來表示攝像機,然後把view matrix附加到每一個物體,來模擬攝像機
    ),用來模擬一個攝像機
  • projection matrix 投影矩陣。將觀察者座標系轉換到裁剪座標系。將3D座標投影到2D螢幕上,裁剪空間外的頂點會被裁剪掉,投影矩陣指定了座標的範圍。

1.3 視口變換是什麼?

視口變換髮生在投影到2D屏幕後,將投影之後歸一化的點對映到螢幕上指定的一塊區域。在OpenGL中,通過glViewPort指定。

1.4 渲染管線的流程?


下面的圖更形象
其中,gl_Position在從頂點著色器輸出後,會由OpenGL自己進行歸一化和視口變換:
片段處理程式的input是頂點處理程式的output經過了插值以後得到的值。

1.5 三種著色器分別有什麼用?完成了什麼過程?

  • 頂點著色器。計算頂點的位置,並將頂點投影在二維螢幕上。
  • 幾何著色器。將形狀(圖元)劃分為更多的形狀(圖元),影響後序的插值結果。
  • 片段著色器。根據頂點著色器和幾何著色器的輸出插值,計算每一個片元的顏色。之後進行測試和混合後生成最終的畫素。

1.6 什麼是光柵化?

光柵(柵格化或者畫素化)化負責的是整個渲染過程中的幾何成像環節,把幾何圖元(點、線,面)投影到成像平面並確定哪些畫素或採樣點被圖元覆蓋。舉例:
  • 輸入,一個三角形的三個頂點,(x0,y0,z0,w0) (x1,y1,z1,w1) (x2,y2,z2,w2)
  • 輸出,這個三角形會覆蓋螢幕上哪些畫素,可以認為是 Point2d [ ]

1.7 OpenGL中有哪幾種緩衝?都有什麼用?

  • 幀緩衝Frame Buffer, 用於建立零時的渲染上下文,幀緩衝是一些二維陣列和OpenG所使用的儲存區的集合:顏色快取、深度快取、模板快取和累計快取。預設情況下,OpenGL將幀緩衝區作為渲染最終目的地。此幀緩衝區完全由window系統生成和管理。這個預設的幀快取被稱作“window系統生成”(window-system-provided)的幀緩衝區。
    • 顏色緩衝 Color Buffer, 包含每個象素的顏色資訊。顏色資訊可以是顏色索引值(在顏色索引方式下),也可以是顏色的紅、綠、藍3個分量(在RGBA方式下),還可以存放表示物體透明程度的Alpha值。
    • 深度緩衝 Depth Buffer, 包含每個象素的深度值。深度值與z座標有關,描述物體上某點距離觀察點的遠近,也可以稱它為Z快取(Z Buffer) 。
    • 模板緩衝 Stencil Buffer, 包含物體的模板值。模板值具有遮蔽作用,用於控制繪製的區域,使螢幕上某些區域可畫,某些區域不可畫。
    • 累積快取(Accumulation Buffer) 包含顏色資訊。其可以合成一系列的繪製結果,實現某些特殊效果。
  • 頂點緩衝 Vertex Buffer, 用於快取頂點資料
  • 元素緩衝 Element Buffer,用於快取頂點序號資料

1.8 Alpha 混合的幾種方式?

通用公式: Color Src *Srcfactor + Dst Dstfactor 其中Color是混合結果,Src是源顏色向量也就是紋理本來的顏色,Dst是目標顏色向量也就是儲存在顏色緩衝中當前位置的顏色向量, srcfactor和dstfactor分別是源因子和目標因子。先進入顏色緩衝區的是目標顏色,比如在紅色方塊上繪製綠色方塊,則紅色是Dst,綠色是Src。不同的 factor 導致了不同的混合方式:
注意,顏色常數向量可以用glBlendColor函式分開來設定。OpenGL中使用 void glBlendFunc(GLenum sfactor, GLenum dfactor)設定混合方式,接收兩個引數,來設定源(source)和目標(destination)因子。OpenGL為我們定義了很多選項,我們把最常用的列在下面。注意,顏色常數向量[Math Processing Error]C¯constant可以用glBlendColor函式分開來設定。在使用alpha 混合前要開啟 glEnable(GL_BLEND);
最常用的混合方式是 glBlendFunc(GL_SRC_ALPHA,  GL_ONE_MINUS_SRC_ALPHA); 

1.9 顏色向量的計算

顏色向量(歸一化的)有兩種計算
  1. 數乘,n*color, n越大,結果越亮
  2. 點乘,colorA colorB , 是A與B的混合,越大,越亮

1.10 GLSL 著色器程式的建立


1.11 GLSL資料傳遞有幾種方式?

  • uniform變數, uniform變數是外部application程式傳遞給(vertex和fragment)shader的變數。因此它是application通過函式glUniform**()函式賦值的。在(vertex和fragment)shader程式內部,uniform變數就像是C語言裡面的常量(const ),它不能被shader程式修改。
  • attribute變數, attribute變數是隻能在vertex shader中使用的變數。(它不能在fragment shader中宣告attribute變數,也不能被fragment shader中使用)。一般用attribute變數來表示一些頂點的資料,如:頂點座標,法線,紋理座標,頂點顏色等。在application中,一般用函式glBindAttribLocation()來繫結每個attribute變數的位置,然後用函式glVertexAttribPointer()為每個attribute變數賦值。
  • varying(in/out)變數,varying變數是vertex和fragment shader之間做資料傳遞用的。一般vertex shader修改varying變數的值,然後fragment shader使用該varying變數的值。因此varying變數在vertex和fragment shader二者之間的宣告必須是一致的。application不能使用此變數。

1.12 為什麼要用齊次座標系

  1. 方便進行平移變換
  2. 能夠簡化透視投影的計算

1.13 馮氏光照模型由哪三部分構成?

http://learnopengl-cn.readthedocs.io/zh/latest/02%20Lighting/02%20Basic%20Lighting/ 光照要處理的就是光源顏色向量和物體顏色向量的點積。三種光照可以組合使用即 (ambient + diffuse + specular) * objectColor。
  1. 環境光  ambient .控制因素是 ambient strength環境光強度,和lightcolor數乘得到ambient環境光。然後在用ambient和物體顏色objectColor點乘。ambient strength由程式設計師指定。
  2. 漫反射 diffuse , 控制因素是 diff 散射因子,也是和lightcolor數乘得到diffuse漫反射光,然後在用diffuse河objectColor點乘。diff散射因子由法線與光線的夾角(點積)得到,漫反射光使物體上與光線排布越近的片段越能從光源處獲得更多的亮度。為了更好的理解漫反射光照:

    1. θ越大,光對片段顏色的影響越小,反過來光線越靠近法線,對物體顏色的影響就越大。
    2. float diff = max(dot(norm, lightDir), 0.0); 兩個向量之間的角度越大,散射因子就會越小:
  3. 鏡面反射 specular,控制因素是 spec 反射強度。和環境光照一樣,鏡面光照(Specular Lighting)同樣依據光的方向向量和物體的法向量,但是這次它也會依據觀察方向,例如玩家是從什麼方向看著這個片段的。鏡面光照根據光的反射特性。如果我們想象物體表面像一面鏡子一樣,那麼,無論我們從哪裡去看那個表面所反射的光,鏡面光照都會達到最大化。

    1. 通過反射法向量周圍光的方向計算反射向量。然後我們計算反射向量和視線方向的角度,如果之間的角度越小,那麼鏡面光的作用就會越大。它的作用效果就是,當我們去看光被物體所反射的那個方向的時候,我們會看到一個高光。

1.14 旋轉的三種方法

  • 旋轉矩陣 4x4
  • 尤拉角 yaw pitch roll 
  • 四元數

1.15 四元數 Quaternion 的概念和作用

四元數本質上是一種高階複數(聽不懂了吧。。。),是一個四維空間,相對於複數的二維空間。我們高中的時候應該都學過複數,一個複數由實部和虛部組成,即x = a + bi,i是虛數單位,如果你還記得的話應該知道i^2 = -1。而四元數其實和我們學到的這種是類似的,不同的是,它的虛部包含了三個虛數單位,i、j、k,即一個四元數可以表示為x = a + bi + cj + dk。那麼,它和旋轉為什麼會有關係呢?
在Unity裡,tranform元件有一個變數名為rotation,它的型別就是四元數。很多初學者會直接取rotation的x、y、z,認為它們分別對應了Transform面板裡R的各個分量。當然很快我們就會發現這是完全不對的。實際上,四元數的x、y、z和R的那三個值從直觀上來講沒什麼關係,當然會存在一個表示式可以轉換。

1.16 四元數、尤拉角、旋轉矩陣的優點和缺點

  • 矩陣旋轉
    • 優點:
      • 旋轉軸可以是任意向量;
    • 缺點:
      • 旋轉其實只需要知道一個向量+一個角度,一共4個值的資訊,但矩陣法卻使用了16個元素;
      • 而且在做乘法操作時也會增加計算量,造成了空間和時間上的一些浪費;

  • 尤拉旋轉
    • 優點:
      • 很容易理解,形象直觀;
      • 表示更方便,只需要3個值(分別對應x、y、z軸的旋轉角度);但按我的理解,它還是轉換到了3個3*3的矩陣做變換,效率不如四元數;
    • 缺點:
      • 之前提到過這種方法是要按照一個固定的座標軸的順序旋轉的,因此不同的順序會造成不同的結果;
      • 會造成萬向節鎖(Gimbal Lock)的現象。這種現象的發生就是由於上述固定座標軸旋轉順序造成的。理論上,尤拉旋轉可以靠這種順序讓一個物體指到任何一個想要的方向,但如果在旋轉中不幸讓某些座標軸重合了就會發生萬向節鎖,這時就會丟失一個方向上的旋轉能力,也就是說在這種狀態下我們無論怎麼旋轉(當然還是要原先的順序)都不可能得到某些想要的旋轉效果,除非我們打破原先的旋轉順序或者同時旋轉3個座標軸。這裡有個視訊可以直觀的理解下;
      • 由於萬向節鎖的存在,尤拉旋轉無法實現球面平滑插值;

  • 四元數旋轉
    • 優點:
      • 可以避免萬向節鎖現象;
      • 只需要一個4維的四元數就可以執行繞任意過原點的向量的旋轉,方便快捷,在某些實現下比旋轉矩陣效率更高;
      • 可以提供平滑插值;
    • 缺點:
      • 比尤拉旋轉稍微複雜了一點點,因為多了一個維度;
      • 理解更困難,不直觀;

1.17 多級漸近紋理 mipmap?有什麼優缺點?

為了加快渲染速度和減少影象鋸齒,貼圖被處理成由一系列被預先計算和優化過的圖片組成的檔案,這樣的貼圖被稱為 MIP map 或者 mipmap
多級漸進紋理由一組解析度逐漸降低的紋理序列組成,每一級紋理寬度和高度都是上一級紋理寬度和高度的一半。寬和高不一定相等,也就是說,這些紋理不一定都是正方形。
優點:提高渲染速度,減少影象鋸齒 缺點:會增加額外的記憶體消耗

1.18 片段和畫素的區別 ? 

  1. 片段是渲染一個畫素需要的全部資訊,所有片段經過測試與混合後渲染成畫素。
  2. 片段是三維頂點光柵化後的資料集合,還沒有經過深度測試,而畫素是片段經過深度測試、模板測試、alpha混合之後的結果
  3. 片段的個數遠遠多於畫素,因為有的片段會在測試和混合階段被丟棄,無法被渲染成畫素。

1.19 深度快取演算法(zbuffer演算法)?

  1. 需要一個空間儲存每個畫素的深度,繪製前初始化所有深度為無限遠,繪製時當前片段如果比zbuffer中的值大(說明更遠),則跳過此片段,保留原來的渲染結果;否則,繪製此片段,並更新zbuffer。
  2. 可以處理對透明物體的消除
  3. 演算法可以並行
  4. 與畫家演算法不同,不需要對物體排序

二、數學基礎

2.1 平面上N個點,每兩個點都確定一條直線, 求出斜率最大的那條直線所通過的兩個點

平面上N個點,每兩個點都確定一條直線,

求出斜率最大的那條直線所通過的兩個點(斜率不存在的情況不考慮)。時間效率越高越好。

平面上N個點,每兩個點都確定一條直線,求出斜率最大的那條直線所通過的兩個點(斜率不存在的情況不考慮)。時間效率越高越好。 關於這道題,網上已經給出瞭解答要點: 3個點A,B,C,把它們的按x座標排序。假設排序後的順序是ABC,那麼有兩種情況:
1.ABC共線,則k(AB)=k(BC)=k(AC) 2.ABC不共線,則ABC將形成一個三角形,那麼k(AC)<max(k(AB), k(BC))
其中k()表示求斜率。 所以程式的基本步驟就是:
1.N個點按x座標排序。 2.遍歷,求相鄰的兩個點的斜率,找最大值。
時間複雜度Nlog(N) 先把這些點按x座標從小到大排序,斜率最大的兩點必然是挨一起的兩個點,所以排序O(n* lg n),遍歷一次O(n)就夠了