2015-02-11 52 views
0

編輯:我發現問題類似於問題here,但我仍然不能提出解決方案,因爲我的問題有點不同。手動包裝紋理座標

我正在使用C++和HLSL,並需要將紋理座標包裹起來,以便紋理平鋪在三角形上。
座標被「包裹」到0-1範圍後,它們將被旋轉,所以我不能簡單地使用紋理採樣器AddressU和AddressV屬性設置爲包裝,因爲它們需要被包裝然後旋轉,所以它可以在採樣器內部不要做。
這裏的解決方案很簡單,只需使用紋理座標的小數部分即可。

這裏是一個像素着色器的一個例子將瓦片紋理36倍(6×6) -

input.tex *= 6.0f; //number of times to tile^2 
input.tex = frac(input.tex); //wraps texCoords to 0-1 range 
return shaderTexture.Sample(SampleType, input.tex); 

這確實瓦的紋理,但可以在邊界其中產生問題紋理包裝。這些瓷磚必須均勻分配到它們正在顯示的空間上,或者它會在邊界相交處形成一個接縫。我的正方形紋理被繪製到800x600像素,所以平鋪5將平均分割,但6不會,並將導致沿Y軸的接縫。
我已經嘗試使用模運算符input.tex = input.tex % 1來包裹座標,但我得到完全相同的結果。我也嘗試改變紋理過濾方法,AddressU和AddressV屬性以及無數種不同的調試方法。

我有一些運氣使用此代碼。如果x座標過高它被設置爲0,如果是太低,它被設置爲1

input.tex *= 6.0f; 
input.tex = frac(input.tex); 
if (input.tex.x > 0.999f) input.tex.x = 0; 
if (input.tex.x < 0.001f) input.tex.x = 1; 
return shaderTexture.Sample(SampleType, input.tex); 

這僅僅修復在特定的地點的問題了,所以它絕對不是一個解決方案。

這張圖片顯示了一個紋理(左)和手動包裝時的樣子(右)。你可以看到寄宿生觸及的並不是每個地方都有這個錯誤。 enter image description here

我也試過沒有改變紋理座標到0-1的範圍,並圍繞每個瓷磚的中心而不是(0.5,0.5)旋轉它們,但是我得到了相同的結果。此外,我的紋理座標完全獨立於頂點,並在像素着色器內進行計算。

我看到的與這個問題有關的任何事情都與一個像素具有高值並且接下來具有較低值有關,例如u = 0.95和下一個像素u = 0.03,這導致它向後插入紋理。但是當我旋轉我的紋理座標時,根本沒有任何變化。即使每個圖塊都應用了隨機旋轉。在這種情況下,邊緣具有彼此接近的各種不同的值,不僅左側高值而右側低值,而且接縫發生的區域不會改變。 enter image description here

回答

2

正如MuertoExcobito所說,主要問題是在邊界紋理座標在1個像素中從1跳到0。在語義上說,整個紋理在這個像素中取平均值是正確的,但它不是由在這個像素中從1到0的紋理內插引起的。真正的原因是mipmapping。

對於您的紋理,在您加載時會生成mipmap。這意味着紋理獲得了多個mipmap級別,這些級別都是前一半的大小。

enter image description here

如果紋理變得扭曲的最高水平的採樣會導致過採樣(pointfiltering狀僞像)。爲了對抗過採樣,紋理查找會根據屏幕空間中紋理座標的變化選擇合適的mipmaplevel。在你的情況下,邊界是一個很小的地方變化很大,這導致使用最低的mipmap可能(這是因爲你看到一個小紅點,這是紅色邊界的原因)。

回到您的問題,您應該通過使用紋理查找方法SampleGradDocs)來控制mipmapping。要獲得像素紋理座標的當前更改,可以使用內在方法ddxddy。他們在着色器中返回一個任意變量,它在本地如何變化到相鄰像素(正確的解釋將深入本主題)。因此,使用下面的代碼應該不會改變任何東西,因爲它應該是相同的語義:

input.tex *= 6.0f; //number of times to tile^2 
input.tex = frac(input.tex); //wraps texCoords to 0-1 range 
float2 xchange = ddx(input.tex); 
float2 ychange = ddy(input.tex); 
return shaderTexture.SampleGrad(SampleType, input.tex, xchange, ychange); 

現在還能用自己的代碼以防止在交換網和ychange迫使圖形設備使用更高的紋理貼圖大的變化。這應該刪除你的工件。

如果你的紋理不需要紋理映射,因爲你是渲染紋理畫面對準和紋理大小不會導致過採樣,可以使用更簡單的替代sampleLevelDocs)在那裏,你可以傳遞參數,這選擇一個特定的mipmap,你可以自己決定。

+0

我一直在想我希望有一種方法來找出像素之間的變化,這工作完美,並保持mipmap水平在一個常數0工作,所以我相信你是正確的,這是一個mipmapping問題。非常感謝! – Frobot 2015-02-17 18:28:38

+0

我添加了一個答案,顯示了我是如何實現這個 – Frobot 2015-02-17 22:43:25

1

此處的代碼導致在單個像素範圍內對整個紋理進行採樣。例如,對於接縫處的兩個相鄰像素,一個「u」樣本可能爲1.0-eps,下一個樣本爲0.0+eps,其中eps爲小於紋理元素寬度的數字。當輸出像素被內插時,您將從1.0.0.0插值,對這兩個樣本之間的整個紋理進行採樣。即使您的輸入紋理實際上不包含任何完全灰色的像素,整個紋理的平均值也會導致「灰度」。

如果您需要旋轉每個範圍內的紋理座標(例如,0..1,1..2獨立旋轉),有幾種方法可以解決這個問題。首先,您可以將插值從線性更改爲點,這將避免紋理元素之間的插值。但是,如果您需要雙線性插值,這可能是不可接受的。在這種情況下,您可以構造一個「網格」網格,並將輸入紋理0..1映射到每個瓦片上,並且瓦片紋理座標在着色器中獨立旋轉。

另一種可能的解決方案是將座標轉換爲0..1空間,執行旋轉,然後將它們轉換回原始空間。例如,你會這樣做:

// pseudo-code: 
int2 whole; 
input.tex = modf(input.tex, whole); 
input.tex = Rotate(input.tex); // do whatever rotation is needed 
input.tex += whole; 

這將確保包裝沒有任何不連續性。或者,您可以讓您的旋轉代碼考慮非統一空間紋理座標。

+0

是每個區塊被獨立地被隨機量旋轉。我真的需要雙線性插值來保持質量。我可以看到如何使用網格網格來解決這個問題,但它不適合我的目的。我的紋理座標獨立於頂點位置,並在PS中計算。我的算法在網格上鋪貼紋理,隨機地旋轉每個拼貼,並在一個方格之間平滑地變形,從而基本上創建一個不明顯的平鋪的無限無縫紋理。除了這個問題之外,它的運作非常好。 – Frobot 2015-02-11 18:50:33

+0

謝謝你的發佈,你理解我的問題完美。我認爲有一種方法可以保持u,v座標在不添加頂點的情況下跳躍,但這可能需要一些沉重的思考。 – Frobot 2015-02-11 18:51:20

+0

增加了另一種可能的解決方案,這將允許您保留bilerp,而不需要創建網格網格。 – MuertoExcobito 2015-02-11 19:03:22

0

Gnietschow發佈了這個問題的答案,但我將添加一個答案,顯示我如何使用答案。 我其實並不確定爲什麼這樣工作,我只知道它甚至可以用其他倍數的平鋪,各種紋理和隨機旋轉。

input.tex *= 6.0f; //number of times to tile^2 
input.tex = frac(input.tex); //wraps texCoords to 0-1 range 
float2 xchange = ddx(input.tex); 
float2 ychange = ddy(input.tex); 
if ((xchange.x < 0)) xchange = float2(0, 0); 
if ((ychange.y < 0)) ychange = float2(0, 0); 
return shaderTexture.SampleGrad(SampleType, input.tex, xchange, ychange); 

如果你不需要mipmapping貼圖可言,這另一種方法Gniet提到的優秀作品也

input.tex *= 6.0f; //number of times to tile^2 
input.tex = frac(input.tex); //wraps texCoords to 0-1 range 
return shaderTexture.SampleLevel(SampleType, input.tex, 0);