OpenGL學習(三)-- OpenGL 基礎渲染

個人 OpenGL 專題學習目錄,但願和你們一塊兒學習交流進步!程序員


1、基礎圖形管線

渲染管線(rendering pipeline),它是一系列數據處理過程,而且將應用程序的數據轉換到最終渲染的圖像。下圖是 OpenGL 4.3 版本的管線。編程

管線.png
OpenGl渲染流程.png

OpenGL 中的 圖元 只不過是頂點的集合以預約義的方式結合在一塊兒罷了。數組

經過最近學習 OpenGL 的藍寶書(《OpenGL超級寶典》),學到了基礎渲染這塊,爲了加深理解,按照書中優化了一下渲染管線的流程圖,並在圖中添加上了翻譯和本身的理解,加深本身的印象並幫助更多學習 OpenGL 的朋友們更好的學習。服務器

OpenGL渲染管線簡化流程圖:

OpenGL渲染管線簡化流程.png

一、客戶端-服務器

管線上半部分是客戶端,下半部分是服務器。就 OpenGL 而言,客戶端是存儲在 CPU 存儲器中的,驅動程序將渲染命令與數據組合起來發給服務器執行。 服務器和客戶端在功能上是異步的。客戶端不斷的將數據和命令組合在一塊兒送入緩衝區,緩衝區再發送到服務器執行。框架

二、着色器

上圖中最大的框表明的是 頂點着色器片元着色器。着色器是使用GLSL編寫的程序。異步

頂點着色器 頂點着色器處理從客戶端輸入的數據,用數學運算來計算光照效果、位移、顏色值等。有幾個頂點,頂點着色器就要執行幾回。 上圖中的 圖元組合(Primitive Assembly) 框圖意在說明3個頂點已經組合在了一塊兒。函數

片元着色器 片元着色器來計算片元的最終顏色(儘管在下一個階段(逐片元的操做)時可能還會改變顏色一次)和它的深度值。在這裏咱們會使用紋理映射的方式,對頂點處理階段所計算的顏色值進行補充。若是咱們以爲不該該繼續繪製某個片元,在片元着色器中還能夠終止這個片元的處理,這一步叫作片元的 丟棄(discard)oop

頂點的着色器和片元着色器之間的區別: 頂點着色(包括細分和幾何着色) 決定了一個圖元應該位於屏幕的什麼位置,而 片元着色 使用這些信息來決定某個片元的顏色應該是什麼。post

着色器的渲染:性能

  • 頂點着色器(必要)
  • 細分着色器(可選)
  • 幾何着色器(可選)
  • 片元着色器(必要)

三、三種向OpenGL 着色器傳遞渲染數據的⽅法

屬性: 就是對⼀個頂點都要做出改變的數據元素。實際上,頂點位置自己就是一個屬性.。屬性能夠是浮點類型,整型,布爾類型等。

Uniform 值: 經過設置 Uniform 變量就緊接着發送一個圖元批次處理命令。Uniform 變量實際上能夠無限次的使⽤。 設置一個應用於整個表⾯面的單個顏色值,還可也是一個時間值。

使用下面的函數:

// Use a stock shader, and pass in the parameters needed
GLint UseStockShader(GLT_STOCK_SHADER nShaderID, ...);
複製代碼

傳遞不用的 Uniform 參數可使用不一樣的存儲着色器: 首先定義一個顏色黑色 vBlack

GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
複製代碼

聲明一個全局的存儲着色器變量 shaderManager

GLShaderManager		shaderManager;
複製代碼
shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
複製代碼
//使用 單位着色器
//參數1:簡單的使用默認笛卡爾座標系(-1,1),全部片斷都應用一種顏色。GLT_SHADER_IDENTITY
//參數2:着色器顏色
shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
複製代碼
GLShaderManger::UseStockShader(GLT_SHADER_IDENTITY,GLfloat mvp[16],GLfloat vColor[4])
複製代碼

紋理: 對紋理進行採樣和篩選。紋理數據的做用不只僅是表現圖形。不少圖形文件格式都是以無符號字節形式對顏色份量進行存儲的,但咱們仍然能夠設置浮點紋理。這就是說,任何大型浮點數據塊(例如消耗資源很大的函數的大型查詢表)均可以經過這種方式傳遞給着色器。

四、使用存儲着色器

在OpenGL 核心框架中,並沒提供任何內建渲染管線,在提交一個幾何圖形進行渲染以前,必須實現一個着色器。着色器由GLToolsC++GLShaderManager 管理。他們可以知足進行基本渲染的基本要求,要求不高的程序員,這些存儲着色器已經足以知足他們的需求。但隨着時間和經驗的提高,大部分開發者可能不知足於此,會着手去寫着色器,手寫的我會在之後的文章裏再寫出來。

4.1着色器的使用

1)GLShaderManager 的初始化:

GLShaderManager		shaderManager;
shaderManager.InitializeStockShaders();
複製代碼

2)GLShaderManager 屬性:

存儲着色器爲每一個變量都使用一致的內部變量命名規則和相同的屬性槽(Attribute Slot)。下表列出了這些屬性: 表-GLShaderManager 預約義的標識符

標識符 描述
GLT_ATTRIBUTE_VERTEX 3份量(x, y, z)頂點位置
GLT_ATTRIBUTE_COLOR 4份量(r, g, b, a)顏色值
GLT_ATTRIBUTE_NORMAL 3份量(x, y, z)表面法線
GLT_ATTRIBUTE_TEXTURE0 第一對 2 份量(s ,t)紋理座標
GLT_ATTRIBUTE_TEXTURE1 第二對 2 份量(s ,t)紋理座標

4.2 GLShanderManager 的 uniform 值

通常狀況,要對幾何圖形進行渲染,咱們須要爲讀寫遞交屬性矩陣,首先要綁定到咱們想要使用的着色程序上,並提供程序的 Uniform 值。GLShanderManager 類能夠(暫時)爲咱們完成這項工做。 useStockShader 函數會選擇一個存儲着色器並提供這個着色器的 Uniform 值,這些工做經過一次函數調用就能完成:

GLShaderManager::UseStockShader(GLenum shader, ... ...);
複製代碼

C 語言(或 C++ 語言)中,......表示函數接受一個可變的參數數量。就這個函數自己而言,它根據咱們選擇的着色器,從堆棧中提取正確的參數,這些參數就是特定着色器要求的 Uniform 值。

(1) 單位(Identity)着色器 GLT_SHADER_IDENTITY

參數1:單位着色器
參數2:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_IDENTITY, GLfoat vColor[4]);
複製代碼

單位(Identity)着色器: 只是簡單地使用默認的笛卡爾座標系(座標範圍 -1.0~1.0)。全部片斷都應用同一種顏色,結合圖形爲實心和未渲染的。這種着色器只使用一個屬性 GLT_ATTRIBUTE_VERTEXvColor 參數包含了要求的顏色。

(2) 平面(Flat)着色器 GLT_SHADER_FLAT

參數1:平面着色器
參數2:容許變化的4*4矩陣
參數3:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_FLAT, GLfoat mvp[16], GLfloat vColor[4]);
複製代碼

**平面(Flat)着色器:**將單位着色器進行了擴展,容許爲集合圖形變換指定一個 4 x 4 的變換矩陣。常常被稱做「模型師徒投影矩陣」。這種着色器只使用一個屬性 GLT_ATTRIBUTE_VERTEX

(3) 上色(Shaded)着色器 GLT_SHADER_SHADED

GLShaderManager::UseStockShader(GLT_SHADER_SHADED, GLfoat mvp[16]);
複製代碼

**上色(Shaded)着色器:**惟一的 Uniform 值就是在幾何圖形中應用的變換矩陣。GLT_ATTRIBUTE_VERTEXGLT_ATTRIBUTE_COLOR 在這種着色器中都會使用。顏色值將被平滑地插入頂點之間(稱爲平滑着色)。

(4) 默認光源着色器 GLT_SHADER_DEFAULT_LIGHT

參數1:默認光源着色器
參數2:模型視圖矩陣
參數3:投影矩陣
參數4:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_DEFAULT_LIGHT, GLfoat mvMatrix[16], GLfloat pMatrix[16], GLfloat vColor[4]);
複製代碼

**默認光源着色器:**這種着色器使對象產生陰影和光照的效果。須要模型視圖矩陣、投影矩陣和做爲基本色的顏色值等 Uniform 值。所需的屬性有 GLT_ATTRIBUTE_VERTEX(頂點份量)和 GLT_ATTRIBUTE_NORMAL(表面法線)。

(5) 點光源着色器 GLT_SHADER_POINT_LIGHT_DIFF

參數1:點光源着色器
參數2:模型視圖矩陣
參數3:投影矩陣
參數4:視點座標系中的光源位置
參數5:顏色值
GLShaderManager::UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, GLfoat mvMatrix[16], GLfloat pMatrix[16], GLfloat vLightPos[3], GLfloat vColor[4]);
複製代碼

點光源着色器: 和默認光源着色器很類似,但光源位置多是待定的。接受 4Uniform,即模型視圖矩陣、投影矩陣、視點座標系中的光源位置和對象的基本漫反射顏色。一樣所需的屬性有 **GLT_ATTRIBUTE_VERTEX(頂點份量)**和 GLT_ATTRIBUTE_NORMAL(表面法線)

(6) 紋理替換矩陣 GLT_SHADER_TEXTURE_REPLACE

GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_REPLACE, GLfoat mvMatrix[16], GLint nTextureUnit);
複製代碼

**紋理替換矩陣:**着色器經過給定的模型視圖投影矩陣,使用綁定到 nTextureUnit(紋理單元) 指定的紋理單元的紋理對幾何圖形進行變換。片斷顏色是從紋理樣本中直接獲取的。所需的屬性有 **GLT_ATTRIBUTE_VERTEX(頂點份量)**和 GLT_ATTRIBUTE_NORMAL(表面法線)

(7) 紋理調整着色器 GLT_SHADER_TEXTURE_MODULATE

GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_MODULATE, GLfoat mvMatrix[16], GLfloat vColor, GLint nTextureUnit);
複製代碼

紋理調整着色器: 這種着色器將一個基本色乘以一個取自紋理單元 nTextureUnit 的紋理。所需的屬性有 GLT_ATTRIBUTE_VERTEX(頂點份量)GLT_ATTRIBUTE_TEXTURE0(紋理座標)

(8) 紋理光源着色器 GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF

參數1:紋理光源着色器
參數2:模型視圖矩陣
參數3:投影矩陣
參數4:視點座標系中的光源位置
參數5:幾何圖形的基本色
參數6:將要使用的紋理單元
GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, GLfloat mvMatrix, GLfoat mvMatrix[16], GLfloat vLightPos[3], GLfloat vBaseColor[4], GLint nTextureUnit);
複製代碼

紋理光源着色器: 這種着色器將一個紋理經過漫反射照明計算進行調整(相乘),光線在視覺空間中的位置是給定的。這種着色器接受 5Uniform 值,即模型視圖矩陣、投影矩陣、視覺空間中的光源位置、幾何圖形的基本色和將要使用的紋理單元。所需的屬性有 GLT_ATTRIBUTE_VERTEX(頂點份量)GLT_ATTRIBUTE_NORMAL(表面法線)GLT_ATTRIBUTE_TEXTURE0(紋理座標)

2、OpenGL 基礎圖元

OpenGL 圖元的模式標識:

圖元類型 OpenGL 枚舉量
GL_POINTS
GL_LINES
條帶線 GL_LINE_STRIP
循環線 GL_LINE_LOOP
獨立三角形 GL_TRIANGLES
三角形條帶 GL_TRIANGLE_STRIP
三角形扇面 GL_TRIANGLE_FAN

一、點和線

(1)點

是最簡單的圖像。每一個特定的頂點在屏幕上都僅僅是一個單獨的點。默認的狀況下,點的大小是一個像素的大小。咱們可經過調用 glPointSize 改變默認點的大小:

void glPointSize(GLfloat size);
複製代碼
// 1.最簡單也是最經常使用的 4.0f,表示點的大小
   glPointSize(4.0f);
    
// 2.設置點的大小範圍和點與點之間的間隔
GLfloat sizes[2] = {2.0f,4.0f};
GLfloat step = 1.0f;

// 獲取點大小範圍和最小步長(增量)
glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
glGetFloatv(GL_POINT_GRAULARITY,&step); 
複製代碼

還能夠經過使用程序點大小模式來設置點大小。

// 3.經過使用程序點大小模式來設置點大小
glEnable(GL_PROGRAM_POINT_SIZE);
// 這種模式下容許咱們經過編程在頂點着色器或幾何着色器中設置點大小。着色器內建變量:gl_PointSize,而且能夠在着色器源碼直接寫
 gl_PointSize = 5.0;
複製代碼

(2)線

比點更進一步的是獨立線段。一個線段就是 2 個頂點之間繪製的。 默認狀況下,線段的寬度是一個像素。改變線段惟一的方式是使用函數 glLineWidth:

void glLineWidth(GLfloat width);
複製代碼
// 設置獨立線段寬度爲1.5f;
glLineWidth(1.5f);
複製代碼

(3)線帶

**線帶(line strip)**連續地從一個頂點到下一個頂點繪製的線段,以造成一個真正鏈接點的線條。 (爲了把圖形鏈接起來,每一個鏈接的頂點會被選定 2 次。一次做爲線段的終點、一次做爲下一條線段的起點),此次是做爲 GL_LINE_STRIP 繪製的。

(4)線環

線環(line loop) 是線帶的一種簡單拓展,在線帶的基礎上額外增長了一條鏈接着一批次中最後一個點和第一個點的線段。

二、繪製三角形

可能存在的最簡單的實體多邊形就是三角形,它只有 3 個邊。光柵化硬件最歡迎三角形。而且如今 OpenGL 已是 OpenGL 中支持的惟一一種多邊形。每 3 個頂點定義一個新的三角形。

(1)單獨的三角形

以下圖是使用 GL_TRIANGLES 繪製的兩個三角形:

繪製三角形.png
繪製金字塔 下面繪製 4 個三角形組成金字塔形的三角形。咱們可使用方向鍵來旋轉金字塔,從不一樣角度進行觀察。可是這個金字塔木有底面,因此咱們能夠看到它的內部。
金字塔無底.png
代碼以下:

//經過三角形建立金字塔
    GLfloat vPyramid[12][3] = {
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f};
    
    //GL_TRIANGLES 每3個頂點定義一個新的三角形
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
複製代碼

(2)環繞

將順時針方向繪製的三角形用逆時針的方式繪製。 以下圖,在繪製第一個三角形時,線條是按照從 V0-V1,再到 V2。最後再回到 V0 的一個閉合三角形。 這個是沿着頂點順時針方向。 這種順序與方向結合來指定頂點的方式稱爲 環繞。 下圖的 2 個三角形的環繞方向徹底相反。

三角形環繞.png

正面與背面:

在默認的狀況下,OpenGL 認爲具備 逆時針方向 環繞的多邊形是 正面 的。而右側的 順時針方向 三角形是三角形的 背面

爲何區分正背面很重要?

由於咱們經常但願爲一個多邊形的正面和背面分別設置不一樣的物理特徵。咱們能夠徹底隱藏一個多邊形的背面,或者給它設置一種不一樣的顏色和反射屬性。紋理圖像在背面三角形中也是相反的。在一個場景中,使全部的多邊形保持環繞方向的一致,並使用正面多邊形來繪製全部實心物體的表面是很是重要的。

若是想改變 OpenGL 這個默認行爲,能夠調用下面的函數: glFrontFace(GL_CW);

參數:GL_CW | GL_CCW

  • GL_CCW:表示傳入的mode會選擇逆時針爲前向(默認:GL_CCW)
  • GL_CW:表示順時針爲前向。

(3) 三角地帶

對於不少表面和形狀來講,咱們可能須要繪製幾個相連的三角形。咱們可使用 GL_TRIANGLE_STRIP 圖元繪製一串相連的三角形。從而節省大量的時間。

三角形帶.png

使用三角帶而不是分別指定每一個三角形,這樣作有兩個優勢:

  • 用前 3 個頂點指定第 1 個三角形以後,對於接下來的每個三角形,只須要再指定 1 個頂點。須要繪製大量的三角形時,採用這種方法能夠節省大量的程序代碼和數據存儲空間。
  • 提供運算性能和節省帶寬。更少的頂點意味着數據從內存傳輸到圖形卡的速度更快,而且頂點着色器須要處理的次數也更少了。

(4) 三角形扇

除了三角形帶以外,還可使用 GL_TRIANGLE_FAN 建立一組圍繞一箇中心點的相連三角形。經過 4 個頂點所產生的包括 3 個三角形的三角形扇。 第一個頂點 V0 構建了扇形的原點,用前 3 個頂點指定了最初的三角形以後,後續的每一個頂點都和原點 (V0) 以及以前緊挨着它的那個頂點 (Vn-1) 造成接下來的三角形。

三角形扇.png

三、一個簡單批次容器

GLTools 庫中包含額一個簡單的容器類,叫作 GLBatch。這個類能夠做爲7種圖元的簡單批次容器使用。並且它知道在使用 GL_ShaderManager 支持的任意存儲着色器時如何對圖元進行渲染。 使用 GLBatch 類很是簡單。首先對批次進行初始化,告訴這個類它表明哪一種圖元,其中包括的頂點數,以及(可選)一組或兩組紋理座標。

參數1:圖元
參數2:頂點數
參數3:一組或者2組紋理座標(可選)
void GLBatch::Begain(GLeunm primitive,GLuint nVerts,GLuint nTexttureUnints = 0);
複製代碼

而後,至少要複製一個由3份量(x, y, z)頂點組成的數組。

//複製表面法線
void GLBatch::CopyVertexData3f(GLfloat *vVerts);
複製代碼

還能夠選擇複製表面發現、顏色和紋理座標。

//複製表面法線
void GLBatch::CopyNormalDataf(GLfloat *vNorms);
//複製顏色
void GLBatch::CopyColorData4f(GLfloat *vColors);
//複製紋理座標
void GLBatch::CopyTexCoordData2f(GLFloat *vTextCoords,GLuint uiTextureLayer);
複製代碼

完成上述工做之後,可調用End來代表已經完成了數據複製工做,而且將設置內部標記,以通知這個類包含哪些屬性。

//結束繪製
void GLBatch::End(void);
複製代碼

實際上,能夠在任何咱們想要的時候進行復制,只要不改變累的大小便可。 而一旦調用 End 函數,就不能再增長新的屬性了(也就是說咱們如今也不能肯定是否要有表面法線了)。

關於提交屬性的 OpengGL 實際內部運行機制實際上比這要複雜的多。GLBatch 類只是一個便利類(convenience class),就像使用GLUT同樣方便。

以上的總結參考了並部分摘抄瞭如下文章,很是感謝如下做者的分享!:

一、《OpenGL超級寶典 第5版》

二、《OpenGL編程指南(英文第八版)》

三、做者CC老師_HelloCoder的《004--OpenGL 圖元》

轉載請備註原文出處,不得用於商業傳播——凡幾多

相關文章
相關標籤/搜索