文章目錄
-
- 3.1 如何在三角形内部進行屬性的插值?
-
- 3.1.1 重心坐标
-
- 什麼是重心坐标?
- 如何求得三個系數,也就是如何求得重心坐标呢?
-
- 方法一:面積比
- 方法二:公式法
- 3.1.2 求得重心坐标後,如何用它進行插值呢?
- 3.2 如何将紋理應用在實際的渲染中?
-
- 3.2.1 紋理的放大(紋理太小了怎麼辦?)
-
- 紋理過小導緻的問題描述
- 雙線性插值
- 雙向三次的插值
- 3.2.2 如果紋理太大了怎麼辦?
-
- 紋理太大導緻的問題描述
- 解決方法一:超采樣
- 解決方法二:Mipmap
-
- 什麼叫mipmap?
- 如何知道要查詢的正方形區域有多大?
- 如何用mipmap來查詢正方形區域的平均值?
- 用三線形插值(正方形)來平滑過渡層與層之間
- 各向異性過濾 Ripmap(矩形區域)
- EWA過濾(不規則形狀,橢圓)
- 重心坐标,為了做插值
- 紋理如何貼在物體表面
- 紋理的其他應用
3.1 如何在三角形内部進行屬性的插值?
3.1.1 重心坐标
為什麼要在三角形内部做插值?
- 當我知道三角形三個頂點的屬性的時候,我們希望通過此知道三角形内部的一些值。并且插值可以做到平滑的過渡。
插值什麼内容?
- 可以在三角形頂點上定義各種不同的屬性,可以通過插值此知道三角形内部點的屬性。這些屬性可以是位置,紋理坐标,顔色,法線,深度,材質等。
如何做插值?
- 通過重心坐标。
什麼是重心坐标?
重心坐标是定義在一個三角形上的。在三角形 ABC 所形成的平面内的任意一點(x, y) 可以表示成三個頂點坐标的線性組合。注意!需要滿足一個條件:線性組合的三個系數加起來等于 1。這三個系數的組合就是點(x, y)在該三角形的重心坐标下的表示。

由于“系數加起來等于 1”這個條件的限制,是以重心坐标雖說是三個數,但隻要知道兩個數的話,就可以 1-這兩個數的和,求得第三個數了。這樣其實也是有道理的,畢竟我這是個二維平面,本來隻用兩個數就可以表示坐标了,不用非得三個數。
如果這個點在三角形内,還需要滿足另一個條件:這三個系數都是非負的。當這個條件滿足的時候,我們就知道這個點一定在三角形内。如果不滿足這個條件,但滿足三個系數和為 1 的話,那麼表示這個點在三角形所在平面,但有可能在三角形外。
從定義,我們也可以知道△ABC 所在三個頂點的重心坐标:A(1, 0, 0)、B(0, 1, 0)、C(0, 0, 1)。
如何求得三個系數,也就是如何求得重心坐标呢?
方法一:面積比
**三角形内的點将三角形劃分成了三個區域,利用這三個區域的面積比求得對應的三個系數。**頂點 A 對面的三角形區域被稱為 A A A_A AA,頂點 B 對面的三角形區域被稱為 A B A_B AB,頂點 C 對面的三角形區域被稱為 A C A_C AC。如下圖左。
三角形的重心有個特殊的性質,可以将三角形 ABC 分成面積相等的三部分。是以三角形重心的重心坐标是( 1 3 \frac{1}{3} 31, 1 3 \frac{1}{3} 31, 1 3 \frac{1}{3} 31)。
方法二:公式法
已知三角形的三個頂點坐标,就可以用公式求得這三個系數是多少。如下圖右。
我們隻要知道:對于三角形内任意一個點,都可以計算它的重心坐标就可以啦。
3.1.2 求得重心坐标後,如何用它進行插值呢?
求得了重心坐标後,就可以用重心坐标去計算三角形内部任意一個點的額插值。我要插值的屬性同樣也能用重心坐标去把它用線性組合表示出來。 意思是:假如三角形頂點有多個屬性,那麼我就可以通過三角形内部的一個點,它的重心坐标 (α, β, γ),将它的屬性通過這個坐标給線性組合起來,得到該點的屬性值。
總結:先算出重心坐标,然後用重心坐标和三角形頂點的屬性資訊做一次線性組合,得到我們想要的三角形内部點的屬性資訊。
但是請注意❗️在投影下,這個三角形的形狀可能會發生變化,是以也是不能保證重心坐标不變的。總結:經過投影後,三角形的重心坐标有可能會發生改變。
是以,如果我們要插值三維空間中的屬性,就應該取三維空間中的ABC坐标來算它的重心坐标是多少 ,再去做插值。不能在投影之後的坐标裡面做插值。
特别說一下深度。光栅化的時候,三角形都被投影到螢幕上去了,三角形會覆寫很多的像素,像素都有中心。對這些中心點可以知道它在投影了的三角形的哪裡,然後求出重心坐标,對投影了的三角形頂點的深度做插值。這個操作方式得出的結果是不對的。
正确操作姿勢:找到像素中心點對應在三角形的位置它在三維空間中的坐标。然後在三維空間中對三角形頂點的深度做插值。(應用逆變換将已經投影到二維平面上的三角形再投影回去。)
在三維空間中的屬性就在三維空間中做插值,然後再把它對應到二維空間中去。
根本原因就是:重心坐标在投影操作下可能會發生變化。
3.2 如何将紋理應用在實際的渲染中?
每個三角形頂點都會對應一個紋理坐标(u, v),那麼三角形内部的采樣點就可以通過重心坐标插值來求得采樣點自己對應的紋理坐标屬性。然後再根據這個紋理坐标檢視紋理圖上該坐标對應的顔色,最後将這個顔色給塗在相應的采樣點上。
3.2.1 紋理的放大(紋理太小了怎麼辦?)
看着很高清的一個牆,上面貼的圖卻很低清,這種情況怎麼辦呢?
紋理過小導緻的問題描述
對于任何一個采樣點,都可以找到它對應的紋理位置。但是這個位置可能不是整數,不是整數怎麼辦呢?就把它四舍五入為整數。那麼在一定的範圍内,我們要查找的是相同的紋理上的像素。
紋理上的像素,叫做紋理元素,或者紋素texel。
**會出現一種現象:一個像素的周圍很多像素都會被映射到同一個紋素上。這就是因為紋理太小了。**這樣的圖檔會看起來有一個個格子,效果并不是很平滑自然。
這就引出需要解決的一個問題:那當我們查到的紋理坐标是非整數的時候,應該如何處理呢?往下看解決方法:
雙線性插值
首先利用水準的兩個頂點的顔色,進行水準插值得到 u 1 u_1 u1和 u 2 u_2 u2的顔色值;然後利用 u 1 u_1 u1和 u 2 u_2 u2的顔色值進行豎直方向上的插值得到我們想要的紅色點的顔色。
最終紅色點所對應的顔色就綜合考慮了周圍四個點的顔色,可以在四個點所圍成的區域内,取得一個平滑的顔色。
找最近的四個點做雙線性插值就可以得到平滑過渡的結果。
雙向三次的插值
不是取周圍的 4 個紋素,而是取的 16 個。然後也是先做多次水準上的插值和垂直方向上的線性插值。
這種做法的運算量比雙線性插值的要大很多,但是效果也會好很多。
3.2.2 如果紋理太大了怎麼辦?
紋理太大導緻的問題描述
近處出現鋸齒現象,遠處出現摩爾紋。
近處一個像素覆寫的紋理區域其實相對較小;在遠處,一個像素其實覆寫的是一片紋理,是比較大的區域。這告訴我們:螢幕上的像素覆寫的紋理上的區域大小是各不相同的。
如果說對于一個像素,它覆寫的紋理區域挺小,用這個像素的中心計算到的紋理坐标查詢一下它的值。得到這個像素區域所覆寫紋理的值,可以近似的這樣認為沒問題。
但是當一個像素覆寫的是一片很大的紋理區域,它使用點采樣所對應到的顔色值就可以代表這個紋理區域的平均顔色值嗎?當然不可以,一個像素點是不能對應這麼大一塊紋理的,紋理中都有不同的顔色變化啊。是以當一個像素點對應的是比較大的一塊紋理區域的時候就不能使用點采樣。
**本質原因:當這個紋理特别大的時候,一個像素裡面有可能包含特别大一塊紋理。這塊紋理一直是在變化的,也就是說這個像素内部頻率變化很高,可是你隻用了一個采樣點(像素)去采樣它,這樣效果肯定不對。**應該需要一個更高頻的采樣方法。
解決方法一:超采樣
将每個像素分成 512 個采樣點,然後把每個采樣點在紋理上對應的位置都計算出來。效果肯定特别好,但是計算量和花銷也是相當大的。
解決方法二:Mipmap
提供另一個完全不一樣的思路。避免采樣,立刻就可以知道一個像素對應的這個紋理區域中的平均值是多少。
mipmap 允許做快速的、近似的、正方形的範圍查詢。
什麼叫mipmap?
從一張圖生成一系列圖,每一張圖都是上一張圖縮小到一半。是以一共可以生成 l o g 2 N log_2 N log2N層。如下圖,當原始圖是128x128大小的時候,可以生成7層mipmap。
在渲染之前,将這些對應的 mipmap 都生成好存儲起來。是以使用 mipmap 會占用更多的存儲量,但隻是多出了原始圖檔存儲量的三分之一。
用 mipmap 做一個近似的在一個正方形區域内做範圍查詢,要立刻得到這個區域内的平均值。
如何知道要查詢的正方形區域有多大?
**有一個近似正方形區域的方法:通過使用螢幕上的相鄰采樣點的紋理坐标,計算紋理坐标之間的距離,将較大的距離作為正方形的邊長,用這個正方形來近似該采樣點的紋理區域。**具體了解如下:
當一個三角形覆寫了一堆采樣點,可以取采樣點自己的中心和采樣點鄰居的中心,分别投影到紋理上去。
将采樣點上面和右邊的點投影到紋理上,然後分别計算,在紋理空間中,采樣點到采樣點上面和右邊的距離。當然這兩個距離會有所不同,簡單起見,就取較大的距離值來近似正方形的邊長。這個正方形的面積對應的就是像素點投影在紋理上的面積大小。
如何用mipmap來查詢正方形區域的平均值?
重要的是:當我們将像素點覆寫的紋理區域近似成一個正方形之後,如何根據之前預計算好的mipmap,來查詢這個邊長是L的正方形區域的值的平均值是多少?
如果這個區域的大小就是1x1 一個像素,那就可以在沒有做mipmap的最原始的那張紋理上找對應的像素就是它的值。如果這個區域的大小是4x4,那麼第1層就是2x2,這個區域到了第2層上就一定會變成1個像素。查詢第2層的像素,就可以知道4x4所對應的一個像素顔色是多少。
總結:L x L這個大小的區域,到了第 l o g 2 L log_2 L log2L 層就一定會對應到一個像素上去。直接去查這一層的像素,就可以立刻得出L x L這個區域的平均值是多少。
每個像素都會投影到紋理上,計算這個像素對應多大區域,然後确定應該去第幾層去找這個平均值。
用三線形插值(正方形)來平滑過渡層與層之間
下圖做了一個可視化。離螢幕比較近就會看到很多細節,應該在較低層去找,離螢幕比較遠的話,應該去較高層查詢顔色的平均值。但是從圖中可以看出,顔色之間的銜接處比較生硬,我們所期望的應該是一個漸變的顔色,得到平滑的過度,這裡就可以采用插值的方式。
有第1層和第2層,想查詢第1.8層的值。做法:對第1層和第2層的内部,分别做雙線性插值。接着将插值後的兩個結果放在一起,在層與層之間再做一次線性插值。這樣不管是整數還是浮點數層,就可以通過“三線性插值”的方式,隻做查詢一次,得到它覆寫的紋理區域面積顔色的平均值。
三線性插值的應用十分十分廣泛,因為它可以得到一個完全連續的表達。它本身的開銷挺小的,就是做兩次查詢和一次插值。
各向異性過濾 Ripmap(矩形區域)
用mipmap是否能完全解決問題?
并不能。對于遠處會有過度模糊的問題。
各向異性過濾的效果會比三線性插值要好。比mipmap多了不均勻的水準和豎直方向上的壓縮。
螢幕上的像素映射到紋理上,不一定都是一個規律的形狀,很有可能會出現一些不規則的,斜着的形狀。如果還用之前的正方形來近似這樣的不規則形狀就會十分不準确,會造成過度模糊的問題。
引入了各向異性過濾後,對于這種長條狀的圖形,會得到一個幾乎完美的解決。各向異性過濾允許我們對這樣的長條形區域做一個快速的範圍查詢,這樣就不用限制在隻能用正方形來近似紋理區域。
但是各向異性過濾其實隻解決了一部分問題,對于矩形的查詢是可以的,比正方形的mipmap查詢效果要好很多。但是對于斜着的這種區域沒有辦法。
EWA過濾(不規則形狀,橢圓)
假設你有任意的一個不規則的形狀,可以把不規則的形狀給拆成很多不同的圓形去覆寫這個不規則的形狀。
這個橢圓,可以拆成3個不同形狀的圓形去覆寫它。多次查詢,自然就可以找到覆寫這樣的不規則形狀的橢圓。
代價就是多次查詢。品質越好,代價越大。
整體思路:先算出mipmap等級,然後算出橢圓系數,最後根據橢圓系數确定橢圓的包圍盒。對于包圍盒内的紋素進行周遊并作權重平均,得出結果。