OpenGL面試
GLSL語言
着色器(shader)是運行在GPU上的小程序,類似於C語言,構造一個着色器在其開頭必須聲明版本。本質上來說,着色器是一個把輸入轉化為輸出的程序。
着色器定義了in
和out
等關鍵字實現數據的輸入和輸出,從而實現數據的交流。如果從一個着色器向另一個着色器發送數據,則必須在發送方聲明一個輸出,在接收方聲明一個類似的輸入。當類型和名字都相同的時候,便會自動鏈接在一起,實現數據傳遞。
另一種從cpu向gpu發送數據的方式是uniform
。uniform
是全局的,無需借助其他中介實現數據傳遞。在着色器程序中聲明uniform
變量,在主程序中通過glGetUniformLocation
獲得其地址,從而設置着色器中uniform變量的值。
CPU和GPU之間如何調度的
如1中所述,GLSL運行在GPU,其通過接口實現和CPU之間的數據轉換。
opengl程序涉及到兩種類型的處理單元--CPU和GPU。opengl主程序由CPU調度運行,圖像處理部分通過GLSL交由GPU執行。CPU與GPU之間的數據傳遞分三個步驟:
- 首先利用內置的OpenGL函數生成一個ID號碼;
- 根據需要對該ID號碼進行內存類型的綁定;在經過上面兩個步驟之后,GPU中用於接收系統內存中數據的“標識符”就准備好了
- 對這部分內存進行初始化,初始化的內容來自於系統內存中,這一部分功能利用glBufferData函數完成。
數據提交到GPU專用的內存中之后,需要根據應用場景對這些數據進行適當的分配。比如,有的數據當做頂點,有的是作為顏色,有的用於控制光照等等
此外,由於GPU具有高並行結構(heighly parallel structure),所以GPU在處理圖形和復雜算法方面計算效率較高。CPU大部分面積為控制器和寄存器,而GPU擁有更多的ALU(Arithmetric Logic Unit,邏輯運算單雲)用於數據處理,而非數據的高速緩存和流控制。
緩沖
[1] 幀緩沖(frame buffer):幀緩沖是下面幾種緩沖的合集。通過幀緩沖可以將你的場景渲染到一個不同的幀緩沖中,可以使我們能夠在場景中創建鏡子這樣的效果,或者做出一些炫酷的特效,存放顯示用的數據的。
[2] 顏色緩沖(color buffer):存儲所有片段的顏色:即視覺輸出的效果。
[3] 深度緩沖(depth buffer):根據緩沖的z值,確定哪些面片被遮擋。由GLFW自動生成。
[4] 模板緩沖(stencil buffer):與深度測試類似,通過比較模板值和預設值,決定是否丟棄片段。
數據在opengl中處理順序是: 頂點着色器 - 片段着色器 - 模板測試 - 深度測試
mipmap
Mipmap是多級漸遠紋理,也是目前應用最為廣泛的紋理映射(map)技術之一。簡單來說,就是實現 “實物(圖片)看起來近大遠小,近處清晰遠處模糊”的效果。它簡單來說就是一系列的紋理圖像,后一個紋理圖像是前一個的二分之一。多級漸遠紋理背后的理念很簡單:距觀察者的距離超過一定的閾值,OpenGL會使用不同的多級漸遠紋理,即最適合物體的距離的那個。由於距離遠,解析度不高也不會被用戶注意到。同時,多級漸遠紋理另一加分之處是它的性能非常好
坐標系
局部空間(local space):或稱為 物體空間.指對象所在的坐標空間
世界空間(world space):指頂點相對於(游戲)世界的坐標。物體變換到的最終空間就是世界坐標系
觀察空間(view space):觀察空間(View Space)經常被人們稱之OpenGL的攝像機(Camera)(所以有時也稱為攝像機空間(Camera Space)或視覺空間(Eye Space))。觀察空間就是將對象的世界空間的坐標轉換為觀察者視野前面的坐標。因此觀察空間就是從攝像機的角度觀察到的空間
裁剪空間(clip sapce):或稱為視覺空間.在一個頂點着色器運行的最后,OpenGL期望所有的坐標都能落在一個給定的范圍內,且任何在這個范圍之外的點都應該被裁剪掉(Clipped)。被裁剪掉的坐標就被忽略了,所以剩下的坐標就將變為屏幕上可見的片段。這也就是裁剪空間(Clip Space)名字的由來。
屏幕空間(screen space):顧名思義,一般由glViewPort
設置。
常用矩陣
model
:主要針對模型的平移、旋轉、縮放、錯切等功能,將模型由局部空間轉換到世界空間
view
:視圖矩陣。攝像機/觀察者的位置等信息(設置鼠標移動、滾輪等效果),將所有世界坐標轉換為觀察坐標。
projection
:投影矩陣。裁剪坐標 轉換到屏幕上
為什么說opengl是一個狀態機
首先簡單了解一下什么是"狀態機",比如我們使用的電腦,接受各種輸入(鼠標,鍵盤,攝像頭等),然后改變自己當前的狀態,但卻並不知道狀態的改變是如何實現的。opengl類似,接受各種參數,然后參數的改變引起當前狀態的改變,達到一種新的狀態(如:顏色改變,紋理變化,光照強弱變化)。
光照
馮式光照模型:環境光照(Ambient)、漫反射(Diffuse)、鏡面(Specular)
光源:點光、定向光、手電筒(聚光燈)
顯示器是二維的,三維數據如何在二維屏幕上顯示的
主要是通過圖形渲染管線(管線:實際上是指一堆原始圖像數據途徑一個輸送管道,期間經過經過各種變換處理,最終輸出在屏幕上的過程)管理的,其被划分為兩個過程:1. 把3D坐標轉換為2D坐標。 2. 把2D坐標轉換為實際有顏色的像素。
2D坐標和像素是不同的,2D坐標精確表示一個點在空間的位置,而2D像素(好像都是整數)是這個點的近似值,2D像素受到個人屏幕/窗口 分辨率的限制。
渲染流水線
首先,發送頂點數據到頂點着色器(Vertex Shader),頂點着色器主要的目的是把3D坐標轉為另一種3D坐標(后面會解釋),同時頂點着色器允許我們對頂點屬性進行一些基本處理。
然后進入圖元裝配(Primitive Assembly)階段將頂點着色器輸出的所有頂點作為輸入(如果是GL_POINTS,那么就是一個頂點),並所有的點裝配成指定圖元的形狀。
圖元裝配階段的輸出會傳遞給幾何着色器(Geometry Shader)。它可以通過產生新頂點構造出新的(或是其它的)圖元來生成其他形狀。
光柵化階段(Rasterization Stage),這里它會把圖元映射為最終屏幕上相應的像素,生成片段(Fragment)。在片段着色器運行之前會執行裁切(Clipping)。裁切會丟棄超出你的視圖以外的所有像素,用來提升執行效率。
片段着色器(Fragment Shader)計算一個像素的最終顏色。通常,片段着色器包含3D場景的數據(比如光照、陰影、光的顏色等等),這些數據可以被用來計算最終像素的顏色。
最終傳到Alpha測試和混合(Blending)階段。這個階段檢測片段的對應的深度和模板值,用它們來判斷這個像素是其它物體的前面還是后面,決定是否應該丟棄。這個階段也會檢查alpha值(alpha值定義了一個物體的透明度)並對物體進行混合(Blend)。
幾何着色器的輸出會被傳入光柵化階段(Rasterization Stage),這里它會把圖元映射為最終屏幕上相應的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器運行之前會執行裁切(Clipping)。裁切會丟棄超出你的視圖以外的所有像素,用來提升執行效率。
gldrawArrays glDrawElement
glDrawElement基於索引繪制,需要配置EBO