1. 程式人生 > >OpenGL 程式設計指南(第八版)學習筆記——8 程式式紋理

OpenGL 程式設計指南(第八版)學習筆記——8 程式式紋理

8 程式式紋理

3D物理模型

因為這章會使用3D物理模型,所以先講解一下書中程式碼使用的3D物理模型。書中的3D物理模型格式有兩種,副檔名都是.vbm,一種的人動物模型,另一種是幾何體模型,如下圖,圖片上面對應的是程式碼中的工程名字。

所有的模型檔案都存放在media資料夾中

雖然副檔名都是.vbm但是模型內部資料結構不一致,所以兩種模型載入的時候使用的程式碼也是不一樣。第一中人物使用的模型載入程式碼是放在工程目錄下面的vbm.h和vbm.cpp,第二種幾何模型使用的程式碼是lib/vbm.cpp和include/vmb.h。

兩種模型包含的資料不一樣,人物模型包含的資料有:頂點位置,法向量,紋理座標;幾何模型包含的資料有:頂點位置,法向量,切線向量,紋理座標。關於第二種模型,書中程式碼的載入函式bool

LoadFromVBM(const char * filename, int vertexIndex, int normalIndex, int texCoord0Index)最後一個引數texCoord0Index實際上是切線向量的頂點屬性,紋理座標的頂點屬性固定是3,這一個bug我沒改,懶。其他關與3D物理模型的問題可以參考程式碼。

規則的花紋

條紋

對應的程式碼工程8.1.1-01Stripe,執行結果:

程式使用到的一些函式可以在Opengl 4.x Reference檢視其功能,程式中也涉及到反走樣的問題,反走樣原理可以檢視第四章裡面的圖元反走樣

程式的實現主要在片元著色器裡面,以下是片元著色器的程式碼:

float scaledT = fract(fsTexCoord*Scale);//放大紋理座標然後取小數部分。

float frac1 = clamp(scaledT/Fuzz, 0.0, 1.0);//除了fuzz區域,其他都時1.

float frac2 = clamp((scaledT-Width)/Fuzz, 0.0, 1.0);//除了fuzz+width區域,其他都是1

程式碼中fsTexCoord是一個float資料,在圓環上的分佈如下圖

Scale是整個圓環的條帶數目,fsTexCoord與之相乘後取小數部分,這相當於確定片源在哪個條帶。對於frac1frac2其在一個條帶內值的分佈如下圖

其他請參考程式8.1.1-01Stripe

磚塊

程式碼工程8.1.1-02BrickColor-Square

執行結果:

以下兩張圖片右邊圖片繪製的黑線分割開了每個磚塊單元

實際上有兩種磚塊分別是: ,整個磚塊紋理都是由這兩種磚塊組成,具體實現可以檢視程式碼,程式碼有詳細註釋。

玩具球

這裡我實現了一個相對簡單一點的例子,在球上面繪製一個三角形,對應程式碼工程8.1.2TriangleBall。

執行結果:

這裡用到了一個數學知識,線性規劃。在繪製球的時候會通過線性規劃計算片源是否在三角形內,如果在三角形內則繪製紅色。下圖是z=0平面時線性規劃座標。

程式中會用到3個平面也就是P1,P2,P3。3維中一個平面的數學表示式aX+bY+cZ+d=0。我們用vec4的四個分量代表a,b,c,d。這樣一個vec4就是一個平面。

晶格

這一塊沒有書中8.1.3的具體模型,實現了一個相對簡單的例子8.1.3Lattice

執行結果:

凹凸紋理

顯示器是一個2D的平面,當顯示一個3D物理模型時,為了使得物體有立體感,需要模擬光照,讓物體表面各個點的亮度根據光照設定明亮。凹凸紋理利用這一點人為的增加法線的干擾,這使得同一表面一些位置明亮不一致,從而看上去有凹凸感。

為了更好的控制凹凸的紋理,設計凹凸紋理時,會在一個平行於z=0的平面上設計。當顯示時,顯示的模型的三角形平面並不會與z=0平面平行,所以需要做一個轉換,將平面的切線當作x軸,法線向量當作z軸,讓後在轉換後的座標裡面計算凹凸紋理。轉換的原理和檢視矩陣一樣,可以參考第五章檢視矩陣的推導。因為只需要與z=0平面平行,所以不需要有位移,只需要轉換,因此只需要mat3*3的矩陣。

以下是在一個平面裡面顯示6*6個凸起來的圓點,程式碼工程8.2-01Bump。

凹凸紋理的限制

凹凸紋理一般只用於顯示細小突起的花紋,因為凹凸紋理實際只是對法線進行的干擾,物體表面實際還是平的。當從90側面看過去時,物體依然是平的,無法看出凹凸感。如下圖(程式碼工程:8.2-02Bump-tours), 使用了凹凸紋物體左右兩邊並沒有凸起。

程式式紋理反走樣

關於走樣和反走樣畫素顯示上的原理,可以參考第4章圖元反走樣

dFdxdFdy函式

這兩個函式求的值是所求變數隨視口(螢幕)x或y處的導數。當繪製三角形時,三角形內部顏色由三個點的顏色是採用線性插值的方式計算的,具體計算方法可以參考第四章光柵與插值。例如當視口寬度為W,高H,三角形內一個點的插值函式為R1 = k1*x1 + k2*y1 + b,片元著色器中dfdx(R1) = k1*(2/W),dfdy(R1) = k2*(2/H)。之所以要乘以2是因為x,y取值都是[-1,1]範圍之間。具體測試兩個函式的工程為8.3.4-02Test_dFdx_dFdy。

fwidth函式fwidth(p) = abs(dFdx(p)) + abs(dFdy(p))

條紋球體

利用dfdx和dfdy我們可以知道繪製圖元時變數單個圖元(畫素)的跨度,這樣我們就可以更好的控制反走樣需要的寬度,可以對smoothstep設定合適的引數進行反走樣。如下例子,對應程式碼(8.3.4-01StripesBall):

關與以上例子,書中給的球形幾何3D模型紋理的s方向在球星處不連續,如下圖,利用作為灰度進行繪製的球極點,明顯可以看到鋸齒紋:

由於這個原因當我們設定的條紋黑白交接處剛好在鋸齒紋附件時,我們無法進行反走樣。

反走樣磚塊

程式碼工程8.3.4-03BrickConvolution

反走樣棋盤

當兩個畫素之間顏色變化過快時,直接使用一個平均顏色替代,程式碼工程8.3.5-01Checkerboard

執行後,縮小視窗,最終會變成一個平均顏色

噪聲

提到噪聲第一反應是嘈雜無序的聲音,這裡噪聲指的是隨機數,通過噪聲我們可以模擬一些無序的運動火源、波浪,可以模擬一些無序的花紋雲朵、裂痕等等。噪聲的隨機數與我們平時通過C語言生成的隨機數有些不一樣。首先噪聲是連續的,C語言生成的隨機數是離散。第二個不同是噪聲生成隨機數時需要一個引數,當引數相同時噪聲函式返回的隨機數也必須相同。當是一維噪聲時,連續則可以通過數學表示式表示為f(x) ≈ f(x+dx),如果繪製成影象會是一條無序的波,如下圖,程式碼為8.4-01Noise1:

多雲的天空,程式碼工程:8.4.4-01NoiseCloudy

太陽表面,程式碼工程:8.4.5-01Turbulence。

火焰,程式碼工程:8.4.5-02SimpleFire