P5r 天下第一!學妹真可愛!
Mesh 在 Three.js 內是個重要的概念,它是用於在場景中渲染三維物體的關鍵組件。Mesh 通過結合 Geometry(幾何體) 與 Material (材質)創建出了可以在渲染器內顯示的可視化對象。
在 Three.js 中,Material 可以包含複數個 Texture(紋理 / 貼圖)用以描述物體表面的細節,如物體表面的凹凸可以使用法線貼圖(normal map)或者位移貼圖(displacement map)、物體表面的環境映射可以使用環境光貼圖(spherical map /cube map)、物體表面的 kd (漫反射系數、也屬於材質的一部分屬性) 與不需要即時計算的陰影(Ambient Occlusion map)等也可以使用 Texture 來實現。
常見 Texture 的類型#
Texture 根據不同的使用場景可被分為多種類型,以下將介紹其中常見的一部分。
Color Map(顏色貼圖)#
Color Map 用於將顏色信息應用到三維模型的表面。它是物體材質的一部分,用於賦予模型以特定的顏色。Color Texture 可以包含物體的基本顏色,也可以用於模擬物體的紋理、圖案、細節等。
其作用包括:
- 基本顏色: 最常見的用途是為物體提供基本的顏色。你可以將一張純色的貼圖應用到物體上,從而使物體呈現特定的外觀。這可以用於模擬各種不同類型的材質,如金屬、塑料、木材等。
- 紋理: 你可以將一張包含圖案、紋理或細節的貼圖應用到物體上,從而在模擬物體表面的視覺細節時提供更多的控制。這可以使物體看起來更加真實,比如木紋、石紋等。
- 顏色變化: 通過在貼圖中使用不同的顏色區域,你可以使物體的顏色在不同的部分發生變化,從而實現藝術上的效果或者強調物體的特定部分。
- 定制效果: 你可以使用 Color Texture 來實現各種視覺效果,比如為一個車輛模型添加品牌標誌,或者為一個角色模型添加獨特的外觀。
Alpha Map#
Alpha Map 用於指定三維模型表面上每個像素的透明度值(Alpha 通道)。透明度貼圖允許你在渲染時控制物體的透明程度,從而實現透明、半透明和不透明的效果。
如圖中左側的樹葉的紋理在經過黑白部分(白色區域可見、黑色區域不可見)的 Alpha Texture 處理後便會裁剪掉黑色部分的區域,從而得到右側乾淨的樹葉。
其作用包括:
- 透明效果: 透明度貼圖可以使物體的部分區域變得透明,從而讓你能夠實現像玻璃、水、煙霧等透明效果。
- 半透明效果: 通過透明度貼圖,你可以讓物體的部分區域變得半透明,從而在視覺上模擬材質的半透明特性,比如薄霧、雲朵等。
- 不透明物體的透明部分: 即使是不透明的物體,也可能有部分區域需要是透明的,如一個物體上的玻璃窗戶。
- 複雜的圖案和紋理: 透明度貼圖可以結合顏色貼圖來實現複雜的圖案和紋理,其中一些區域是透明的。
Normal Map(法線貼圖)#
Normal Map 被用於在渲染中模擬表面細節,從而增強物體的視覺效果。法線貼圖並不會改變物體的實際幾何結構,而是通過在每個像素處存儲法線信息,改變光照計算,從而使物體在渲染時看起來具有更多的細節和深度。由於其並不會實際改變幾何結構,因此在即時光照下生成的陰影也只會是原本幾何結構的現狀。
其作用包括:
- 增加細節: 法線貼圖可以在不增加多邊形數量的情況下,給物體表面增加細節。這使得物體看起來更加真實,因為光照效果會因為法線的改變而發生變化,營造出凹凸的效果。
- 模擬凹凸效果: 法線貼圖可以用來模擬物體表面的凹凸效果,如凹陷、突起、皺紋等。這些細節會在光照計算中產生陰影和高光,使物體看起來更具質感。
- 節省多邊形數量: 使用法線貼圖可以避免使用大量的多邊形來表示物體的細節,從而減輕計算負擔,提高性能。
Ambient Occlusion Map(環境遮蔽貼圖)#
環境遮蔽貼圖(Ambient Occlusion Map)被用於在渲染中模擬環境遮蔽效果。它是一種灰度圖像,其中每個像素表示物體表面在凹陷或相對遮擋的程度,從而可以用於調整物體在渲染時的光照效果,增加細節感和深度感。
其作用包括:
- 模擬遮蔽效果: 環境遮蔽貼圖可以模擬環境中的遮擋和遮蔽,使暗角和凹陷的區域看起來更暗。這增加了物體的深度感和視覺細節。
- 增強表面質感: 通過在環境遮蔽貼圖中存儲凹凸信息,可以使物體的表面看起來更具紋理和質感。
- 增加真實感: 使用環境遮蔽貼圖可以增加渲染的現實感,使物體看起來更接近真實世界中的光照和遮擋效果。
Metalness Map(金屬度貼圖)#
Metalness Map 用於在渲染中控制物體表面的金屬度屬性。金屬度是一個決定物體是金屬性還是非金屬性(絕緣體)的屬性。金屬度貼圖在 PBR(Physically Based Rendering,基於物理的渲染)中非常常見,用於實現更真實的渲染效果。
- 控制金屬度: 金屬度貼圖的每個像素可以表示對應區域的金屬度屬性。對於金屬物體,金屬度值較高,而對於非金屬物體,金屬度值較低。
- 影響反射: 金屬度會影響物體對光的反射行為。金屬物體具有高反射率,而非金屬物體的反射率相對較低。
- 增加真實感: 通過使用金屬度貼圖,你可以讓物體的不同部分表現出不同的金屬性質,從而增加渲染的真實感。
Roughness Map(粗糙度貼圖)#
Roughness Map 用於在渲染中控制物體表面的粗糙度屬性。粗糙度是一個決定物體表面光滑度的屬性,粗糙的表面會散射光線,使物體的反射更模糊,而光滑的表面會產生更銳利的反射。
其作用包括:
- 控制光滑度: 粗糙度貼圖的每個像素可以表示對應區域的粗糙度屬性。高粗糙度值意味著表面更加粗糙,低粗糙度值表示表面更加光滑。
- 影響反射: 物體表面的粗糙度會影響光線的散射程度,從而影響物體的反射行為。光滑表面會產生清晰的反射,而粗糙表面會產生模糊的反射。
- 增加真實感: 通過使用粗糙度貼圖,你可以為物體的不同部分設置不同的粗糙度,從而增加渲染的真實感和視覺細節。
Code#
這裡我寫了一個 demo 可以進一步了解對應貼圖的表現。下面聊聊我自己在學習中遇到的一些比較重要的點。
Texture Magnification & Texture Minification (紋理放大與縮小)#
在聊紋理放大 / 縮小前,需要明確一個前提 —— 紋理的最小單位 texels(紋理像素) 與 pixels(像素) 不同,前者由紋理本身的精度決定大小,而後者由物理設備決定大小,因此有可能出現一個 texel 內包含多個 pixel 或是一個 pixel 內出現多個 texel 的情況。也就是說 texel 與 pixel 並不一定會完全重合,從而出現紋理覆蓋到模型表面時會出現異常(如走樣、摩爾紋之類的)
Texture Magnification(紋理放大)#
舉例來說如果存在一個 200 * 200 的紋理圖像需要被應用在 500 * 500 的平面上時必然會出現走樣,我們需要了解紋理的每個 texel 在被應用到模型時需要經過 (u, v) 的轉換,在這個例子內紋理會被拉伸放大,一個紋理像素的內容會被應用到 6.25 個像素上(也就是說這 6.25 個像素會擁有相同性質 —— 不管是顏色還是法線方向)這顯而易見的會使紋理變得模糊不清(如下圖左)。
雖然上圖並非是我所舉的例子,但圖左的效果便是多個 pixel 使用了同一個 texel 的值導致的,在 Three.js 中也存在對應的屬性(Three.NearestFilter)
NearestFilter returns the value of the texture element that is nearest (in Manhattan distance) to the specified texture coordinates.
NearestFilter 的效果肉眼可見的糟糕,但好在它不需要額外的計算,對於一些不重要的內容可以選用。
除此之外,Three.js 也在紋理放大的場景下提供了 LinearFilter 的選項,從描述上看不難判斷出該選項基於 Bilinear Interpolation(雙線性插值)
LinearFilter is the default and returns the weighted average of the four texture elements that are closest to the specified texture coordinates, and can include items wrapped or repeated from other parts of a texture, depending on the values of wrapS and wrapT, and on the exact mapping.
就如引言內提到的,對應像素上的值為其附近的 4 個紋理像素上的加權平均,該思想便是雙線性插值的體現(Games101 p9 0:28 處有詳細介紹、此處不展開),而這種方法會使圖像擁有更加柔和的漸變(見上圖中間部分)。
Texture Minification 與 mipmap(紋理縮小與 mipmap)#
與 Magnification 的情況相反,如果是在單個 pixel 內渲染多個 texel (如下圖所示意的)。
可以從圖內看到一個像素內存在有多個紋素,如果按照之前 Nearest 的方式去應用距離像素中心最近的紋素的值,很顯然也會丟失掉大多數信息從而導致走樣(Three.NearestFilter),那如果使用上面提到的雙線性插值(Three.LinearFilter)是否可以解決問題呢?如果該像素內只存在 4 個紋素倒是可以解決走樣的問題,但如果一個像素內存在超過 4 個紋素那該走樣的依然還是會走樣,除非不斷增加參與線性插值的紋素數量 —— 比如 Bicubic Interpolation 就是通過對周圍 16 個點進行加權平均從而提高精度來對抗走樣,但很容易想到通過增加計算量來防止走樣這樣的做法無疑對性能有著很大的要求,因此為了減輕對客戶端的性能開銷,圖形學內引入了一種名為 mipmap 的技術 —— 將在其他機器上計算的結果塞入到 Texture 內,在 Minification 時直接使用 mipmap 上已有的計算結果從而減少對客戶端的算力要求。(屬於是空間換時間了,詳細介紹可看 Games101 p9 0:43 或者參考資料內的 Real-Time Rendering 4th)
在 Three.js 內關於 mipmap 的選項有:
-
THREE.NearestMipmapNearestFilter:選擇與要著色的像素大小最匹配的 mipmap,並使用 NearestFilter 條件(最靠近像素中心的紋素)生成紋理值。
-
THREE.NearestMipmapLinearFilter:選擇與要紋理的像素大小最匹配的兩個 mipmap,並使用 NearestFilter 標準從每個 mipmap 生成紋理值。最終紋理值是這兩個值的加權平均值。
-
THREE.LinearMipmapNearestFilter :選擇與要紋理的像素大小最匹配的 mipmap,並使用 LinearFilter 標準(最接近像素中心的四個紋素的加權平均值)來生成紋理值。
-
THREE.LinearMipmapLinearFilter:(默認)選擇與要紋理的像素大小最匹配的兩個 mipmap,並使用 LinearFilter 標準從每個 mipmap 生成紋理值。最終紋理值是這兩個值的加權平均值。
關於 Three.js 內與紋理放大 / 縮小相關的官方例子可參考。