《Real-Time Rendering 4th Edition》讀書筆記--簡單粗糙翻譯 第六章 紋理 Texturing

寫在前面的話:因爲英語不好,所以看得慢,所以還不如索性按自己的理解簡單粗糙翻譯一遍,就當是自己的讀書筆記了。不對之處甚多,以後理解深刻了,英語好了再回來修改。相信花在本書上的時間和精力是值得的。

———————————————————————————————

“渲染一張圖像你要做的是讓它看起來是對的。”

   一個表面的紋理就是你看到的和感覺到的畫面,想象一副油畫的表面。在計算機圖形中,紋理(Texturing)就是一個先取得一個表面然後運用一些圖片、函數或其他的數據源來修改它在每個位置的表現的過程。舉個例子,不需要提供磚牆的一個精準幾何模型,僅需要一張磚牆的圖像,鋪到一個四邊形上。除非觀察者靠近牆壁,幾何細節上的缺陷是看不太出來的。

        然而,僅僅靠着磚牆的表面貼圖是不足以令人信服的,因爲缺乏幾何細節。例如,牆上水泥部分應該是啞光的,磚塊區域是光滑的,而在這個例子中觀察者會注意到兩張材質的粗糙度是一樣的。這時候就需要第二張圖像紋理了。不像第一張紋理是改變表面的顏色,第二張紋理則會改變表面的粗糙度,這取決於表面的位置。在這個例子中,水泥和磚塊從第一張紋理中拿到了顏色,從第二張紋理中拿到了粗糙度。

        問題又來了,觀察者發現所有的磚塊看起來都是平坦的,這是不對的,磚塊表面一般都是不平整的,這時候就可以用到凹凸貼圖(bump mapping)。通過改變渲染磚塊時的法線,讓它們看起來不那麼順滑。如果以一個較淺的角度來觀察,問題又出現了,按道理磚塊相較於水泥部分會突出,甚至以直線觀察過去應該會看到磚塊在水泥部分產生陰影。視差貼圖(Parallax mapping)解決了這個問題,視差遮蔽貼圖(parallax occlusion mapping)用來提高了真實度。置換貼圖(Displacement mapping)則是真實的將模型的頂點進行了偏移以產生凹凸感。圖6.1是一個用到了顏色紋理和凹凸貼圖的例子。

圖6.1 紋理。 顏色和凹凸貼圖被應用到魚模型上來增強它的視覺細節等級。

6.1 紋理管線(Texture Pipeline)

        紋理(Texturing)是一種高效修改表面材質和模型的技術。想象一下對單個着色像素所做的事,通過修改在着色方程中用到的數值來改變材質的顏色、光照等等。這種修改的值通常都是基於表面的位置的。以磚牆例子爲例,表面上的任意一點的顏色都對應在一張磚牆的圖像中,基於表面位置一一對應。在圖像紋理中的像素通常稱爲紋素(texel),用來區分在屏幕上的像素。

        紋理可以由紋理管線來描述。紋理過程的起始點是空間位置,這個位置可以是世界空間,但大部分時候是模型的局部空間,這樣模型移動,紋理會跟着一起移動。按Kershaw的話講,這個空間中的點會經過投影函數,然後得到一組數據,稱爲紋理座標(texture coordinate),用來訪問紋理。這個過程稱爲映射(mapping),全稱是紋理映射(texture mapping)

        在使用紋理座標前,會經過一個或多個對應函數(corresponder functions)把紋理座標轉換到紋理空間(texture space),然後通過紋理空間的座標位置(texture space location)來獲取紋理對應的值(obtain value),例如,從圖像紋理中得到一組索引值來檢索像素,檢索得到值(texture value)經過轉換函數(value transform function)的時候可能會發生變化,最終的結果值(transformed texture value)會用來修改表面的一些屬性,例如材質或着色法線。圖6.2展示了單個紋理的紋理管線過程。

圖6.2 單個紋理的紋理管線過程。

        圖6.3就是一個磚牆例子使用這個管線的示意圖。找到模型空間下的座標位置(x,y,z),也就是(-2.3,7.1,88.2),然後對這個座標位置進行投影,把(x,y,z)轉換成二維座標(u,v),牆上任意一點都會對應一組範圍在0 和1之間的紋理座標,例子中是(0.32,0.29)。然後通過紋理座標訪問紋理獲得對應位置上的顏色值,這張磚牆紋理的大小是256x256,所以對應函數(corresponder function)會把uv座標乘以256,得到(81.92,74.24)。去掉小數點,像素(81,74)找到了對應的顏色值(0.9,0.8,0.7)。這個紋理顏色是在sRGB顏色空間的,如果想要在着色方程中用這個顏色值,需要把它轉換到線性空間(0.787,0.604,0.448)。

圖 6.3 用磚牆例子展示管線。

6.1.1 投影函數(The Projector Function)

        紋理過程的第一步是要獲得表面的座標位置,然後將其投影到紋理座標空間,通常就是二維uv空間。給模型打包的時候通常是允許藝術家給每個頂點定義uv座標的,模型包初始化可能採用的是投影函數(projector function)或者網格展開算法(mesh unwrapping algorithm),藝術家也可以用這種方式改變每個頂點位置的時候改變uv座標。圖6.3展示了各種紋理投影。

圖 6.4 不同類型的紋理投影。上排從左到右依次是球面的(Spherical)投影、圓柱體的(cylindrical)投影、平面的(planar)投影和natural(u,v)投影。而下排是各投影應用到物體上的表現。

        表面法線也可以是投影函數的輸入參數,可用來選擇在6個平面投影方向上選取哪一個方向進行投影,通常會在個面銜接縫隙處出現問題,Tarini等人提出了polycube map,模型會被映射到一組立方體投影,不同的體積空間會映射到不同的立方體投影上

        通常一個投影函數足夠搞定一個完整的模型,但藝術家們通常會使用工具把模型細分開來,然後給各個部位採用不同的紋理投影方式。如圖6.5所示。

圖 6.5 展示瞭如何給一個模型使用多個紋理投影。Box mapping 包含6個平面映射(planar mapping),每一個面對應一個。

        在實時渲染中 ,投影操作通常在建模階段完成,然後把投影后的結果存在頂點上,當然也不是一直這樣,有時候在頂點着色或像素着色中也會用到投影。一些渲染方法,例如環境映射(environment mapping),在計算像素的時候使用了自己特別的投影函數。

        球面投影(spherical projection)將點投射到以某個點爲中心的假象球面上

        圓柱投影在計算u紋理座標和球面投影一樣,而在計算v紋理座標的時候是按沿着圓柱軸線的距離。這種投影對有着自然軸物體非常有用,例如旋轉表面的時候。

        平面投影就像x射線束,沿着一個方向進行平行投影,將紋理應用到所有表面上,它使用的是正交投影。這種投影方式對貼花非常有用。

        在投影方向的邊緣通常會有些失真,藝術家們通常需要手動把這些模型分成多個接近平面的部分,也有工具來展開模型網格。圖6.6 展示了圖6.5中所示的創建雕塑的工作空間。網格展開過程也是很大的研究領域,網格參數化(mesh parameterization)

圖 6.6 雕塑模型的一些小紋理合成兩張大紋理。右圖展示了展開的三角形網格及其如何在紋理中展示。

        紋理座標空間並不總是一個二維平面,有時候是一個三維空間。紋理座標可用三維向量(u,v,w)來表示,其實w是沿着投影方向的深度。有些系統中採用4位座標,通常表示爲(s, t, r, q),其中q表示這四個值在一個齊次座標系中,

        紋理座標空間一個重要類型是方向型的,空間中的每個點都可由輸入方向來訪問。對這個空間可視化的一個方式是在單位球上的點,每個點的法線表示就是在當前位置訪問紋理的方向。使用方向參數化的最常見紋理類似就是立方體貼圖(cube map)

        因爲表面可以應用多個紋理,則可能需要定義多組紋理座標。但是這些座標後面的原理都是一樣的:這些紋理座標都是在表面上進行插值得到,用於檢索紋理值。在插值之前,這些紋理座標需要由對應函數進行變換。

6.1.2 對應函數(The Corresponder Function)

        對應函數把紋理座標轉換到紋理空間座標位置,爲紋理應用到表面提供了靈活性。例如,運用API來選擇顯示一個紋理的某一部分,只有子紋理會在後續操作中應用到。

        對應函數的另外一種形式就是矩陣變換,可以應用在頂點着色或像素着色中。包含平移、旋轉、縮放、剪切或投影紋理到表面。需要注意,對紋理變換的順序和我們期待的順序相反,因爲紋理變換實際影響的是空間。紋理本身是沒有進行變換的,而決定了紋理位置的空間被改變了。

        另一類對應函數是控制一個紋理的應用方式。紋理在表面的uv座標範圍是[0,1]。但是如果超出了這個範圍呢?對應函數決定了該如何表現。在OpenGL中,這類型的對應函數稱爲“循環模式”(wrapping mode),在DirectX中,這個稱爲“紋理尋址模式”(texture addressing mode)。常用類型如下:

        ·wrap(DirectX),repeat(OpenGL),或者tile——紋理會重複自己;算法上,紋理座標的整數部分被刪掉了。這對重複一張紋理覆蓋表面非常有用,這通常是默認的。

        ·mirror——紋理會重複自己,但是每一次重複都是鏡像的。例如,一張圖像正常表現是從0到1,在1到2則是翻轉的,從2到3又回到了正常的,然後再翻轉,等等。這位紋理邊緣提供了連續性。

        ·clamp(DirectX)或clamp to edge(OpenGL)——超出了[0,1]範圍外的值會被鉗在這個範圍內。這會導致圖像紋理的邊緣是重複的。當在紋理邊緣附加進行雙線性插值時,這會對避免採樣到紋理相反邊緣上非常有用。

        ·border(DirectX)或clamp to border(OpenGL)——超過[0,1]範圍外的會使用一個單獨定義的邊框顏色進行渲染。如圖6.7所示。紋理的每個軸可以使用不一樣的對應函數,例如,紋理可以在u軸上使用repeat模式,在v軸上使用clamp模式。在DirectX中還一種 mirror once模式,會沿着紋理座標的0值處鏡像一次,然後進行clamp,這對對稱圖案非常有用。

圖 6.7 從左到右依次是圖像紋理的repeat模式,mirror模式,clamp模式和 border模式。

6.1.3 紋理值(Texture values)

        經過對應函數後會生成紋理空間座標,然後利用座標拿取紋理值。對圖形紋理來說,可以訪問紋理來檢索圖像中的紋素信息。在實時渲染中大部分用到的都是圖像紋理(Image Texturing),但是也有程序紋理(Procedural Texturing),在程序紋理中,從紋理空間獲取紋理值並不是涉及到內存查找,而是函數計算。

        最爲直接的紋理值是RGB三個值,用來替換或修改表面的顏色,類似的,還有單一灰度值。另外一種類型是RGBa值,a(alpha)值通常表示的顏色的不透明度,決定了顏色對像素的影響程度。也就是說,也可以存儲其他類型的值,比如表面的粗糙度。

6.2 圖像紋理(Image Texturing)

        圖形紋理中,二維圖像是最有效粘貼到三角形表面上的。

        像素着色器訪問紋理是通過使用紋理座標調用texture2D。GPU負責把紋理座標轉換成像素座標。主要兩種紋理座標系統:在DirectX紋理中左上角的座標爲(0,0),右下角的座標是(1,1);在OpenGL中,(0,0)在紋理左下角,和DirectX的y軸反向。像素的座標是整數的,但我們經常想訪問兩個像素直接的位置,然後對其進行混合。這帶出了一個問題:像素中心的浮點值座標是多少。有兩種系統:截斷(truncating)和湊整(rounding)Direct9定義了每個中心座標爲(0.0,0.0),使用的湊整。這個系統考慮到DirectX的原點是在左上角,所以會有(-0.5,-0.5)。Direct10修改成了和OpenGL一樣,紋素中心爲(0.5,0.5)使用的是截斷,更準確的說是flooring向下取整。像素(5,9)定義的是在u座標上從5.0到6.0,在v座標上是從9.0到10.0。

        需要注意一個術語:依靠紋理讀取(dependent texture read)。有兩種定義,第一種尤其適合移動設備,當通過texture2D或類似的函數來訪問紋理,依靠紋理讀取會發生在像素着色器計算紋理座標時(取代了從頂點着色器中傳過來的無修改紋理座標),注意,這意味着對傳入紋理座標可做任何修改,甚至簡答的交換u和v的值。在早期移動GPU上,如OpenGL ES3.0,就不支持依靠紋理讀取,紋素數據可以預取到。另外一種定義對早期桌面GPU很重要,依靠紋理讀取發生在當一個紋理座標需要依靠先前的紋理座標的時候。例如, 一個紋理可以改變着色法線,通過訪問一個立方體貼圖輪流改變紋理座標。

        在紋理圖像大小通常是個紋素,其中m和n都是非負整數,這些都是2的冪次方(power-of-two,POT)紋理,現代GPU可以處理非2冪次方(non-power-of-two,NPOT)的紋理了,允許生成的圖像可以當做紋理。然而,一些老的移動GPU可能不支持NPOT紋理的mipmapping(多級漸遠紋理)。圖像加速器對紋理大小的上限不一樣,DirectX 12允許的最大紋素是

6.2.1 放大(Magnification)

        在圖6.8中,一個48x48的紋理被紋理到一個方片上,方片的大小和紋理的大小很接近,底層圖像系統需要放大紋理。放大最爲常用的過濾技術有最近鄰點插值法(nearest neighbor,實際上是盒式濾波)雙線性插值(bilinear interpolation)以及三次卷積插值法(cubic convolution)。這些會提高放大的質量。儘管支持三次卷積插值(又稱爲雙三次插值 bicubic interpolation)的硬件還未普及開,但是可以在着色程序中完成。

圖6.8 48x48的紋理放大到320x320。作圖使用的是最近鄰點插值法濾波,每個像素選擇的是最近的紋素;中間圖使用的是雙線性插值濾波,使用的是四個最近紋素的平均值;右圖使用的是三次卷積插值濾波,使用的是一個5x5最近紋素的平均值。

        在圖6.8中的左圖,使用的是最近鄰點插值,這種放大技術的一個特點是一些個別紋素會特別明顯,這種現象稱爲像素化。雖然這種方法的質量不高,但是它每個像素只需要一個紋素。

        同樣的圖採用雙線性插值(有時稱爲線性插值,linear interpolation),效果如圖6.8中間圖所示。對每個像素,這種濾波會找到四個鄰點紋素,然後在像素二維方向上進行線性插值找到一個混合值。這個結果值是模糊的,很多鋸齒消失了。

        回到磚牆的例子,我們得到了p點的uv座標(81.92,74.24)。在這我們使用OpenGL的左下點爲原點的紋素座標系,因爲它更符合標準笛卡爾座標系統。我們的目的是對四個最近的紋素進行插值,用紋素中心定義了一個紋素大小的座標系。如圖6.9,爲找到最近的四個像素,我們從採樣點位置減去中心點(0.5, 0.5),得到(81.42,73.74)。去掉小數部分後,四個最近像素的範圍從(x,y)=(81,73)到(x+1, y+1)=(82,74)。我們例子中的小數部分是(0.42,0.74),是樣本相對於四個紋素中心形成的座標系的位置,我們定義爲(u',v')。

圖 6.9 雙線性插值。左圖展示了四個方塊對應四個紋素,藍色點是紋素中心。右圖展示了由四個紋素中心組成的座標系。

        定義紋理訪問函數爲t(x,y),其中x和y是整數,函數返回的是紋素的顏色值。對任意(u',v')進行雙線性插的顏色值可以通過兩步得到。首先,對於下面的兩個紋素,t(x,y)和t(x+1,y)進行水平插值(使用u'),對於上面的兩個紋素(t(x,y+1)和t(x+1, y+1))同樣處理。從下面的紋素我們可以得到(1-u')t(x,y) + u't(x+1, y),如圖6.9 左圖中靠下的綠色小圓圈,而對上面紋素有(1 - u')t(x, y+1) + u't(x + 1, y + 1),對應靠上的綠色小圓圈。然後拿這兩個值在垂直方向上進行插值(使用v'),所以可以得到雙線性插值顏色值爲:

直觀地說,越靠近採樣點的紋素對最終的結果影響越大。    

        一種常見的模糊放大是使用細節紋理(detail texture)。這些紋理表示的是表面細節,從手機上的抓痕到地面上的灌木叢。這些細節作爲單獨的紋理疊加在放大的紋理上。高頻重複使用細節紋理 ,結合低頻放大紋理,視覺效果上類似於使用單一高分辨率紋理。

        雙線性插值在兩個方向上進行了插值。然而,也可以不需要線性插值。例如,一個紋理由棋盤圖案中的黑色和白色像素組成。使用雙線性插值在紋理中給出不同的灰度值樣本,通過重映射,所有灰度值低於0.4認爲是黑色,所有灰度值高於0.6的認爲是白色,中間的灰色被拉伸來填補空白。這個紋理看起來更像是一個棋盤格了,同時添加一些混合的紋理。如圖6.10所示。

圖6.10   最近鄰點插值,雙線性插值,中間部分通過重新映射,使用相同的2×2棋盤紋理。注意,最近鄰點採樣給出的正方形大小略有不同,因爲紋理和圖像網格並不完全匹配。

        在圖6.8右圖中,使用的是一個雙三次插值濾波,效果更好,但是花費也更爲昂貴。考慮到昂貴的開銷,一種類似的技術被提出來,使用光滑曲線進行插值。兩種常見的曲線是smoothstep 曲線和quintic 曲線

對smoothstep曲線有:,並且平滑在0和1之間。同樣,quintic曲線有類似的特性:。這兩種曲線如圖6.11所示。

圖 6.11 左邊s(x)是smoothstep曲線,右邊是quintic 曲線q(x).

圖 6.12  四種不同方式來放大一個一維紋理。橙色圓圈表示紋素的中心也表示紋素的值(高度)。從左到右依次是:最近鄰點插值,線性插值,每對鄰點紋素間使用quintic曲線插值、三次卷積插值。

6.2.2 縮小(Minification)

        當一個紋理被縮小後,一些紋素可能會覆蓋一個像素,如圖6.13所示。爲了給每個像素拿到正確的顏色值,需要整合能夠影響到像素的紋素。然而很難確定每個紋素對像素的影響是具體是多少,實際上對實時系統是難以做到高效的。

圖 6.13 縮小紋理: 通過一排像素單元格查看一個棋盤格紋理方片,能夠粗略看出一個像素被多少個紋素影響。

        因爲這些限制,針對GPU的一些算法被提出來了。例如,使用最近鄰點插值,和放大紋理類似,選擇像素網格中心位置能夠看到的紋素。這個算法會引起一些走樣。如圖6.14所示,最上圖使用的就是最近鄰點插值法。在水平方向上,因爲只選取了衆多影響像素的紋素中的一個,就會出現鋸齒。當觀察者移動的時候,這些鋸齒表現的更爲明顯,屬於時間走樣(temporal aliasing)的一種表現。

        另外一種經常用到的方法是雙線性插值,再次和放大紋理類似,對於縮小來說,這個方法只比最近鄰點插值好一點,用混合四個紋素取代了只取一個紋素值,當一個紋素的影響超過四個紋素的混合時,這種濾波會快速失效產生走樣。也有好的解決方案,如5.4.1節討論到的,走樣問題可以通過採樣和濾波技術解決。一個紋理的信號頻率取決於屏幕上紋素之間距離有多近。根據奈奎斯特極限(Nyquist limit),我們需要確保紋理信號頻率不超過採樣頻率的一半。例如,一個圖像是黑白線交替組成的,紋素都是分開的,則波長是兩個紋素的寬度( 黑線到黑線的距離),所以頻率是1/2。爲了合適的把紋理顯示在屏幕上,頻率最低必須要有2x1/2,也就是一個像素一個紋理。所以,通常紋理必須要達到一個紋素一個像素才能避免走樣。

圖6.14 最上圖使用的是點採樣(最近鄰點插值),中間圖是mipmapping,最下圖使用的是區域求和表(summed area table)。

        爲了達到這個目標,除了加大像素的採樣頻率,還可以減少紋理的頻率。爲了解決徹底解決走樣問題,各種紋理縮小算法被提出來了。

        在這些紋理反走樣算法背後的基本思想都是一樣的:對紋理進行預處理並創建數據結構,以幫助計算一組紋素對一個像素的影響的快速近似值。對實時渲染系統,這些算法會花費固定的時間和資源,用這種方式,對每個像素進行一個固定數量的採樣,並結合計算一組紋素對像素的影響。

多級漸遠紋理(Mipmapping)

        最流行的紋理反走樣算法是多級漸遠紋理計算(mipmapping)。如今可以在所有圖形加速器上完成。“Mip”表示的是 multum in parvo,拉丁語,表示在意小空間裏的很多東西——這是一個非常棒的名字來表示原始紋理經過不斷重複的濾波來生成更小的圖像。

        當採用多級漸遠紋理最小化濾波(mipmapping minimization filter),原始紋理會生成一組大小越來越小的紋理,在實際使用紋理的時候,從中取出對應大小的紋理,紋理降採樣到原區域的四分之一,每個新的紋素通常都是計算原紋理中的四個相鄰紋素的平均值得到。這個縮減是遞歸實現的,直到紋理的一個維度或兩個維度只有一個紋素。這個過程如圖6.15所示,整個過程的這組紋理通常稱爲mipmap chain。計算座標d的目的是確定沿着mipmap的金字塔軸在哪裏採樣。

圖6.15  一個mipmap從原始紋理(0級)開始,在金字塔的底部,每2x2區域會合成下一級的一個紋素。縱軸是紋理的第三個座標,d。在該圖中,d不是線性的; 它是一種度量,一個樣本使用哪兩個紋理層次進行插值。

        生成高質量的mipmap有兩點很重要:好的濾波和伽馬校正。mipmap通常都是取一個2x2的紋素的平均值得到下一等級的值。使用的濾波是一個盒式濾波,最差的濾波之一,生成的質量比較差,並會造成一些不需要的低頻模糊效果,在保持一些高頻的時候會引起走樣。最好使用Gaussian、Lanczos、Kaiser或類似的濾波。在紋理邊緣需要注意使用的是紋理重複使用還是單一拷貝。

        對於在非線性空間中編碼的紋理(如大多數彩色紋理),在過濾時忽略gamma校正將修改mipmap級別的感知亮度。 當你離物體越遠,使用未校正的mipmaps,物體整體看起來就越暗,對比度和細節也會受到影響。 因此,將這些紋理從sRGB轉換爲線性空間是很重要的,在該空間中執行所有的mipmap過濾,並把最終結果轉回到sRGB顏色空間存儲。大部分API是支持sRGB紋理的,所以需要在線性空間生成正確的mipmaps,但是最終結果要保存在sRGB空間。當訪問sRGB紋理,首先需要把值轉換到線性空間,然後再進行合適的放大或縮小操作。

        當屏幕像素區域投影到紋理上(圖6.16),需要一個或多個紋素。使用像素單元的邊界嚴格上是不對的,但是可以用來簡單示意下。在單元格外面的紋素是可以影響到當前像素的顏色的,見5.4.1。爲了檢測出大致有多少紋理會影響到當前像素,常用計算d在OpenGL中稱爲 λ,又稱爲 texture level of detail,紋理LOD)的方法有兩種。一種是利用像素單元形成的四邊形的長邊來近似於像素的覆蓋範圍。另外一種是利用 ∂u/∂x, ∂v/∂x,∂u/∂y,  ∂v/∂y這四個差值的絕對值來作爲量度。

圖 6.16 左圖展示的是一個像素單元方片以及它的紋理視圖。右圖展示的是像素單元投影在紋理上。

區域求和表(Summed-Area Table)

        區域求和表(SAT)可以避免過度模糊。 要使用此方法,首先要創建一個與紋理大小相同但存儲顏色的精度要更高的數組(例如,紅綠藍分別有16位或更高)。 在這個數組的每個位置,必須計算並存儲由這個位置和紋素原點(0,0)形成的矩形中所有對應紋理的紋素的總和。在紋理過程中,像素單元在紋理上的投影被綁定在一個矩形中,可以通過訪問區域求和表來決定這個矩形的平均顏色,會作爲紋理的顏色傳遞給像素。 使用圖6.17所示的矩形的紋理座標計算平均值。計算公式如下:

其中,x和y是矩形的紋素座標,s[x,y]是紋素的區域求和表。 這個方程通過計算從右上角到原點的整個面積的和,然後減去A區域和B區域的面積,C區域的面積被減去了兩次,所以需要加回來一次。圖6.14展示了使用區域求和表的結果, 接近地平線的線在右邊邊緣處更清晰, 但是中間的斜線仍然很模糊。 問題是當一個紋理沿着它的對角線被觀察時,會形成一個大矩形,許多位於像素附件的紋素會被計算。 例如,想象一個長而薄的矩形,它代表像素單元的反投影,對角橫過整個紋理,如圖6.17所示。 整個紋理矩形的平均值將被返回,而不僅僅是像素單元內的平均值。

圖 6.17 像素單元反投影到紋理上,綁定在一個矩形上;矩形的四個角被用來訪問區域求和表。

無約束的各向異性的濾波(Unconstrained Anisotropic Filtering)

        對於當前的圖形硬件,進一步改進紋理濾波最常用的方法是複用現有的mipmap硬件。 其基本思想是將像素單元反投影,然後對紋理上的這個quad進行多次採樣,然後對這些採樣進行組合。 如上所述,每個mipmap樣本都有一個位置和一個與之相關的方形區域。沒有使用一個mipmap樣本來近似這個quad的覆蓋範圍,而是使用幾個正方形來覆蓋這個quad。 quad的短邊可以用來確定d(不像在mipmaping中,經常用到的是長邊), 這會使每個mipmap樣本的平均面積更小,也因此更不模糊。quad的長邊用來創建一個各向異性的線,平行於長邊,穿過quad的正中間。 各向異性在1:1和2:1之間時, 沿着這條線取兩個樣本(如圖6.18)。 各向異性比例越高,沿軸採集的樣品越多。

        這種方案允許各向異性線在任何方向上運行,因此不存在區域求和表的限制, 它也不需要比mipmaps更多的紋理內存,因爲它使用mipmap算法進行採樣。 圖6.19所示就是各向異性濾波的一個例子。

圖 6.18  各向異性濾波。 像素單元的反投影創建了一個四邊形(quad),在長邊間形成了一個各向異性線。

圖 6.19 mipmap和各向異性濾波對比。左圖是三次線性mipmap,右圖是採樣16:1比例的各向異性濾波。越靠近水平線, 各向異性濾波提供了更清晰的結果,走樣更小。

6.2.3 體紋理(Volume Texture)

        圖片紋理的一種直接擴展就是三維圖像數據,可由(u,v,w)或(s,t,r)來訪問。例如,醫學影像數據可以生成爲一個三維網格, 通過在這個網格中移動一個多邊形,可以查看這些數據的二維切片。一個類似的想法是用這個方式來表示體積光,表面上一個點的光照是通過尋找它在體積內的位置和光照方向來計算的。

        大部分GPU都支持體紋理的mipmapping。 由於在體紋理的單個mipmap層中濾波涉及到三線性插值,因此在mipmap層之間濾波需要進行四線性插值(quadrilinear interpolation)。Sigg和Hadwiger討論了這個問題及一些涉及到體紋理的其他問題,並給出了一些有效的解決方案,如通過濾波或其他操作。

        儘管體紋理需要更高的內存及高消費的濾波,但是它還是有獨立無二的優勢。爲三維網格尋找一個好的二維參數化的複雜過程可以跳過了,因爲三維座標可以直接作爲紋理座標了。這避免了二維參數化經常發生的失真和縫合問題。 體紋理也可以被用來表示木頭大理石等材質的體積結構。

        利用體紋理對錶面進行紋理是極其不高效的,因爲絕大多數樣本都沒用到。Benson、Davis、DeBry等人提出了利用一個稀疏八叉樹來存儲紋理數據,這個機制非常適合交互式繪畫系統, 因爲在創建表面的時候不需要顯式的分配紋理座標,並且八叉樹可以保存任何想要等級的紋理細節。

6.2.4 立方體貼圖(Cube Maps)

       立方體貼圖(cube map或cube texture)也是一種紋理類型,有六張紋理,分別和立方體的每個面一一對應。通過一個三分量紋理座標向量( 它指定了從立方體中心指向外的射線的方向)來訪問立方體貼圖。 立方體貼圖可用來表示一個方向函數的值;它們最常用於環境映射( environment mapping )。

6.2.5 紋理表示方式(Texture Representation)

        在一個應用中處理很多紋理有幾種方法來提高性能。6.2.6節說到的紋理壓縮是一種,而本節主要聚焦在紋理貼圖集(texture atlases)紋理數組(texture arrays)無約束紋理(bindless texture),這些方法的目的都是來避免在渲染的時候頻繁改變紋理的帶來的消耗。19.10.1節和19.10.2節講到了紋理流(texture streaming)紋理編碼轉換(transcoding)

        爲了合批(batch)餵飽GPU,一般儘可能少地改變狀態。爲了做到這個,可以把一些紋理放入到一張大紋理上,稱爲紋理圖集(texture atlas),如圖6.20左圖。注意,子紋理的形狀是任意的,如圖6.6。最優的子紋理佈置圖集是N¨ oll and Stricker提出的( Efficient Packing of Arbitrarily Shaped Charts for Automatic Texture Atlas Generation )。在生成和訪問mipmap的時候仍需要小心,因爲mipmap的上層可能包含了一些獨立無關的形狀。Manson和Schaefer提出了一種優化mipmap生成的算法,通過考慮表面的參數化過程,大幅度改良了結果。Burley和Lacewell提出了一個系統:Ptex,在細分表面的每個quad都有自己的小紋理,這個方法的好處是可以避免在網格上分配獨特的紋理座標, 在紋理圖集的斷開部分的接縫上沒有瑕疵。爲了通過quad濾波,Ptex使用了adjacency數據結構。

圖 6.20 左圖:紋理圖集,9個小紋理被組合進一個大紋理中。右圖:一個更現代的方法是把小紋理組織成一個紋理數組(texture array),在大部分現代API中都能找到這個概念。

        使用圖集的一個問題是使用wrapping/repeat和mirror模式,影響的不是子紋理而是整個大紋理另外一個問題是對圖集生成mipmap的時候,子紋理會滲透到另外一個子紋理上。 然而,這是可以避免的,在將每個子紋理放入大型紋理圖集之前,分別爲它們生成mipmap層次結構,並對子紋理使用2的冪次分辨率。

        解決這些問題的一個簡單方法是使用texture array API,這會避免mipmapping和repeat模式帶來的任何問題,如圖6.20右圖所示。在紋理數組裏的全部子紋理都必須擁有相同的大小,格式,mipmap層次結構,MSAA設置。像紋理圖集一樣,設置紋理數組只會執行一次,然後在着色可以利用下標訪問數組中的任意元素。這會比綁定每個子紋理快5倍。

        還有一個API可以避免狀態切換帶來的消耗,稱爲無約束紋理,bindless textures。沒有無約束紋理,使用API把一個紋理綁定到特定的紋理,紋理單元的數量上限是一個問題,這加大編程人員的複雜度。驅動需要確保了紋理是保留在GPU上。而有無約束紋理,對紋理數量的上限沒有了要求,因爲每個紋理都有一個64位指針關聯,有時候稱爲handle,指向紋理數據。這些handle可以有多種方式訪問,例如通過uniform,通過varying數據,其他紋理或者一個着色器存儲緩衝對象(shader storage buffer object,SSBO)。應用程序需要確保紋理是駐留在GPU端的。無約束紋理避免了驅動的任何類型綁定帶來的消耗,使渲染更快。

6.2.6 紋理壓縮(Texture Compression)

       固定比例的紋理壓縮是一種直接解決內存問題、帶寬問題和緩存內容的方法。利用GPU來解碼壓縮紋理,紋理需要更少的紋理內存,緩存大小變得更高效。同時,這些紋理使用起來更高效,因爲訪問它們需要而內存帶寬更少。通過壓縮紋理,可以提供更大的紋理。例如,一個無壓縮紋理每個紋素使用3個字節 ,紋理大小是512x512,大小爲768kB。使用壓縮後,壓縮比爲6:1,一張1024x1024的紋理只需要512kB。

        壓縮算法有很多,文件格式也對應各種各樣,例如JPEG,PNG,但是在硬件上進行解碼是很費的(19.10.1節會介紹到紋理轉碼)。S3研發出一個方案,稱爲S3紋理壓縮(S3 Texture Compression,S3TC)被DirectX採樣作爲標準,稱爲DXTC(DirectX Texture Compression)。在DirectX 10中被稱爲BC(Block Compression)。後來,OpenGL的標準實際上也是這個方案,因爲計劃所有的GPU都支持它。它的優點是創建一個固定大小壓縮紋理,有着獨立的編碼片段,因爲解碼很簡單(也快)。紋理的每個壓縮部分都可獨立處理。沒有依賴查找表或其他依賴,所有解碼很簡單。

        DXTC/BC的壓縮方案有幾種,它們有一些共同特性。在4x4的紋素塊上進行編碼,稱爲瓦塊(tiles)。每塊都是獨立編碼的,編碼是基於插值的。對每個編碼數量,需要存儲兩個參考值(例如顏色)。塊中每16個紋素的插值因子需要存儲起來,沿着兩個參考值之間的一個線選擇一個值,例如等於兩個存儲顏色值的顏色值,或對兩個存儲顏色插值得到的顏色值。 這種壓縮只需要存儲的兩種顏色及每個像素的短索引值。

        7種編碼如表6.1所示。注意“DXT”表示是在DirectX9中,“BC”表示在DirectX10和後續版本中。從表中可以看到,BC1有兩個16位的顏色值(紅色5位,綠色6位,藍色5位),每個紋素有一個2位插值因子來選擇是從兩個存儲參考值中選一個,還是從兩個中間值中選擇一個。BC2編碼顏色和BC1一樣,但是給每個紋素添加了4位(bits per texel,bpt)來量化raw)alpha。對BC3,每個塊的RGB數據編碼都和DXT1塊一樣,在這個基礎上,alpha數據編碼使用了兩個8位參考值,以及每個紋素有一個3位的插值因子。每個紋素可以選擇兩個alpha參考值的一個,或選擇6箇中間值的一個。BC4只有一個通道,編碼和BC3中的alpha編碼一樣。BC5有兩個通道,每一個通道都和BC3中的編碼一樣。

        BC6H是爲高動態範圍(high dynamic range,HDR)紋理準備的,每個紋素的RGB三個通道都初始化了一個16位浮點值。這個模式使用了16位,結果是8bpt(bits per texel,bpt)。它有一種模式是針對單行的,另外一種模式是針對兩行的。在BC7中,每個塊可以有1到3行,存儲8bpt。目標是8位RBG紋理、RBGA紋理的高質量紋理壓縮。和BC6H有很多相同特性,但是它是針對LDR紋理的格式,BC6H是針對HDR的。注意,BC6H和BC7在OpenGL中對應地被稱爲BPTC—FLOAT和BPTC。這些壓縮技術不只是用於二維紋理,同樣也適用於立方體紋理或體紋理。

表 6.1 紋理壓縮格式。所有的壓縮塊爲4x4紋素。存儲那列表示的是每個塊的位數(B)和每個紋素的位數(bits per texel,bpt)。參考顏色的符號前面是顏色通道,後面是每個通道的位數。例如RGB565表示紅色是5位,綠是6位,藍色是5位。

        這些壓縮機制的主要確定是會丟失精度(lossy)。以BC1到BC5爲例,只有4個或8個插值被用來表示16個像素。如果一個瓦塊裏有着大量的不同值,會丟失一些精度。實際上,如果正確使用的話,這些機制是可以提供可以接受的保真度。

        BC1-BC5的問題是所有給塊用的顏色都是在RGB空間的一條直線上。例如,紅色,綠色,藍色不能表示一個單一塊。BC6H和BC7能支持更多行,所以可提供更高的質量。

        對OpenGL ES,有一個壓縮算法,稱爲Ericsson texture compression(ETC),包含在API中。這個機制的效果和S3TC一樣,可以快速解碼,隨機訪問,沒有直接查找,固定比例。它會把一個4x4紋素壓縮進一個64位的塊,每個紋素4位。基本原理如圖6.21所示。每個2x4塊(或4x2塊,基於那種效果更好)存儲一個基本顏色。每個塊也會從一個小的靜態查找表中選擇四個常量,塊中每個紋素可以選擇從該表添加其中一個值。這修改了每個像素的光照。紋理的質量和DXTC相當。

圖 6.21 ETC(Ericsson Texture Compression)對每個像素塊進行編碼,然後修改每個像素的光照來創建最終紋素顏色值。

        相比於ETC算法,ETC2,包含在OpenGL ES3.0中,使用了未使用位組合(unused  bit combinations)添加了更多的模式。未使用位組合是也是一種壓縮的表示,解壓後得到的也是相同的紋理。例如,在BC1中,將兩個參考顏色設置爲相同是無用的,因爲這將表示一個常量顏色塊,而只要一個參考顏色包含該常量顏色,就可以獲得該常量顏色塊。ETC2添加了兩種新的模式,使用4個顏色值,最終得到的模式是在RGB空間中的平面,用於處理平滑過渡。Ericsson alpha Compression(EAC)是對紋理alpha值得壓縮,這種壓縮和基於ETC壓縮類似,但是隻針對一個組成部分,並且結果紋理每個紋素存儲4位。可以選擇和ETC2一起。ETC1,ETC2,EAC都是OpenGL4.0的核心,OpenGL ES3.0,Vulkan及Metal的組成部分。

        法線貼圖的壓縮需要更加小心,通常RGB顏色的壓縮格式對法線xyz數據並不管用。大部分方法都利用了這個事實:法線是單位長度, 進一步假設其z分量爲正(對於切線空間法線的合理假設)。 這樣就只需要存儲法線的x和y分量。z分量可以計算如下:

這本身會導致少量的壓縮,因爲只存儲兩個分量,而不是三個。 由於大多數gpu本身並不支持三組件紋理,這也避免了浪費一個組件的可能性。 進一步的壓縮通常是通過以BC5 / 3Dc格式的紋理存儲x和y分量來實現的。如圖6.22所示。 由於每個塊的參考值限定了x和y分量的最小值和最大值,因此可以將它們視爲在xy平面上定義了一個邊界框。 3位插值因子允許在每個軸上選擇8個值,因此邊界框被劃分爲一個8×8的可能法線網格。 或者,可以使用EAC的兩個通道(用於x和y),然後計算上面定義的z。

圖 6.22 左圖: 球面上單位法線只需要對x和y分量進行編碼。右圖: 對於BC4/3Dc, xy平面上的一個方框將法線包圍起來,每個4x4塊的法線可以使用該方框內的8×8法線(爲清晰起見,此處僅顯示4×4塊法線)。

        在不支持BC5/3Dc或EAC格式的硬件上,一種常見的備用方法是使用dxt5格式紋理,並將兩個組件存儲在綠色通道和alpha組件中(因爲它們的存儲精度最高)。其他兩個組件未使用。

        PVRTC是應用在被稱爲PowerVR硬件上的紋理壓縮格式,如今廣泛應用在iPhones和iPads上。它以4x4紋素爲一個塊進行壓縮,併爲每個紋素提供2bit'和4bit兩種方案。關鍵思想是爲紋理提供了兩個低頻(平滑)信號,通過使用相鄰紋素數據的塊和插值得到。然後在兩個信號間每個紋素使用1位或2位進行插值。

        自適應可伸縮紋理壓縮(Adaptive scalable  texture compression,ASTC)會將一個n x m個紋素的塊壓縮進128bit,塊大小範圍從4x4到12x12,結果也不同,從低至每個紋素0.89 bit到每個紋素8 bit。ASTC使用了很多技巧來實現緊湊的索引表示, 每個塊可以選擇行數和端點編碼。此外, ASTC可以處理任何從1-4通道的紋理以及LDR和HDR紋理。 ASTC是OpenGL ES 3.2及後續版本中的組成部分。

         上述所有的紋理壓縮方案都是有損的,而當壓縮一個紋理時,在這個過程中花費時間不同。花費幾秒甚至幾分鐘進行壓縮,要想獲得高質量的壓縮, 這通常是作爲離線預處理完成的,並存儲起來供以後使用。當然,如果你只想花費幾毫秒,低質量的結果,紋理壓縮可以進行近實時壓縮,可立即使用。例如,天空盒每隔一秒左右會重新生成一次,因爲雲朵會有緩慢移動。因爲使用固定函數硬件,解壓縮會非常快。壓縮數據要比解壓花費的時間長很多,這種差異稱爲數據壓縮不對稱(data Compression asymmetry)

        Kaplanyan提出來一些方法來提升壓縮紋理的質量,包含顏色值紋理和法線貼圖, 建議使用每個分量使用16位來創建映射。 對於顏色紋理,然後執行直方圖重正化(histogram renormalization)(在這16位上),然後在着色器中使用縮放和偏置常數(bias constant)(每個紋理)來反轉其效果。 直方圖歸一化(histogram normalization)是一種將圖像中使用的值擴展到整個範圍的技術,可以有效增強對比。 每個分量使用16位可以確保在重正化之後,直方圖中沒有未使用的槽位,這減少了許多紋理壓縮方案可能引入的帶寬干擾。如圖6.23所示。 此外,Kaplanyan建議,如果75%的像素高於116/255,那麼紋理使用線性顏色空間,否則將紋理存儲在sRGB中。 對於法線貼圖,他還注意到BC5/3Dc經常獨立於y壓縮x,這意味着並不總是能找到最好的法線。 相反,他建議對法線使用以下誤差度量:

其中n是原始法線,nc是經過壓縮後又解壓的法線。

圖 6.23  紋理壓縮中,每個分量使用16位而不是8位的效果。從左到右依次是:原始紋理,每個分量8位的DXT1壓縮,每個分量16位且在着色器中經過了重正化的DXT1壓縮。 紋理已被渲染了強烈的燈光,以更清楚地顯示效果。

        需要注意到在不同的顏色空間進行壓縮紋理,會加速紋理的壓縮。常用的變換是從RGB->YCoCg:

其中Y是亮度,Co和Cg是色度。這個逆變換也不貴:

少量的增加。這兩種變換都是線性的, 從這個方程中可以看出6.6是一個矩陣-向量乘法,它是線性的(見方程4.1和4.2)。 這很重要,因爲它可以存儲YCoCg,而不是在紋理中存儲RGB;  紋理硬件仍然可以在YCoCg空間中進行濾波,然後像素着色器可以根據需要轉換回RGB。注意,這種轉換本身是有損的,這可能有關係,也可能沒有關係。

        RGB->YCoCg變換的另外一種可逆的變換總結如下:

其中》表示右位移。這意味着可以兩者之間來回轉換。 需要注意的是,如果RGB中的每個分量都有n位,那麼Co和Cg都有n + 1位,以保可逆轉換;Y只需要n位。

6.3 程序紋理(Procedural Texture)

        給定一個紋理空間位置,執行圖像查找(image lookup)是生成紋理值的一種方法。 另一種方法是對函數求值,被定義爲程序紋理(procedural texture)

        儘管程序貼圖紋理通常用於離線渲染應用程序,但圖像紋理在實時渲染中更爲常見。這是由於在現代gpu中圖像紋理硬件的超高效率,它可以在一秒鐘內執行數十億次紋理訪問。然而,GPU架構正朝着更便宜的計算和(相對)更昂貴的內存訪問的方向發展。這些趨勢使得過程紋理在實時應用中得到了更大的應用。

        體紋理是程序紋理的一個非常有魅力的應用。考慮到體圖像紋理需要高存儲,這些紋理可以通過各種技術生成,最常用的方法是使用一種或多種噪聲函數來生成。如圖6.24所示。 噪聲函數通常是在連續的2次冪的頻率上採樣,稱爲octaves每個octave給一個權重,通常會隨着頻率增加而衰減,這些加權採樣的和稱爲turbulence功能

圖 6.24 兩個使用體紋理的實時生成的程序紋理例子。左邊大理石是使用光線行進(ray marching)渲染的半透明體紋理。右圖的物體是由在真是環境上使用了複雜的程序木材質着色器的合成圖像生成。

        因爲計算噪聲的花費較大,在三維數組中的格點通常是預先計算的,然後使用在紋理插值上。有許多方法可以使用顏色緩衝混合來快速生成這些數組,例如Perlin,Olano,McEwan,Parberry,Green,Cook和DeRose這些人提出的算法。

         另一種類型的程序紋理是物理模擬或其他交互過程的結果,如水波或擴展的裂縫。 在這種情況下,程序紋理可以有效地在動態條件下產生無限的變化。

         當生成一個二維的程序紋理時, 參數化問題可能比創建紋理帶來更多的困難,在創建紋理時,拉伸或縫合導致的瑕疵可以手工修改或修改。 一個解決方案是通過直接在表面合成紋理來完全避免參數化。 在複雜表面操作是一項技術挑戰,也是一個活躍的研究領域。

        程序紋理的反走樣要比圖像紋理的反走樣既困難又簡單。一方面,預先計算方法,例如mipmapping不可用,給程序員帶來負擔,另外, 程序紋理構造者擁有關於紋理內容的“內部信息”,因此可以對其進行調整以避免走樣。 這對於通過合併多重噪聲函數而創建的過程紋理尤其正確。 每個噪聲函數的頻率是已知的,因此任何可能引起走樣的頻率都可以丟棄,實際上降低了計算的成本。

6.4 紋理動畫(Texture Animation)

        應用在表面的紋理並不一定是靜態的(static),例如, 視頻源可以用紋理,每幀都在變化。

        紋理座標也不一定是靜態的, 無論是在網格的數據本身或通過應用在頂點或像素着色器的函數。 想象一下,建模一個瀑布,它的紋理需要看起來就像落水一樣。假設v座標就是水流的方向,爲了確保水流動, 每一個連續幀的v座標都需要減去一個分量。 從紋理座標中減去一個值會使紋理本身看起來向前移動。

         更精細的效果可以通過對紋理座標使用一個矩陣變換。 除了平移之外,還允許進行線性轉換,如縮放、旋轉和剪切,圖像扭曲變形變換以及廣義投影。 通過使用紋理混合技術,可以實現其他動畫效果。例如,從大理石紋理開始,逐漸淡化爲肉體紋理,就可以使雕像栩栩如生。

6.5 材質貼圖(Material Mapping)

         紋理的一個常見用法是修改材質屬性來影響着色方程。 真實世界的物體表面通常有不同的物質屬性。 爲了模擬這樣的物體,像素着色器可以從紋理中讀取值,並在計算着色方程之前使用它們修改材質參數。 最常被紋理修改的參數是表面顏色。 這種紋理被稱爲反射率彩色貼圖(albedo color map)漫反射彩色貼圖(diffuse color map)。 但是,任何參數都可以通過紋理進行修改:替換它、乘以它或以其他方式更改它。 例如,在圖6.25中,三個不同的紋理被應用到一個表面,代替了常量值。

圖 6.25 金屬磚和砂漿。 右邊是表面顏色的紋理,粗糙度(越亮越粗糙),凹凸貼圖的高度(越亮越高)。

         紋理在材料中的使用可以更進一步。 不需要修改方程中的參數時,紋理可以用來控制像素着色器本身的流(flow)和功能。 兩個或兩個以上材質有不同的着色方程和參數時,可以通過一個紋理指定表面的哪些區域有哪些材質來應用到一個表面上,從而導致對每個材質執行不同的代碼。 例如,有一些生鏽區域的金屬表面可以使用一個紋理來指示鏽跡的位置,根據紋理查找有條件地執行着色器的生鏽部分,否則執行閃亮的金屬着色器。

         着色模型的輸入,如表面顏色,與着色器輸出的最終顏色有線性關係。 因此,包含這些輸入的紋理可以用標準技術進行填充,避免走樣。 包含非線性着色輸入的紋理,例如粗糙度或凹凸貼圖(章節6.7),需要更加小心以避免走樣。 考慮到着色方程的濾波技術可以改善這些紋理的結果,這些技術將在9.13節講到。

6.6  Alpha貼圖(alpha mapping)

        Alpha值可以用於許多使用alpha混合或alpha測試的效果,例如有效渲染樹葉、爆炸和遠處物體等等。 本節討論了alphas中紋理的使用,同時指出了各種限制和解決方案。

         一種與alpha紋理相關的效果是貼花(decaling)。 例如,假如你想在茶壺上畫一幅花的圖案,你不需要整幅圖,只需要花所在的部分。 通過給紋素的alpha賦值0,可以使它透明,這樣就不會產生任何影響。 因此,通過適當地設置貼花紋理的alpha值,您可以用貼花替換或混合底層表面。 通常,鉗制對應功能(clamp corresponder function)是用一個透明的邊框把貼花的一個副本(相對重複的紋理)應用到表面。 圖6.26展示了貼花是如何應用的。有關貼花的更多信息,請參見20.2節。

圖 6.26 貼花的一種實現。先渲染一次場景得到幀緩衝, 然後渲染一個框(box),對於框內的所有點,貼花紋理被投影到幀緩衝內容中。最左邊的紋素是完全透明的,所以它不會影響到幀緩衝,黃色紋素之所有看不見,是因爲它被投影到表面隱藏起來的那部分上了。

        Alpha紋理的另一個應用是製作Cutouts。 假設你製作了一個灌木的貼花圖像,並將它應用到場景中的一個矩形上。 其原理與貼花相同,不同之處在於,cutout不會與下墊面齊平,cutout是繪製在其背後的任何幾何圖形的頂部。 通過這種方式,使用單個矩形,您可以呈現具有複雜輪廓的對象。

        在灌木的例子中,如果你旋轉觀察它,錯覺就失敗了,因爲灌木沒有厚度。一種方法是複製這個矩形並沿灌木軀幹旋轉90度。這兩個矩形構成了一個便宜的三維灌木,有時被稱爲“交叉樹”(cross tree)。 這種錯覺從地面上看是相當有效的。如圖6.27所示。 Pelze提出了一個類似的方案,使用三個cutouts來表示草。在13.6節,將會介紹一種稱爲廣告牌(billboarding)的技術, 用來將這樣的渲染減少到一個單一的矩形。如果觀察者移動到地面以上,當從上面看到灌木時,錯覺就會消失,將會看到兩個cutouts。如圖6.28所示。 爲了解決這個問題,可以通過不同的方式添加更多的cutouts——切片、分支、分層——來提供更有說服力的模型。

圖 6.27 左邊展示的是灌木的紋理貼圖和1bit的alpha通道貼圖。右邊是灌木渲染在一個矩形中,通過添加第二個(複製第一個)矩形,然後旋轉90度,可以形成一個便宜的三維灌木。

圖6.28  從離地面稍遠一點的地方看“交叉樹”灌木,然後離得更遠看,發現錯覺就消失了。

         結合alpha紋理和紋理動畫可以產生令人信服的特殊效果,如閃爍的火炬,植物生長,爆炸,和大氣效果。

         有幾個選項可以用來渲染帶有alpha貼圖的對象。Alpha混合(5.5節)允許部分透明值(便於對象邊緣反走樣),以及部分透明對象。 然而,alpha混合需要不透明三角形渲染完後再去渲染需要混合的三角形,並按從後到前的順序渲染。一個兩個cutout紋理構成的交叉樹,沒有渲染順序是正確的,因爲每個一個四邊形都有一部分在另外一個的前面。例如,一塊草地可能有成千上萬個由2個cutout表示的草, 每個網格對象可以由許多獨立的方片。明確地對每個方片進行分類是非常不切實際的。

         在渲染時,這個問題可以通過幾種不同的方式得到改善。 一種是使用alpha測試,有條件地丟棄alpha值低於像素着色器中給定閾值的片段的過程。操作如下:

其中,texture.a是紋理查找的alpha值, 參數alphaThreshold是一個用戶提供的閾值,它決定哪些片段將被丟棄。 這個二進制可見性測試允許三角形以任意順序呈現,因爲透明片段會被丟棄。 我們通常希望對任何alpha值爲0.0的片段執行此操作。 丟棄完全透明的片段還有一個額外的好處,即節省了進一步的着色器處理和合並的成本,以及避免在z-buffer中錯誤地將像素標記爲可見。 對於cutout,我們通常將閾值設置爲0.0以上,比如0.5或更高,然後進一步忽略alpha值,而不是將其用於混合。這樣可以避免無序的瑕疵。 但是,質量很低,因爲只有兩層透明(完全不透明和完全透明)。 另一種解決方案是爲每個模型執行兩個pass,一個用於固體cutout,它被寫入z緩衝區,另一個用於半透明的採樣,它不是寫入z緩衝區。

         Alpha測試還有另外兩個問題,即放大倍數過大和縮小倍數過大。 當alpha測試與mipmapping一起使用時,如果不進行不同的處理,效果可能難以令人信服。如圖6.29上圖所示那樣, 樹葉變得比預期的更透明。這可以用一個例子來解釋。 假設我們有一個具有四個alpha值的一維紋理,即(0.0,1.0,1.0,0.0)。 通過平均,下一個mipmap級別變成(0.5,0.5),然後最高級別是(0.5)。 現在,假設我們使用αt = 0.75。當訪問mipmap level 0時,可以看到每4個紋素將有一個會通過丟棄測試。 但是,當訪問接下來兩個級別時,由於0.5 < 0.75,所有內容都將被丟棄。

圖 6.29 上圖: 使用了mipmapping的alpha測試,沒有任何修正。下圖: 根據覆蓋率重新調整了alpha值的alpha測試。

圖 6.30  頂部是不同mipmap級別的葉子的混合,較高的級別爲了能見度進行了放大。 底部顯示是alpha測試值爲0.5的不同級別的mipmap,展示了遠離物體時像素是如何減少的。

        Castano提出了一個簡單的解決方案。對mipmap級別爲k,所以平均值ck定義爲:

其中,nk是mipmap k級的紋素數量,α(k,i)是像素i在mipmap k級的alpha值。αt是用戶爲方程6.9提供的閾值。在這裏,我們假設 α(k,i) > αt時結果爲1,否則爲0。注意k=0表示最低級別的mipmap,即原始紋理。 對於每一個級別的mipmap,然後我們找到一個新的mipmap閾值αk,而不是使用αt, 這樣ck等於c0(或儘可能接近)。這可以由二進制搜索來完成。最終,在mipmap k級的全部紋素的alpha值被縮放爲αt/αk。圖6.29下圖使用的就是這種方法,NVIDIA的紋理工具有支持這個方法。

         Wyman和McGuire提出了一種不同的解決方案,公式6.9的一行代碼在理論上被替換爲:

隨機函數返回了[0,1]之間的統一值,這會使得平均值是對的。例如,如果紋理查找的alpha值是0.3,片元將會有30%的機率被丟棄。 這是一種隨機透明的形式,每個像素只有一個樣本。 在實際應用中,爲了避免時空高頻噪聲,將隨機函數替換爲哈希函數:

對上述函數的嵌套調用形成了一個三維散列,即

返回[0,1)之間的一個值。

         透明至覆蓋(alpha to coverage)和類似功能的透明度自適應反走樣,獲取片元的透明度值,並傳遞進像素覆蓋的採樣點。這個想法和5.5節說到的screen-door透明很像,只不過在子像素級別上。 假設每個像素有四個樣本位置,一個片元覆蓋一個像素,由於cutout紋理,有25%的透明75%的不透明。透明至覆蓋模式使片元變得完全不透明,但它只覆蓋了四個樣本中的三個。 因爲每個繪製的樣本都是完全不透明的,最靠近的葉片會隱藏在它後面的物體。 正確地混合半透明的邊緣像素是不需要排序的,因爲alpha混合已經關閉。

        透明至覆蓋對alpha測試反走樣效果很好,但是在alpha混合的時候會有瑕疵。 例如,兩個具有相同alpha覆蓋率的alpha混合片元將會使用相同的子像素模式,這意味着一個片元將完全覆蓋另一個片元,而不是與它混合。 Golus提出了使用fwidth()着色器指令來給內容提供更清晰的邊緣。參見圖6.31。

圖 6.31 葉子紋理的邊緣有部分alpha覆蓋,從左到右的渲染技術依次是:alpha測試(alpha test),alpha 混合(alpha blend),透明至覆蓋(alpha to coverage),有尖銳邊緣的透明至覆蓋(alpha to coverage with sharpened edges)。

         任何使用alpha紋理,瞭解雙線性插值是如何影響顏色值的都很重要。假設有兩個相鄰的紋素:rgba=(255,0,0,255)是一個純紅色,相鄰的紋素rgba=(0,0,0,2),黑色,幾乎完全透明。那麼兩個紋素中間位置的rgba會是多少呢?簡單的插值得到的是(127,0,0,128),rgb顏色是一個暗紅色。 然而,這個結果實際上並不是暗的,它是一個已經預先乘以它的alpha的完全紅色。如果你插值alpha值, 爲了得到正確的插值,你需要確保被插值的顏色在插值之前已經預乘了alpha。舉個例子,假設幾乎透明的紋素的rgba設爲(0,255,0,2),給它加了一點點綠色。這個顏色沒有預乘alpha值,插值後結果變成(127,127,0,128)。 微小的綠色突然把結果變成了一個(預乘的)黃色樣本。 這個相鄰的紋素的預乘的rgba如果是(0,2,0,2),則給出了(127,1,0,128)的預乘結果。 這個結果更有意義,結果的預乘顏色大多是紅色,帶有一點難以察覺的綠色。

         忽略雙線性插值,會導致在decal和cutout對象周圍產生黑邊。暗紅結果會被接下來的管線當作未預乘的顏色對待,邊緣會變黑。這種效果在alpha測試也會看到。 最好的策略是在雙線性插值之前進行預乘。 WebGL的API支持這一點,因爲合成對於web頁面很重要。 然而,雙線性插值通常是由GPU執行的,在這個操作完成之前,着色器無法對紋素值進行操作。 圖像在PNG等文件格式中沒有預乘,因爲這樣做會失去顏色精度。 當使用alpha紋理時,這兩個因素結合在一起會導致默認的黑色邊緣。 一種常見的解決方法是對cutout紋理進行預處理,在透明部分塗上“黑色”紋素,一種從附近不透明紋素的顏色衍生出來的顏色。 所有的透明區域通常都需要用這種方式重新繪製, 手工或自動, 因此,mipmap級別也就避免了邊緣問題。 同樣值得注意的是,在使用alpha值形成mipmaps時,應該使用預乘值

6.7 凹凸紋理(Bump Mapping)

        本節描述了一個大類:小規模細節表示技術,統稱爲凹凸貼圖(bump mapping)。這些方法都是通過修改每個像素的着色方程來實現的。它們比單獨的紋理映射更具有三維效果,但是沒有添加任何額外的幾何圖形。

        物體細節可以被分爲三個尺度:宏觀(macro-features),覆蓋很多像素,中觀(meso-features),包含一些像素,微觀(micro-features),大小小於一個像素。這些類別有些不固定,因爲觀察者可以在不同的距離觀察同一個物體。

        宏觀幾何表示的是頂點、三角形或其他幾何圖元。當創建一個三維角色,四肢和頭部是典型的宏觀模型。微觀幾何封裝在着色模型中, 它通常在像素着色器中實現,並使用紋理映射作爲參數。着色模型模擬了表面微觀幾何的交互,即,有光澤的物體是在微觀下是光滑的,漫反射表面在微觀下是粗糙的。 角色的皮膚和衣服有不同的材質,因爲它們使用不同的着色器,或者至少在這些着色器中有不同的參數。

        中觀幾何描述的是介於這兩者之間的一切。它包含的細節過於複雜,無法有效地使用單獨的三角形渲染,但它足夠大,可以讓觀察者在幾個像素上區分表面曲率的個別變化。 人物臉上的皺紋,肌肉組織的細節,衣服的褶皺和接縫,都是中觀尺度的。 一組統稱爲凹凸紋理的技術通常用於中觀尺度建模。 它們在像素級調整着色參數, 觀察者可以感覺到從基礎幾何結構中產生的微小擾動(有立體感), 它實際上是平坦的。 不同類型凹凸貼圖的主要區別在於它們是如何表示細節特徵的。 變量包括了寫實的等級和細節特徵的複雜性。 例如,數字藝術家通常給模型雕刻細節,然後用軟件將這些幾何元素轉換成一個或多個紋理,例如凹凸紋理,或者漸暗紋理(crevice-darkening texture)

         Blinn在1978年提出了在紋理中編碼中觀尺度細節的想法。他觀察到在着色期間,如果我們用一個輕微擾動的表面法線代替真正的法線,表面似乎有小規模的細節。 他將描述擾動表面法線的數據存儲到一個數組中。

         關鍵的思想是,我們不是使用紋理來改變光照方程中的顏色分量,而是使用紋理來修改表面法線。 曲面的幾何法線保持不變;只是修改了照明方程中使用的法線。 這個操作沒有物理意義;我們改變表面的法線,但表面本身在幾何意義上仍保持光滑。 就像每個頂點都有一個法向量會給人一種表面在三角形之間是光滑的錯覺,修改每個像素的法向量會改變三角形表面本身的感知,而不需要修改它的幾何形狀。

         對於凹凸貼圖,法線必須相對於某些參照系改變方向。 爲此,在每個頂點上存儲一個切線座標系(tangent frame),也稱爲切線空間基(tangent-space basis)。 這個參照系被用來將光轉換到一個表面位置空間(surface location’s space)(反之亦然)來計算擾動法線的效果。 對於應用了法線映射的多邊形表面,除了頂點法線外,我們還存儲了所謂的切線(tangent)雙切線向量(bitangent  vectors)雙切線向量又被錯誤的認爲是副法線向量(binormal vector)。

         切向量和雙切線向量表示法線貼圖本身在模型空間中的軸, 因爲我們的目的是將光線轉換到相對於地圖的光線。如圖6.32所示。

圖 6.32  一個球面三角形和頂點的切線展示。 球體和圓環面的形狀有一個自然的切線空間基,就像圓環面上的經緯度線所顯示的那樣

        法線n,切線t和雙切線b,三個向量構成了一個基礎矩陣:

這個矩陣有時候縮寫爲TBN(Tangent Bitangent Normal),將光的方向(對於給定的頂點)從世界空間轉換爲切線空間。 這些向量不一定相互垂直, 因爲法線貼圖本身可能被扭曲以適應表面。 然而,非正交基會導致紋理傾斜,這意味着需要更多的存儲空間,同時也會影響性能,例如。,那麼這個矩陣就不能被一個簡單的轉置所反向。 一種節省內存的方法是隻存儲頂點處的切線和雙切線,然後取它們的叉乘來計算法線。 然而,只有當矩陣的旋向性總是相同時,這種方法纔有效。 模型通常是對稱的:飛機、人、文件櫃和許多其他對象。 因爲紋理消耗大量的內存,所以它們經常被鏡像到對稱模型上。 因此,只存儲對象紋理的一側,但是紋理映射將其放在模型的兩側。 在這種情況下,切線空間的旋向性在兩邊是不同的,不能被假設。 在這種情況下,如果在每個頂點上都存儲了額外的信息以表明旋向性,那麼仍然可以避免存儲法線。 如果正切座標系是正交的,也可以將基存儲爲四元數,這種更節省空間,並且可以節省每像素的計算量。 質量上的小損失是有可能的,儘管在實踐中很少見到。

6.7.1 Blinn算法(Blinn's Methods)

         Blinn的原始凹凸紋理算法在紋理的每個紋素上存儲兩個有符號的值,bu和bv。 這兩個值對應的是沿着圖像u軸和v軸改變法線的量。 也就是說,這些紋理值,通常是雙線性插值的,被用來縮放正交垂直於法線的兩個向量。 這兩個向量加到法向量來改變方向。 bu和bv兩個值描述了曲面在該點的方向。如圖6.33所示。這種類型的凹凸貼圖紋理被稱爲偏移矢量凹凸貼圖(offset vector bump map)偏移貼圖(offset map)

圖 6.33 左圖,法向量n被從凹凸貼圖u和v方向上的值(bu,bv)修改了,得到n'(沒有歸一化)。右圖,展示了高度場及它對着色法線的影響, 這些法線可以代替插值之間的高度,爲了得到更平滑的外觀。

         另一種表示凹凸貼圖的方法是使用高度場(heightfield)來修改表面法線的方向。 每個黑白色紋理值表示一個高度,因此在紋理中,白色表示高區域,黑色表示低區域(或者反之亦然)。如圖6.34所示例子。 這是第一次創建或掃描凹凸貼圖時使用的一種常見格式,也是在1978年由Blinn提出的。和第一種算法類似,高度場也得到兩個有符號的值u和v。 這是通過取相鄰列之間的差來得到u的斜率,取相鄰行之間的差來得到v的斜率。 一種變體是使用Sobel濾波,它會給予直接相鄰的鄰居更大的權重。

圖 6.34  波狀高度場凹凸紋理及其在球面上的應用。

6.7.2 法線貼圖(Normal Mapping)

        凹凸貼圖的一個常用方法是用來存儲一個法線貼圖。該算法和結果在數學上和Blinn算法一樣,只是存儲格式和像素着色計算不同。

        法線貼圖會把(x,y,z)編碼映射到[-1,1],即對一個8bit紋理x軸的值,0對應-1.0,255對應1.0。圖6.35展示了一個例子。顏色值[128, 128, 255],一個淡藍色,表示一個平坦的表面,即法線爲[0,0,1]。

圖 6.35 存儲了法線貼圖的凹凸貼圖。每個顏色通道實際上表面的法線座標。紅色通道表示x偏差,越紅,越多的法線指向右邊,綠色表示y偏差,藍色表示z偏差。右圖是使用了法線貼圖生成的圖像。注意立方體頂部的扁平外觀。

        法線貼圖最初是作爲世界空間法線貼圖引入的,在實踐中很少用到。對這種類型的貼圖,擾動是很直接的, 在每個像素處,從紋理中檢索法線並直接使用,沿着光的方向,去計算表面上該點的着色。法線貼圖也可以定義在物體空間,這樣模型就可以被旋轉而法線仍然有效。 但是,世界空間和對象空間表示都將紋理以特定的方向綁定到特定的幾何圖形上,這限制了紋理的重用。

         相反,擾動法線通常是在切線空間中檢索, 即,相對於表面本身。 這允許表面的變形,以及最大限度地重複使用正常紋理。 切線空間法線也可以壓縮得很好,因爲z分量的符號(與未擾動曲面法線對齊的那個)通常可以假定爲正的。

        法線貼圖也可以很好地增強寫實感,如圖6.36。

圖 6.36  一個遊戲場景中使用的法線貼圖凹凸貼圖的例子。左上圖沒有使用右邊兩個法線貼圖。左下圖則應用了法線貼圖。右圖是兩個法線貼圖。

         相比較於顏色紋理的濾波,法線貼圖的濾波是一個難題。通常,法線和着色顏色值之間的關係並不是線性的 ,所以標準的濾波方法可能會導致走樣。 想象一下,樓梯是由閃閃發光的白色大理石砌成的。 某些角度,樓梯的頂部或側面可以捕捉到光線,並反射出明亮的鏡面高光。 然而,樓梯的平均正常角度是45度; 它會從與原來樓梯完全不同的方向捕捉高光。 當帶有高光的凹凸貼圖在沒有正確濾波的情況下渲染時,當採樣剛好處在高光來回閃爍位置,可能會出現吸引人的閃光效果。

        Lambertian曲面是一種特殊的情況,在這種情況下,法線貼圖對着色的影響幾乎是線性的。Lambertian着色幾乎完全是一個點積,這是一種線性操作。 對一組法線求平均值並對結果執行點積,相當於對單個法線點積結果求平均值:

注意這些平均向量在使用前並沒有歸一化。公式6.14展示了標準濾波和mipmap幾乎可以爲Lambertian表面生成正確的結果。結果不是非常準備因爲Lambertian着色方程不是一個點積;它是一個鉗制點積:max(l · n, 0)。鉗制操作使它不線性。 這將使表面過度變暗,以使光的方向發生偏光,但在實踐中這通常是可以接受的。 需要注意的是,一些通常用於法線貼圖的紋理壓縮方法(比如從另外兩個中重建z分量)不支持非單位長度的法線,因此使用非歸一化的法線貼圖可能會造成壓縮困難。

         在非Lambertian曲面的情況下,通過將着色方程的輸入作爲一個整體進行濾波,而不是單獨對法線貼圖進行濾波,可能會產生更好的結果。

        最後,從高度貼圖派生出法線貼圖可能很有用,h(x, y)。 首先,在x和y方向上的導數的近似值是用中心差分來計算的:

則在紋素(x,y)處的非歸一化法線爲:

需要注意紋理的邊界處。

6.8 視差貼圖(Parallax Mapping)

         凹凸和法線貼圖的一個問題是凹凸不會隨着視角改變位置,也不會互相遮蔽。 例如,如果你沿着真正的磚牆看,在某個角度你不會看到磚與磚之間的灰漿。磚牆的凹凸貼圖則永遠不會有這種遮擋,因爲它只是改變了法線。 最好是讓凹凸貼圖實際上影響的表面位置是渲染的每個像素。

         視差貼圖的概念是由Kaneko在2001年提出的,並由Welsh進行了改進和推廣。 視差是指當觀察者移動時,物體的位置也發生相對移動。 隨着觀察者移動時,凹凸應該看起來有高度。 視差映射的關鍵思想是通過檢查可視像素的高度來對像素中應該看到的內容進行有根據的猜測。

         對於視差貼圖,凹凸存儲在一個高度場紋理中。 當觀察表面上一個給定的像素時,在該位置會得到到一個高度場值,並用於偏移紋理座標以得到表面的不同部分。偏移的幅度取決於得到的高度值以及眼睛到表面的角度。如圖6.37所示。高度場值要麼存儲在單獨的紋理中,要麼打包在別的紋理未用到的顏色通道或者alpha通道中( 在打包不相關的紋理時必須小心,因爲這會對壓縮質量產生負面影響)。高度場值在被用來轉換座標前有被縮放和偏差 ,縮放決定了高度場在表面之上或之下延伸的高度,而偏差給出了“海平面”高度,在這個高度上不會發生偏移。給定一個紋理座標位置P,矯正過的高度場高度爲h,一個已經歸一化的觀察向量V(高度值爲Vz,而水平面分量爲Vxy)則新的視差矯正紋理座標Padj爲:

注意,不像大部分着色方程,在這裏執行計算的空間很重要,觀察向量需要定義在切線空間。

圖 6.37 左圖是目標: 表面上的實際位置可由觀察向量穿過高度場來確定。 視差貼圖做了一個一階近似,取右圖矩形上該位置的高度,然後用它找到一個新的位置Padj。

        通過一個簡單的近似, 如果凹凸高度變化相對緩慢,在實踐中效果相當好的。 鄰近的紋素也差不多相同的高度,所以利用原始位置的高度來評估新位置的高度是合理的。 然而,這種方法在淺視角下就行不通了。 當觀察向量接近表面水平線時,一個小的高度變化會導致大的紋理座標偏移,這種近似是失敗的。

        爲了改善這個問題,Welsh提出了偏移限制的觀點, 這樣做的目的是限制偏移量,使其永遠不會超過能檢索到的高度。方程爲:

注意,這個方程比上個方程的計算要快。 幾何上的解釋是,高度定義了一個半徑,超過這個半徑位置就無法有偏移。如圖6.38所示。

圖 6.38 視差貼圖的偏移限制。偏移量最多達到原始位置的高度的量,如以虛線表示圓弧。灰色箭頭表示的是原始方程的結果,黑色箭頭表示則是偏移限制後的結果。

6.8.1 視差遮蔽貼圖        

         凹凸貼圖不修改基於高度場的紋理座標,它只改變位置的着色法線。視差貼圖提供一個簡單的高度場效果的近似, 假設了一個像素點的高度與相鄰像素點的高度大致相同。這種近似容易出錯。這種凹凸不會彼此遮蔽,也不會有投影。我們想在這個像素點看到的是,觀察向量和高度場的第一個交點。

         爲了更好地解決這個問題,一些研究人員建議使用光線沿着觀察向量行進(ray marching),直到找到一個(近似的)交點。 這可以在像素着色中完成,其中高度數據可以通過訪問紋理得到。以這樣或那樣的方式利用光線的技術被歸納爲視差映射技術的一個子集。

         這些類型的算法被稱爲視差遮蓋貼圖(parallax occlusion mapping,POM)浮雕貼圖(relief mapping)方法,或其他名稱。 關鍵的思想是先沿着投影向量測試一個固定數量的高度場紋理樣本。對於掠射角度的觀察射線,通常會生成更多的樣本,這樣就不會錯過最近的交點。 沿着射線的每個三維位置都會被檢索到, 轉換到紋理空間,並處理,以確定它是在高度場的上面或下面。 一旦找到了高度場下面的一個樣本,它往下的距離,以及之前的樣本在上面的距離,會被用來找到交集位置,如圖6.39所示。 然後,使用附加的法線貼圖、顏色紋理和任何其他紋理,使用該位置對錶面進行着色。高度追蹤算法(heightfield tracing approach)同樣會對凹凸不平的表面有投影。如圖6.40所示。

圖 6.39 從眼睛發出的綠色光線投影到表面所在屏幕, 每隔一定的時間採樣一次(紫色的點),然後獲取高度。 該算法求出了人眼發出的射線與擬合曲線高度場的黑色線段的第一個交點。 

圖 6.40 未使用光線行進的視差貼圖(左圖)和使用了光線行進的視差貼圖的比較(右圖)。左圖立方體上面是平坦的,而使用了光線行進的右圖的立方體上面是有遮蔽效應的。

圖 6.41 法線紋理和浮雕紋理。法線紋理沒有自我遮蔽發生。 浮雕貼圖在重複紋理的輪廓上有問題,因爲矩形更多的是進入高度場的觀察,而不是真正的邊界定義。

圖 6.42  視差遮蔽貼圖,又名浮雕貼圖,讓道路上的石塊更具有真實感。 地面實際上是一組應用了高度場的簡單的三角形。

6.9 紋理光照(Textured Lights)

         紋理也可以用來增加光源的視覺豐富性,並允許複雜的強度分佈或聚光燈功能。 對於把所有的照明都限制在圓錐或截錐體上的光,投射紋理可以用來調節光的強度, 這允許聚光燈有形狀,有圖案的光,甚至“幻燈片放映機”的效果(圖6.43所示)。 這些光通常被稱爲gobo或cookie光。

圖 6.43  投影紋理光(Projective textured light)。 紋理被投射到茶壺和地平面上,用來調節光在投影平截頭體中的貢獻(在平截頭體外被設置爲0)。

        對於不侷限於截錐體,而是向各個方向照射的光線,可以使用一個立方體貼圖來調節強度,而不是二維投影紋理。 一維紋理可以用來定義任意距離的衰減函數。 結合一個二維的角度衰紋理,這可以考慮用於複雜的體積照明模式。 一個更普遍的可能性是使用三維(體積)紋理來控制光線的衰減。 這允許任意體積的效果,包括光束。這種技術是內存密集型的(就像所有的體紋理一樣)。如果光的體積效果沿着三個軸都是對稱的, 通過將數據鏡像到每個八分圓,這樣將內存佔用減少8倍。

         紋理可以添加到任何光類型,以啓用額外的視覺效果。紋理光允許藝術家們簡單控制光照,他們可以簡單編輯使用的紋理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章