天天看點

OpenGL基礎(01)基礎知識1 基礎概念2 渲染相關概念與常見問題3 坐标系統和矩陣相關概念4 常見問題總結

1 基礎概念

1.1 OpenGL

 是一個由Khronos組織制定并維護的規範(Specification)。該規範嚴格規定了每個函數該如何執行,以及它們的輸出值。至于内部具體每個函數是如何實作(Implement),将由OpenGL庫的開發者自行決定。因為OpenGL規範并沒有規定實作的細節,具體的OpenGL庫允許使用不同的實作,隻要其功能和結果與規範相比對。

1.2 常見的OpenGL庫

  • GLFW:它是一個專門針對OpenGL的C語言庫,它提供了一些渲染物體所需的最低限度的接口。它也是一個輕量級的,開源的,跨平台的library。支援OpenGL及OpenGL ES,用來管理視窗,讀取輸入,處理事件等。因為OpenGL沒有視窗管理的功能,是以很多熱心的人寫了工具來支援這些功能,比如早期的glut,現在的freeglut等。那麼GLFW有何優勢呢?glut太老了,最後一個版本還是90年代的。freeglut完全相容glut,算是glut的代替品,功能齊全,但是bug太多。穩定性也不好(不是我說的啊),是以GLFW應運而生。
  • GLEW:不同的顯示卡公司,也會釋出一些隻有自家顯示卡才支 持的擴充函數,你要想用這數涵數,不得不去尋找最新的glext.h,有了GLEW擴充庫,你就再也不用為找不到函數的接口而煩惱,因為GLEW能自動識别你的平台所支援的全部OpenGL進階擴充函數。也就是說,隻要包含一個glew.h頭檔案,你就能使用gl,glu,glext,wgl,glx的全部函數。
  • GLAD庫,它是繼GL3W,GLEW之後,目前最新的用來通路OpenGL規範接口的第三方庫。
  • OpenAL:聲音庫,用于給3D程式加入聲音。API接口跟OpenGL一緻,還可以處理一些三維音效(音源位置處理),格式方面對wav和ogg支援強一點。
  • GLV:界面庫,如果想在程式中加點按鈕、狀态條、标簽。。。,又不想去到3D引擎或大型GUI庫(例如CEGUI)那一步,這類輕量級的界面庫很值得。

1.3 OpenGL的着色器程式(shader program)調用步驟

OpenGL基礎(01)基礎知識1 基礎概念2 渲染相關概念與常見問題3 坐标系統和矩陣相關概念4 常見問題總結

說明:從調用流程來看,真正需要編寫的着色器程式實際上有 頂點着色器(vertex shader)和 片段着色器(fragment shader)。

2 渲染相關概念與常見問題

2.1 立即渲染模式與 核心模式

  • 立即渲染模式:是一種固定渲染管線,這個模式下繪制圖形很友善,但缺少自由度和靈活性,目前基本淘汰。。
  • 核心模式:要求使用者真正了解OpenGL和圖形程式設計,它有一些難度,然而提供了更多的靈活性,更高的效率,更重要的是可以更深入的了解圖形程式設計。

2.2 shader(着色器)

它是一種存在于GPU中的程式。是可程式設計的,并允許操作幾何形狀和像素顔色。不同的着色器,比如:頂點着色器(Vertex shader)和片段着色器(Fragment shader),整體構成了一個管線。

2.3 shader工作原理(管線的處理流程)

OpenGL基礎(01)基礎知識1 基礎概念2 渲染相關概念與常見問題3 坐标系統和矩陣相關概念4 常見問題總結

管線處理流程圖

上圖即為管線處理的流程圖,這裡整理下,共分為6個階段:

  1. 頂點着色器(Vertex Shader):它把一個單獨的頂點作為輸入。主要目的是把3D坐标轉為另一種3D坐标(後面會解釋),同時頂點着色器允許我們對頂點屬性進行一些基本處理。
  2. 圖元裝配(Primitive Assembly):階段将頂點着色器輸出的所有頂點作為輸入,以指定的順序連接配接頂點建構基本體狀态。輸出給幾何着色器。螢幕外面的任何圖元都被剪輯并在下一階段忽略。
  3. 幾何着色器(Geometry Shader):它把圖元形式的一系列頂點的集合作為輸入,它可以通過産生新頂點構造出新的(或是其它的)圖元來生成其他形狀。輸出到下一階段,光栅化。
  4. 光栅化階段(Rasterization Stage):,這裡它會把圖元映射為最終螢幕上相應的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器運作之前會執行裁切(Clipping)。裁切會丢棄超出你的視圖以外的所有像素,用來提升執行效率。
  5. 片段着色器(Fragment Shader):它的主要目的是計算一個像素的最終顔色,這也是所有OpenGL進階效果産生的地方。通常片段着色器包含3D場景的資料(比如光照、陰影、光的顔色等等),這些資料可以被用來計算最終像素的顔色。即:将顔色或紋理應用到片段内的像素。
  6. 測試階段(Alpha測試和混合(Blending)階段):片段被送出到幾個測試,比如:透明度(Alpha)測試,模闆(Stencil)測試,深度(Depth)測試等。并對物體進行混合(Blend)。是以,即使在片段着色器中計算出來了一個像素輸出的顔色,在渲染多個三角形的時候最後的像素顔色也可能完全不同。

結果:像素被儲存在幀緩存(Framebuffer)中,更具體地說,是儲存在Default-Framebuffer中。這些是你在移動裝置螢幕上看到的像素。

3 坐标系統和矩陣相關概念

3.1 OpenGL的常用坐标系

将坐标轉換為标準化裝置坐标,接着再轉化為螢幕坐标的過程通常是分步,也就是類似于流水線那樣子,實作的,在流水線裡面我們在将對象轉換到螢幕空間之前會先将其轉換到多個坐标系統(Coordinate System)。将對象的坐标轉換到幾個過渡坐标系(Intermediate Coordinate System)的優點在于,在這些特定的坐标系統中進行一些操作或運算更加友善和容易,這一點很快将會變得很明顯。對我們來說比較重要的總共有5個不同的坐标系統:

  • 局部空間(Local Space,或者稱為物體空間(Object Space)):對象相對于局部原點的坐标;也是對象開始的坐标。
  • 世界空間(World Space):這些坐标是相對于世界的原點的。
  • 觀察空間(View Space,或者稱為視覺空間(Eye Space)):以錄影機或觀察者的角度觀察的坐标。
  • 裁剪空間(Clip Space):裁剪坐标是處理-1.0到1.0範圍内并判斷哪些頂點将會出現在螢幕上。
  • 螢幕空間(Screen Space):最後展現在螢幕的内容。在上一步将裁剪坐标轉換為螢幕坐标,我們将這一過程成為視口變換(Viewport Transform)。視口變換将位于-1.0到1.0範圍的坐标轉換到由glViewport函數所定義的坐标範圍内。最後轉換的坐标将會送到光栅器,由光栅器将其轉化為片段。
OpenGL基礎(01)基礎知識1 基礎概念2 渲染相關概念與常見問題3 坐标系統和矩陣相關概念4 常見問題總結

在這個5個坐标系變換的過程中,我們需要3個關鍵矩陣,model、view、projection。

3.2 OpenGL中 常用矩陣

  • model,模型矩陣:将本地坐标轉換為世界坐标。是一種轉換矩陣,它能通過對對象進行平移、縮放、旋轉、錯切 來将它置于它本應該在的位置或方向。 
  • view,視圖矩陣:将所有世界坐标轉換為觀察坐标。錄影機/觀察者的位置等資訊(設定滑鼠移動、滾輪等效果)。
  • projection,投影矩陣:将頂點坐标從觀察空間轉換到裁剪空間,将裁剪坐标轉換到螢幕上。

3.3 标準化裝置坐标(Normalized Device Coordinates, NDC)

标準化裝置坐标是一個x、y和z值在-1.0到1.0的一小段空間。任何落在範圍外的坐标都會被裁剪,不會顯示在你的螢幕上。下面你會看到我們定義的在标準化裝置坐标中的三角形(忽略z軸):

OpenGL基礎(01)基礎知識1 基礎概念2 渲染相關概念與常見問題3 坐标系統和矩陣相關概念4 常見問題總結

與通常的螢幕坐标不同,y軸正方向為向上,(0, 0)坐标是這個圖像的中心,而不是左上角。最終你希望所有坐标都應該在這個坐标空間中,否則它們就不可見了。你的标準化裝置坐标接着會變換為螢幕空間坐标(Screen-space Coordinates),這是使用你通過glViewport函數提供的資料,進行視口變換(Viewport Transform)完成的。所得的螢幕空間坐标又會被變換為片段輸入到片段着色器中。

3.4 右手坐标系(Right-handed System)

按照約定,OpenGL是一個右手坐标系。最基本的就是說正x軸在你的右手邊,正y軸往上而正z軸是往後的。想象你的螢幕處于三個軸的中心且正z軸穿過你的螢幕朝向你。坐标系畫起來如下:

OpenGL基礎(01)基礎知識1 基礎概念2 渲染相關概念與常見問題3 坐标系統和矩陣相關概念4 常見問題總結

右手坐标系

 為了了解為什麼被稱為右手坐标系,按如下的步驟做:

  1. 張開你的右手使正y軸沿着你的手往上。
  2. 使你的大拇指往右。
  3. 使你的食指往上。
  4. 向下90度彎曲你的中指。 

如果你都正确地做了,那麼你的大拇指朝着正x軸方向,食指朝着正y軸方向,中指朝着正z軸方向。如果你用左手來做這些動作,這就是有名的左手坐标系。注意在标準化裝置坐标系(NDC)中OpenGL使用的是左手坐标系(投影矩陣改變了慣用手的習慣)。

4 常見問題總結

4.1 三維資料如何在二維螢幕上顯示?

通過圖形渲染管線來管理。流程為:3D坐标-->2D坐标-->實際有顔色的像素。

4.2 OpenGL中如何傳遞變量?

變量共分為三類:uniform變量、attribute變量、varying變量。

  • uniform變量:它是外部應用程式傳遞給shader的變量。Qt中通過program->setUniformValue("uniform_name", value_name);來指派的。在shader程式内部,uniform變量就像是C語言裡面的常量(類似const類型),不能被shader程式修改。
  • attribute變量:它是隻能在vertex shader中使用的變量。(不能在fragment shader中聲明和使用)。attribute變量用來表示一些頂點的資料,如:頂點坐标,法線,紋理坐标,頂點顔色等。在Qt中,使用 void QOpenGLShaderProgram::bindAttributeLocation(const char *name, int location)來綁定每個attribute變量的位置,然後用函數glVertexAttribPointer()為每個attribute變量指派。
  • varying變量:它是vertex和fragment shader之間做資料傳遞用的。一般vertex shader修改varying變量的值,然後fragment shader使用該varying變量的值。是以varying變量在vertex和fragment shader二者之間的聲明必須是一緻的。應用不能使用此變量。

繼續閱讀