在前面的例子中,我們要么是直接給頂點賦顏色值,要么是在頂點屬性中設置Diffuse和Specular系數,從而根據光照參數計算得到物體表面顏色,但這樣得到的顏色真實感要差很多。如果我們直接把一副圖像映射到三角形面上,從而得到物體表面顏色值,效果會好很多,比如下面的兩幅圖,右邊的圖是把一副圖片映射到2個三角形上。
甚至,我們還可以直接使用圖像的顏色值做為頂點(或者pixel)的diffuse值,融合光照計算公式,得到最終的表面顏色值,這樣會有更好的效果。
通常,我們把圖像映射到三角形面上的技術稱作紋理映射(texture mapping)。在紋理映射過程中,我們使用紋理坐標的方式(或稱u、v坐標),把紋理進行划分,如下圖,假設圖像為256*256像素,則左上角紋理坐標為(0,0),右上角為(1,0),左下角為(0,1),右下角為(1,1)。
之所以使用這種歸一化的uv坐標,主要是要使得uv坐標值和紋理圖像大小沒有關系,比如對下面的圖,uv坐標(0.5, 0.5),其實就是映射到紋理上的像素(128,128),如果換成一個更大的紋理圖像(1024*1024),則(0.5,0.5)就映射到(512,512)。
對於一個三角形,我們要為每一個頂點指定uv坐標,如下圖的2個三角形,我們按照圖上標的紋理坐標指定uv坐標,則左邊的2個三角形則會被完整的映射成右邊的紋理形狀。
下面了解幾個和紋理采樣有關的概念:
Magnification與Minification:
如果光柵化后的三角形正好是256*256像素,這和紋理的大小正好相等,那么映射關系就是一個像素對應紋理的一個單元(texel),但實際情況可能更復雜。
比如:紋理大小為256*256,但我們的兩個三角形組成的quad卻是512*512,則一個紋理單元要對應quad上的四個像素,一個紋理單元對應多個光柵化后的像素的情況通常稱作Magnification,
在magnification情況下如何進行紋理映射呢?通常是用雙線性插值的方式,比如下面左邊的圖中紅色的pixel,沒有對應的紋理texel,則它的顏色值用它的上下左右的pixel顏色插值得到,當然我們也可以選擇最接近的紋理單元顏色做為其顏色,具體要視紋理濾波中設置的MAG值。
和magnification的情況相反,minification表示三角形面小,而紋理大的情況,就是一個pixel如何在多個紋理單元中選擇顏色的問題。這時可以選擇最接近的紋理單元,也可以把上下左右紋理單元進行雙線性插值,再和pixel映射起來。比如下圖128*128的quad面和256*256紋理映射的情況:
mipmap層:
通常,我們創建紋理的時候,可以選擇創建mipmp層,這時,系統就自動會為我們創建一系列下采樣的圖像,每個圖像都是前面一個圖像的1/4(如下圖所示),我們可以自己選擇mipmap的層數。如果使用dds文件的話,其中已經包含了mipmap層,所以最好把自己需要的紋理轉化成dds格式,預處理得到需要mipmap層數。
在使用mipmap層的情況下,如何做紋理映射呢?
比如紋理原始大小是256*256,我們的三角形是178*178,那么這時會做三線性插值,四邊形先和128*128紋理執行magnificaiton插值得到一個結果,再和256*256紋理做minification插值得到一個結果,最后再對這2個結果進行線性插值,得到的顏色為最終的pixel顏色。
紋理的尋址模式:
通常我們頂點的紋理坐標在[0,1]范圍內,這樣光柵化后的三角形pixel總能從紋理中找到對應的紋理單元。
我們也可以通過設置紋理尋址模式,來擴展紋理映射,對於不在[0,1]之間的紋理坐標,也可以找到對應的紋理單元。
通常的紋理尋址模式包括:
wrap方式:
對於不在[0,1]之間的紋理坐標,采用纏繞的方式來對應紋理單元,比如下面圖所示,這樣的方式特別適合用小紋理來貼一個大的平面,比如在地面鋪瓷磚,在地面上鋪草地等等。這也要求我們設計紋理圖片時,把左右、上下的邊緣部分,最好能無縫的連接起來。
border方式:
把不在[0,1]范圍的紋理坐標設置為一些指定的顏色,比如下圖,指定為紅色
Clamp方式:
把不在[0,1]范圍的紋理坐標指定為離其最近的紋理單元顏色。
Mirror方式:
就是把不在[0,1]范圍的紋理坐標按鏡像的方式指定紋理單元,如下圖所示:
現在我們了解一些紋理采樣的設置,在D3D11中使用紋理前我們必須設定采樣狀態,主要就是通過下面的的函數:
// 創建紋理采樣狀態描述符.
samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
samplerDesc.BorderColor[0] = 0;
samplerDesc.BorderColor[1] = 0;
samplerDesc.BorderColor[2] = 0;
samplerDesc.BorderColor[3] = 0;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
// 創建紋理狀態.
result = device->CreateSamplerState(&samplerDesc, &m_sampleState);
if(FAILED(result))
{
return false;
}
本篇教程中,我們了解D3D11紋理使用中的一些基本概念,下一篇日志,要開始寫代碼了…