OpenGL 程式設計指南(第八版)學習筆記——8 程式式紋理
8 程式式紋理
3D物理模型
因為這章會使用3D物理模型,所以先講解一下書中程式碼使用的3D物理模型。書中的3D物理模型格式有兩種,副檔名都是.vbm,一種的人動物模型,另一種是幾何體模型,如下圖,圖片上面對應的是程式碼中的工程名字。
所有的模型檔案都存放在media資料夾中
雖然副檔名都是.vbm但是模型內部資料結構不一致,所以兩種模型載入的時候使用的程式碼也是不一樣。第一中人物使用的模型載入程式碼是放在工程目錄下面的vbm.h和vbm.cpp,第二種幾何模型使用的程式碼是lib/vbm.cpp和include/vmb.h。
兩種模型包含的資料不一樣,人物模型包含的資料有:頂點位置,法向量,紋理座標;幾何模型包含的資料有:頂點位置,法向量,切線向量,紋理座標。關於第二種模型,書中程式碼的載入函式bool
規則的花紋
條紋
對應的程式碼工程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與之相乘後取小數部分,這相當於確定片源在哪個條帶。對於frac1和frac2其在一個條帶內值的分佈如下圖
其他請參考程式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章圖元反走樣。
dFdx和dFdy函式
這兩個函式求的值是所求變數隨視口(螢幕)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