中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

OpenGL進階(十七) - 深入理解OpenGL

發布時間:2020-06-30 05:34:11 來源:網絡 閱讀:5932 作者:拳四郎 欄目:開發技術

翻譯自《 OpenGL Programming Guide》(8th) 第一章,標題為  Introduction to OpenGL。

紅寶書第八版和第七版的最大的區別就是OpenGL的版本從OpenGL2.X變成了OpenGL4.X,渲染流水線也從固定流水變化為可編程流水線,shader滿天飛...

好,進入正文。


此文主要內容如下:

1.介紹OpenGL的作用,告訴你OpenGL在計算機圖形學中能做什么,不能做什么;

2.介紹OpenGL程序的常用結構;

3.介紹OpenGL渲染流水線的每個階段。


什么是OpenGL

 OpenGL進階(十七) - 深入理解OpenGL

       OpenGL是一組應用程接口(Application programming interface),即它是一個能夠操縱計算機圖形硬件的程序庫。OpenGL4.3版包含了3500多個函數接口,用于創建圖像,操作物體等,一切都是為了創建交互性三維計算機圖形程序。

       OpenGL是按照流水線型設計的,和硬件無關,這讓它能夠運行于各種各樣的圖形硬件上。同時它也是軟件無關的,可以運行于不同的操作系統,而只需操作系統只需提供一個讓OpenGL運行的GUI庫,同樣的OpenGL也還會提供描述三維模型或者讀取圖片文件的方法,你需要做的是將一系列三維圖元(比如點,線,三角形), 來組成三維物體。

      OpenGL并不是一個新事物,1.0版本在1994年6月友硅谷圖形計算機系統開發出來,后續有很多OpenGL的其它版本,也有很多基于OpenGL開發的軟件庫用于更簡單快速地進行應用程序開發,比如游戲開發,科學或者醫學可視化系統地開發,抑或僅僅是為了顯示圖像。越是新版本的OpenGL,和原版本的OpenGL差異就越大。

     下面的一個列表簡單地描述了OpenGL渲染一張圖片需要用到的操作。

 ●  初始化用于顯示圖元的數據;

 ●  將輸入的圖元作為輸入,在其上執行各種Shaders,計算出圖元的位置,顏色和其他的渲染屬性;

 ●  將圖元的數學描述轉化為用于顯示在屏幕上的片段(fragment),這個過程稱為柵格化;

 ●  最后執行fragment shader,處理上面得到的fragments,輸出的是fragments的最終顏色和位置;

 ●  還可能會執行一個額外的fragment處理,進行混合,透明之類的操作。


        OpenGL是一個client - server系統,你寫的應用程序被當作client,運行子啊圖形硬件上的OpenGL實現作為服務端,在一些OpenGL實現中,client和server是運行在不同的機器上的,之間用網絡連接,在這種情況下,client的命令通過網絡由協議進行傳輸,服務器收到命令之后生成最終圖像。


         譯者注: OpenGL 內部是一個巨大的狀態機,你所做的大部分是對這個狀態機進行讀取和設置工作。在對物體渲染的時候, OpenGL 會根據狀態機中的當前狀態來進行渲染,好比我們寫OpenGL 程序調用 API 實際上是在寫配置文件一樣。 OpenGL 的狀態很多, 涵蓋光照,紋理,隱藏面消除,霧等等。

        

        OpenGL程序的第一印象

        OpenGL可以用來做很多事,那么一個也可能是非常復雜的,但是OpenGL應用程序的基本結構通常是類似的:

● 初始化狀態機中的各種狀態變量(譯者注:狀態變量型別是一些C數據類型的 typedef, 有 GLfloat, GLboolean, GLint, GLuint 等等);

● 指定要渲染的物體。

        在看代碼之前,我們來介紹一些圖形學名詞,前面我們提到的渲染,指的是計算機從一些模型創建出一張圖像的過程,OpenGL只是渲染系統的一種,還有其他的方式,比如ray-tracing,但是用ray-tracing的系統也可能用OpenGL來顯示圖像或者用于計算。

        我們的模型或者物體,用術語來說的話,它們是由一系列圖元來確定的,包括點,線,三角形,它們都是由頂點確定的。

       另一個使用OpenGL至關重要的概念是Shader,它們是在圖形硬件中執行的程序,最好的理解方式是將shader當成為GPU(Graphics

Processing Unit)特別編譯的一些小程序.OpenGL包含了編譯shader的工具。

     在OpenGL中有四個shader處理階段可以使用,最普遍的是vertex shaders,用于處理頂點,還有fragment shaders,用于處理柵格化時候的片段,vertex和fragment shaders在每一個OpenGL程序中都會用到。

      最終生成的圖像包含了顯示在屏幕上的一系列像素。一個像素是顯示器上的最小顯示單位。像素的值存放在 frame buffer中,然后傳輸至顯示設備,frame buffer是由圖形硬件管理的一個存儲區。

      下圖顯示了一個簡單的OpenGL程序的輸出,在窗口中渲染了兩個三角形,源碼如下:

OpenGL進階(十七) - 深入理解OpenGL

/////////////////////////////////////////////////////////////////////// // // triangles.cpp // /////////////////////////////////////////////////////////////////////// #include <iostream> using namespace std; #include "vgl.h" #include "LoadShaders.h" enum VAO_IDs { Triangles, NumVAOs }; enum Buffer_IDs { ArrayBuffer, NumBuffers }; enum Attrib_IDs { vPosition = 0 }; GLuint VAOs[NumVAOs]; GLuint Buffers[NumBuffers]; const GLuint NumVertices = 6; //--------------------------------------------------------------------- // // init // void init(void) {     glGenVertexArrays(NumVAOs, VAOs);     glBindVertexArray(VAOs[Triangles]);     GLfloat vertices[NumVertices][2] = {         { -0.90, -0.90 }, // Triangle 1         { 0.85, -0.90 },         { -0.90, 0.85 },         { 0.90, -0.85 }, // Triangle 2         { 0.90, 0.90 },         { -0.85, 0.90 }     };     glGenBuffers(NumBuffers, Buffers);     glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),                  vertices, GL_STATIC_DRAW);     ShaderInfo shaders[] = {         { GL_VERTEX_SHADER, "triangles.vert" },         { GL_FRAGMENT_SHADER, "triangles.frag" },         { GL_NONE, NULL }     };     GLuint program = LoadShaders(shaders);     glUseProgram(program);     glVertexAttribPointer(vPosition, 2, GL_FLOAT,                           GL_FALSE, 0, BUFFER_OFFSET(0));     glEnableVertexAttribArray(vPosition); } //--------------------------------------------------------------------- // // display // void display( void) {     glClear(GL_COLOR_BUFFER_BIT);     glBindVertexArray(VAOs[Triangles]);     glDrawArrays(GL_TRIANGLES, 0, NumVertices);     glFlush(); }  //--------------------------------------------------------------------- // // main // int main(int argc, char** argv) {     glutInit(&argc, argv);     glutInitDisplayMode(GLUT_RGBA);     glutInitWindowSize(512, 512);     glutInitContextVersion(4, 3);     glutInitContextProfile(GLUT_CORE_PROFILE);     glutCreateWindow(argv[0]);     if (glewInit()) {         cerr << "Unable to initialize GLEW ... exiting" << endl;         exit(EXIT_FAILURE);     }     init();     glutDisplayFunc(display);     glutMainLoop(); } 

        也許代碼看上去有點多,但你以后會發現,幾乎你寫的每個OpenGL的基礎框架都是如此,我們使用了OpenGL以外的一些程序庫來處理創建窗口,接受鼠標鍵盤輸入等事件,我們還需要創建一些幫助函數和小的C++類來簡化我們的例子。OpenGL是C語言的庫,不過我們所有的例子都是用C++來寫的,這些C++都是很簡單的C++,實際上大部分C++是用來實現向量類和矩陣類的。

        簡單地說一下上面的例子做了哪些事,我會在后面詳細地解釋,所以現在看不懂也不用著急。

● 在最開始,我們添加了相應的頭文件,圣米格了一些全局變量和其他的有用的結構體;

● init()是用于初始化后面程序要用到的一些數據,這些大部分是后面渲染要到的定點信息,或者是用于紋理映射要用到的圖像信息,在本例的init()中中,我們首先是指定了要渲染的兩個三角形的位置信息,之后初始化要使用的shader,這次我們只用到了vertex shader和 fragment shader。LoadShaders函數就是用于加載給GPU執行的Shader。init()最后部分做的事稱為 shader plumbing ,在這里將數據和shader中的變量進行綁定;

● display函數是程序中真正執行渲染的部分。函數中通過調用OpenGL的函數進行渲染,基本上所有的display函數會做相同的三個步驟:
1.用glClear() 函數清理窗口;

2.調用OpenGL相應的函數來渲染;

3.將渲染出的圖像用于顯示。

● 最后,mian函數中做了很多的事情 - 創建窗口,調用init,進入時間循環,這里還有一些gl開頭的函數,但和其他的函數又有一些不同,簡單地說,它們是用于在不同的操作系統中寫OpenGL的工具庫:GLUT和GLEW.


OpenGL語法(略)


OpenGL渲染流水線

      OpenGL實現了通常所說的渲染流水線。這個流水線分為一系列不同的階段,能夠將應用程序提供給OpenGL的數據轉化為一幅最終的渲染圖。下面的圖為OpenGL4.3的流水線,這個流水線從發布至今已經進化了非常多。

OpenGL進階(十七) - 深入理解OpenGL


        OpenGL在最初將我們提供的圖形數據(頂點和圖元)傳入到一系列的shader 階段:vertex shading,tesselation shading,然后是geometry shading,這些都在柵格化之前做完。rasterizer會將在裁剪區域的所有圖元生成fragments,然后對每一個生成的fragment執行fragment shader。

        就如你所看到的,shaders在創建OpenGL應用程序中扮演了一個非常重要的角色。  你有權利去決定去使用哪個shader 階段,在每個階段中做哪些事情。并不是每一個階段都是必須;實際上,只有vertex shaders和fragment shaders才是一定要用到的。Tessellation 和 geometry shaders只是可選項。

        現在,我們對每一個階段都做一下更加深入地了解,這樣你對整體就有更好的拿捏。我知道這些東西對你來說可能有點無法理解,但現在最好是硬著頭皮看一下。你最后一定會明白理解一些理論會讓你在OpenGL的路上走得更遠。

        準備向OpenGL發送數據

        OpenGL要求將所有的數據都存儲在buffer對象中,所謂buffer對象就是OpenGL server維護的內存塊。將數據存放在buffer中有很多種方法,但是最常用的一種是用 glBufferData()函數,在初始化buffer之前,還有一些額外的工作要做。

        向OpenGL發送數據

        當我們初始化好buffers之后,我們可以用OpenGL的繪制函數來繪制幾何圖元,比如glDrawArray().

        OpenGL中的繪制通常意味著將頂點信息傳送給OpenGL server。一個頂點意味著一個信息的集合,集合中有你想要的任何信息,幾乎一定會包含頂點的位置信息,其他的值(比如法線)將會決定像素的最終值。

        Vertex Shading

        對于每一個需要渲染的頂點,vertex shader都會去處理和頂點相關的數據。根據在柵格化之前要激活哪些shader,vertex shader可能會非常簡單,可能僅僅是將數據拷貝傳遞到下一個階段 - 我們常稱為是 pass-through shader。對于一個很復雜的vertex shader ,用于計算頂點在屏幕中的位置(通常會用到矩陣變換),計算頂點光照等等。

        一個復雜的應用程序可能會有多個vertex shader,但每次只能執行一個。

       Tesselation Shading

        當vertex shader將每個相關的頂點都處理過一遍之后,如果tessellation shader 階段被激活,tessellation shader將會繼續處理這些數據,tesselation用 patchs來描述一個物體,在這個階段可以用一些相對簡單的patch圖形來細分模型來提供更好的外觀, Tesselation Shading 階段可以用兩個shader來處理,一個用于處理patch 數據,一個用于生成最終形狀。

        Geometry Shading 

         下一個shader階段是geometry Shading,在這個階段可以在柵格化之前處理單個的集合圖元,比如添加一些圖元。這個階段也是可選的,但是非常有用。

        圖元組裝

         前面的所有階段都是在針對頂點的信息操作,圖元組裝階段將頂點組裝成一組相關聯的幾何圖元,為后面的裁剪和柵格化做準備。

        裁剪

        有些點會在視口(你打算渲染的窗口)的外面,所以需要將和頂點相關的圖元進行一些處理,將不在視口中的圖元裁剪掉,這個過程叫做裁剪,是在OpenGL中自動處理的。

        柵格化

         裁剪之后馬上要做的就是柵格化,裁剪之后的圖元都傳遞到 raseriser 中生成fragment。可以將fragment當作是“候選像素”,這些像素存儲在framebuffer中。柵格化之后得到的fragment還是能夠改變顏色,處理這些fregments在下面兩個階段,fragment shading和 per fragment 操作。


Fragment Shading

         可編程的最后一個階段是fragment shading,在這里你可以控制fragments的顏色。在這個階段,shader可以決定fragments的最終顏色(雖然在下一個階段,per-fragment操作會最后一次改變顏色),fragment shaders非常地有用,在這里可以處理 texture mapping。如果一個fragment不應該被繪制,fragment shader也可以停止一個fragment的處理,這個過程稱為 fragment discard。

         一個很好的思考頂點shader和片段shader的不同點的方法是:vertex shading決定圖元在屏幕中處于什么位置,fragment shading用前面的信息來決定fragment的顏色。

         

          Per-Fragment Operations

          這里指的是一些額外的fragment 處理,這是對每個fragment處理的最后階段。在這里fragments的可見性由深度測試和模板測試決定。


詳解例子(略)


參考

《 OpenGL Programming Guide》(8th) 下載


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

桂东县| 自治县| 怀宁县| 阳江市| 通山县| 玉门市| 北碚区| 梁山县| 慈溪市| 偏关县| 武清区| 湖南省| 河间市| 泰安市| 理塘县| 集贤县| 资源县| 尖扎县| 喀什市| 原阳县| 崇信县| 中阳县| 静乐县| 如东县| 卢氏县| 灵川县| 兴仁县| 大新县| 清远市| 洞口县| 厦门市| 滕州市| 泗洪县| 勃利县| 东城区| 和林格尔县| 台湾省| 尼木县| 都江堰市| 铜鼓县| 金坛市|