纹理映射技术
第六集 紋理映射技術(shù)
?
為使建立的3D模型更接近現(xiàn)實(shí)世界中的物體, 簡單的顏色變換已經(jīng)無能為力, 這時我們就需要紋理映射技術(shù)了.
?
??? 這一集我們講解基礎(chǔ)的紋理映射技術(shù)的數(shù)學(xué)模型, 對于在粒子系統(tǒng)使用的過程紋理技術(shù)在高級部分講解.
?
6.1 二維紋理映射
?
6.1.1 紋理映射的簡單建模
?
??? 二維紋理映射就是從二維紋理平面到三維物體表面的映射. 一般二維紋理平面是有范圍限制的, 在這個平面區(qū)域內(nèi), 每點(diǎn)都可用數(shù)學(xué)函數(shù)表達(dá), 從而可以離散的分離出每點(diǎn)的灰度值和顏色值, 這個平面區(qū)域稱為紋理空間, 一般將紋理空間的平面區(qū)域定義在[0, 1] * [0, 1].
?
??? 紋理映射是確定物體表面一點(diǎn)P在紋理空間中的對應(yīng)點(diǎn)(u, v), 從而紋理空間中的點(diǎn)(u, v)處的紋理值就是物體表面點(diǎn)P的紋理屬性. 這樣屏幕上顯示的像素的顏色可通過下面的映射得到,
F : Screen Space --> Object Space
?G : Object Space --> Texture Space
其中F我們在第二集中已詳細(xì)講解過, 所以我們關(guān)心的是G. 只要確定了物體表面的紋理屬性, 接著就是將物體表面上各點(diǎn)所對應(yīng)的紋理值作為光照明模型中的相應(yīng)參數(shù)進(jìn)行光強(qiáng)度計算, 再繪制畫面.
?
??? 所以G函數(shù)的定義直接影響畫面的效果, 如何確定G非常重要. G可表示為,
(u, v) = H(x, y, z)
?
其中(u, v)和(x, y, z)分別是紋理空間和物體空間中的點(diǎn). 現(xiàn)在從簡單的圓柱開始, 一個高h(yuǎn), 半徑為r的圓柱的紋理映射方法, 我們用參數(shù)形式表示圓柱,
?
x = r * cos a
y = r * sin a
z = h b
0 <= a <= 2PI, 0<= b <= 1
?
從紋理空間 [0, 1] * [0, 1] 到 [0, 2PI] * [0, 1] 的線性變換為,
u = a / 2PI
v = b
?
這樣, 我們建立了物體空間到紋理空間映射的表達(dá)式. 如圖6.1
圖6.1
?
??? 物體表面沒有象圓柱一樣, 所以上面的方法不實(shí)用.
?
6.1.2 兩步法紋理映射
?
??? 1986年, Bier和Sloan提出了一種獨(dú)立于物體表面的紋理影射技術(shù), 將紋理空間到物體空間的映射分為兩個簡單的映射復(fù)合. 兩步法紋理映射的核心是引進(jìn)一個包圍物體的中介三維曲面作為中間映射媒體, 主要過程分兩步,
?
(a). 將二維紋理映射到一個簡單的三維物體表面, 如平面, 球面,圓柱面, 立方體表面, 采用不同的中間映射媒體生成的紋理效果是不同的, 要根據(jù)目標(biāo)物體表面來選擇.
?
???? S : (u, v) --> (x', y', z')?? ---??? S-映射
?
???? 如半徑為R的球的S-映射為,
?
x = R * cos a * sin b
y = R * sin a * sin b
z = R * cos b
(0 <= a <= 2PI,?0<= b <= PI)
?
(b). 將(a)的三維物體表面上的紋理映射到目標(biāo)物體表面,
?
????O : (x', y', z') --> (x, y, z)??? ---???O-映射
?
???? O-映射一般有4種, 如圖6.2,
圖6.2
????(1). 取視線在物體表面可見點(diǎn)(x, y, z)處的反射與
????????? 中間映射媒體表面上的交點(diǎn)(x', y', z')作為
????????? (x, y, z)的映射點(diǎn).
?
????(2). 取物體表面可見點(diǎn)(x, y, z)處的法線與中間映射
?????? ?? 媒體表面上的交點(diǎn)作為映射點(diǎn).
?
????(3). 取物體中心到可見點(diǎn)(x, y, z)的射線與中間映射
????????? 媒體表面上的交點(diǎn)作為映射點(diǎn).
?
????(4). 中間映射媒體表面上的點(diǎn)(x', y', z')處的法線
????????? 與物體表面的交點(diǎn)為(x, y, z), (x', y', z')
????????? 為(x, y, z)的映射點(diǎn).
?
???其中(1)映射方式應(yīng)用的最廣, 稱為環(huán)境映射, 我們下一節(jié)會簡單的說明一下. 其余的三種O-映射和上面4種S-映射有12種組成, 其中可用的為5種, 如下表.
?
?
三種O-映射和4種S-映射有12種組成
_____________________________________________________________
|????/???????? |?????????|????????? |????????? |?????????|
|????? / S-映射 |?? 平面?? |? 圓柱面? |? 立方體? |??球面?? |
|O-映射/?????? |?????????|????????? |????????? |?????????|
|---------------|----------|----------|----------|----------|
| ?表面法向(2)?|?? 冗余?? |? 不適合? | 不太適合 | 不太適合 |
|---------------|----------|----------|----------|----------|
|? 物體中心(3)? |?? 冗余?? |? 不適合? |?? 適合??|?? 適合?? |
|---------------|----------|----------|----------|----------|
| 中介面法向(4) |??適合?? |?? 適合?? |?? 適合?? |?? 冗余??|
+---------------+----------+----------+----------+----------+
?
6.1.3 環(huán)境映射
?
???環(huán)境映射是兩步法紋理映射的特例, 環(huán)境映射是近似的模擬光線跟蹤, 但沒有光線跟蹤那么復(fù)雜, 所以能大大提高光線跟蹤算法的效率, 目前在商業(yè)應(yīng)用中廣泛使用的紋理影射技術(shù). 環(huán)境映射的過程如圖6.3,
圖6.3
(1). 物體被中介曲面球包圍, 中介曲面球記錄了物體表面的紋理值.
(2). 視點(diǎn)的光線經(jīng)過一像素的四個角點(diǎn)投射到物體表面, 經(jīng)物體表面的反射到達(dá)中介曲面球, 圖6.3中P的紋理屬性就由中介曲面球上的E點(diǎn)唯一確定.
(3). 中介曲面球的缺點(diǎn)是在球的兩極點(diǎn)紋理變化太快, 形成紋理的突變.
?
???為避免中介曲面球的極點(diǎn)紋理突變,環(huán)境映射中可采用立方體表面為中介表面, 整個環(huán)境紋理映射由六幅圖(mip-map表)組成, 六幅圖象是六個90度視域方向拍攝的或繪制的真實(shí)景物圖象, 其映射過程簡單的將入射光線束通過物體表面片投射到立方體上, 入射光線束形成的錐和立方體表面的交集就是該物體表面片的紋理, 如圖6.4.
圖6.4
???立方體環(huán)境映射計算量比球面環(huán)境映射大很多, 主要因?yàn)楣饩€和立方體表面的交集區(qū)域計算復(fù)雜. 為次可以以立方體中心為投影中心, 將立方體表面包含的紋理屬性投影到立方體的外接球面上, 再由球面環(huán)境映射來確定物體表面紋理, 這樣既省計算時間, 又可避免球面環(huán)境映射在極點(diǎn)的紋理突變.
?
6.2 特殊紋理映射
?
???這節(jié)的例子以后給出.
?
6.2.1 凹凸紋理映射
?
???兩步法紋理映射方法只能實(shí)現(xiàn)物體表面的顏色(花紋)紋理, 但不能實(shí)現(xiàn)物體表面幾何形狀的凹凸不平而形成的粗糙質(zhì)感. 為此, 出現(xiàn)了無須修改物體模型就能實(shí)現(xiàn)物體表面凹凸不平效果的紋理技術(shù) -- 凹凸紋理映射(Bump mapping).
?
???由于物體表面的光強(qiáng)度是由物體表面的法向量決定的, 凹凸紋理映射通過對物體表面各采樣點(diǎn)的法向量作微小的調(diào)整來改變物體表面的光強(qiáng)度, 達(dá)到凹凸不平效果.
?
???但不是任何調(diào)整都能產(chǎn)生凹凸不平的真實(shí)感效果, 我們需要建立數(shù)學(xué)模型, 定義物體表面參數(shù)方程, 在(m, n)點(diǎn)的法向量為,
?
V =V(m, n)
?
設(shè)Vm,Vn分別是V在m, n上的分量, 那么在(m, n)點(diǎn)的單位法向?yàn)?
?
N =N(m, n) = Vm X Vn / | Vm XVn |
?
現(xiàn)在用一個微小調(diào)整的函數(shù)P(m, n),?一般這個函數(shù)是連續(xù)可微的, 那么通過函數(shù)P(m, n)調(diào)整的點(diǎn)(m, n)的新法向量為,
?
???????????????????????????????????| V'm? = Vm + PmN
V'(m, n) =V(m, n) + P(m, n) N?? = |
???????????????????????????????????| V'n? = Vn + PnN
?
從而得到新的單位法向, 如圖6.5
N' =V'm X V'n = N + D
D= PmA - Pn B
A= N XVn,?? B = N XVm
?
圖6.5
???對于函數(shù)P(m, n), 可以用一張凹凸圖來離散的給出各點(diǎn)的值.
?
6.2.2 位移紋理映射(Displacement mapping)
?
???凹凸紋理映射能很好的模擬面的凹凸不平的真實(shí)感效果, 但在物體的輪廓線上的凹凸不平效果無法表達(dá), 如圖6.6
圖6.6
我們將含有凹凸信息的面按一定方向旋轉(zhuǎn)后, 原來的凹凸不平的真實(shí)感效果就不明顯了.為此出現(xiàn)了位移紋理映射技術(shù), 它和凹凸紋理映射技術(shù)相似, 只是位移紋理映射是沿法向方向調(diào)整的是采樣點(diǎn)的位置, 將凹凸信息的顯示用點(diǎn)坐標(biāo)的調(diào)整來得到, 如圖6.7
圖6.7
當(dāng)含有凹凸信息的面轉(zhuǎn)過90度后, 我們還是能看出面的凹凸信息.
?
???值得注意的是, 位移紋理映射對物體表面的造型進(jìn)行了改變, 所以呈現(xiàn)的凹凸信息能實(shí)現(xiàn)陰影效果.
?
6.3 紋理映射中的反走樣
?
???紋理映射常常需要將紋理圖案映射到不同大小的物體表面, 理想的是物體表面大小和紋理圖案大小一致. 但實(shí)際物體表面大于或小于紋理圖案的情況多, 這時必須取這區(qū)域的紋理屬性值--如像素顏色的平均值作為對應(yīng)表面的紋理屬性, 但當(dāng)物體表面形狀復(fù)雜時, 計算平均值并不能精確得到物體表面的紋理屬性.
?
???另外將一張黑白的世界象棋圖映射到物體表面, 當(dāng)物體表面小到接近單位像素時, 表面只能顯示黑色或白色, 當(dāng)物體位移或旋轉(zhuǎn)時, 會出現(xiàn)黑白閃爍現(xiàn)象.
?
???上面都是紋理映射的走樣問題, 相應(yīng)的就有反走樣技術(shù), 我們講解mip-map技術(shù).
?
6.3.1 mip-map
?
???mip是拉丁文multum in parvo(聚集在一塊小區(qū)域內(nèi)的許多東西)的縮寫, mip-map技術(shù)根據(jù)初始的紋理圖案每邊的分辨率S, 形成一個四棱錐型的mip-map, 如圖6.8
圖6.8
圖左邊是四棱錐型的mip-map, 其中紋理圖案的層數(shù)是[log2S] + 1, 也就是設(shè)置一張64X64的紋理圖案, mip-map就以64X64為底的四棱錐, 一共有[log264] + 1 = 7層, 最上層為一個像素, 同時我們也知道為何紋理圖案都建議是2的冪次的原因.
?
???mip-map的存儲是以一張查找表的形式存儲的, 如圖6.8的右邊, 一張64X64的紋理圖案的查找表的大小是128X128. 存儲時先提取紋理圖案中每像素的R, G, B值, 然后對R, G, B值逐級壓縮來得到.
?
???mip-map在確定屏幕上可見表面的紋理過程如下,
(1). 計算屏幕上可見表面的中心在紋理空間上的映射點(diǎn)坐標(biāo)(u, v).
(2). 確定紋理空間中以(u, v)為中心, 邊長為d的正方形, 要求正方形能覆蓋表面在紋理空間中映射的區(qū)域.(實(shí)際這樣算d太復(fù)雜, 一般d為表面在紋理空間中映射的區(qū)域的最大邊長)
(3). 根據(jù)d的大小確定使用哪一級的紋理map.
因?yàn)閙ip-map中的紋理圖案存儲的是特定的圖案, 即只有邊長d = 2^k, k = 0, 1, ..., [log2S]的圖案, 對于在2^k < d < 2^(k + 1)的邊長d, mip-map通過線性插值第k層的紋理和第k + 1層的紋理得到.
?
6.4 紋理映射的例子
?
6.4.1 代碼更新
?
???這集的例子是game4 project.
?
我們在這例子里改變了頂點(diǎn)的屬性, 加了頂點(diǎn)的紋理屬性, 紋理屬性是確定頂點(diǎn)在紋理空間中的映射點(diǎn)的坐標(biāo)的.
?
我們在確定頂點(diǎn)在世界中的坐標(biāo)的同時, 還要確定頂點(diǎn)對應(yīng)的紋理空間中的坐標(biāo). 一般紋理空間中的坐標(biāo)都在[0, 1]之間的, 例子中可以該成[0, 2]或[0, 4]之間, 看看物體的紋理又會如何.
?
---------------------------------------------------------------
// gamedef.h中改了頂點(diǎn)的屬性
#define D3DFVF_MYVERTEXTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
?
struct MYVERTEXTEX
{
?? FLOAT x, y, z;
?? DWORD colour;
?? FLOAT tu, tv;
};
?
// d9texobject.cpp中,加入load texture file的函數(shù)
HRESULT CD9TexObject::SetTexture(LPCTSTR pName)
{
?? if (m_bInit)
?? {
??????? return D3DXCreateTextureFromFile(m_pD3DDev, pName, &m_pD3DTexture);
?? }
?? return E_FAIL;
}
// d9texobject.cpp中,確定頂點(diǎn)在世界中的坐標(biāo)的同時,
// 確定頂點(diǎn)在紋理空間中映射點(diǎn)的坐標(biāo),
// 我們只是簡單將紋理圖案映射在四邊形的表面上
HRESULT CD9TexObject::UpdateD3DVertex()
{
??? LPVOID pV = NULL;
??? UINT nSize = 18 * sizeof(MYVERTEXTEX);
??? MYVERTEXTEX aVertex[] =
??? {
??????? {m_fx - m_fW, m_fy + m_fH, m_fz - m_fD, D3DCOLOR_XRGB(0, 0, 255), 0.0f, 1.0f },
??????? {m_fx - m_fW, m_fy + m_fH, m_fz + m_fD, D3DCOLOR_XRGB(255, 0, 0), 0.0f, 0.0f },
??????? {m_fx + m_fW, m_fy + m_fH, m_fz - m_fD, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 1.0f },
??????? {m_fx + m_fW, m_fy + m_fH, m_fz + m_fD, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0.0f },
??????????????????????????????????????????????????????????????????????????????????
??????? {m_fx - m_fW, m_fy - m_fH, m_fz - m_fD, D3DCOLOR_XRGB(255, 0, 0), 0.0f, 1.0f },
??????? {m_fx - m_fW, m_fy + m_fH, m_fz - m_fD, D3DCOLOR_XRGB(0, 0, 255), 0.0f, 0.0f },
??? ????{m_fx + m_fW, m_fy - m_fH, m_fz - m_fD, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 1.0f },
??????? {m_fx + m_fW, m_fy + m_fH, m_fz - m_fD, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0.0f },
??????????????????????????????????????????????????????????????????????????????????
??????? {m_fx + m_fW, m_fy - m_fH, m_fz + m_fD, D3DCOLOR_XRGB(0, 0, 255), 0.0f, 1.0f },
??????? {m_fx + m_fW, m_fy + m_fH, m_fz + m_fD, D3DCOLOR_XRGB(0, 255, 0), 0.0f, 0.0f },
??????????????????????????????????????????????????????????????????????????????????
??????? {m_fx - m_fW, m_fy - m_fH, m_fz + m_fD, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 1.0f },?
??????? {m_fx - m_fW, m_fy + m_fH, m_fz + m_fD, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 0.0f },
??????????????????????????????????????????????????????????????????????????????????
??????? {m_fx - m_fW, m_fy - m_fH, m_fz - m_fD, D3DCOLOR_XRGB(255, 0, 0), 0.0f, 1.0f },
??????? {m_fx - m_fW, m_fy + m_fH, m_fz - m_fD, D3DCOLOR_XRGB(0, 0, 255), 0.0f, 0.0f },
??????? ??????????????????????????????????????????????????????????????????????????
??????? {m_fx + m_fW, m_fy - m_fH, m_fz - m_fD, D3DCOLOR_XRGB(0, 255, 0), 0.0f, 1.0f },?
??????? {m_fx + m_fW, m_fy - m_fH, m_fz + m_fD, D3DCOLOR_XRGB(0, 0, 255), 0.0f, 0.0f },
??????? {m_fx - m_fW, m_fy - m_fH, m_fz - m_fD, D3DCOLOR_XRGB(255, 0, 0), 1.0f, 1.0f },
??????? {m_fx - m_fW, m_fy - m_fH, m_fz + m_fD, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0.0f }
??
??? };
??? if(FAILED(m_pD3DVBuffer->Lock(0, nSize, &pV, 0)))
??? {
??????? return E_FAIL;
??? }
?? MoveMemory(pV, aVertex, nSize);
??? m_pD3DVBuffer->Unlock();
?? return S_OK;
}
?
// d9texobject.cpp中,渲染時設(shè)置物體紋理
VOID CD9TexObject::Render()
{
?? if (m_bInit)
?? {
??????? m_pD3DDev->SetStreamSource(0, m_pD3DVBuffer, 0, sizeof(MYVERTEXTEX));
??????? m_pD3DDev->SetFVF(D3DFVF_MYVERTEXTEX);
??????? if (m_pD3DTexture != NULL)
??????? {
?
????????????m_pD3DDev->SetTexture(0, m_pD3DTexture);
????????????m_pD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
??????? }
??????? else
??????? {
????????????m_pD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
??????? }
??????? m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0,? 2);
??????? m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 4,? 8);
??????? m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 14, 2);
?? }
}
?
6.4.2 說明
?
6.4.2.1 函數(shù)
?
??? 一般紋理文件是單獨(dú)的存放的, 這是要創(chuàng)建紋理用到了
HRESULT D3DXCreateTextureFromFile(LPDIRECT3DDEVICE9 pDevice,
?????????????????????????????????LPCTSTR pSrcFile,
?????????????????????????????????LPDIRECT3DTEXTURE9 * ppTexture);
函數(shù)從單獨(dú)的紋理文件中創(chuàng)建紋理, 用這函數(shù)的好處是隨時可改紋理文件但不用編譯代碼. 如果為提高速度, 可以將紋理加入可執(zhí)行文件中作為資源, 這是使用
HRESULT D3DXCreateTextureFromResource(LPDIRECT3DDEVICE9 pDevice,
?????????????????????????????????????HMODULE hSrcModule,
?????????????????????????????????????LPCTSTR pSrcResource,
?????????????????????????????????????LPDIRECT3DTEXTURE9 * ppTexture);
?
??? 在渲染時, 要告訴IDirect3DDevice9物體的紋理, 使用
HRESULT SetTexture(DWORD Sampler,
??????????????????IDirect3DBaseTexture9 * pTexture);
現(xiàn)在的DirectX Graphics最多能使用8各紋理的多重紋理, 現(xiàn)在我們直用一個, 所以Sampler設(shè)為0.
6.4.2.2 紋理過濾器
?
??? 紋理到物體表面的映射需放大或縮小, 過濾器的作用就是確定紋理的縮放變化的平滑等級的. 紋理過濾器是使用IDirect3DDevice9中的SetSamplerState函數(shù)來設(shè)置的.
?
???我們將平滑等級按一般到高級列出, 同時也要注意, 平滑等級越高, 計算越耗時.
(1). 使用最近點(diǎn)采樣技術(shù)(這是系統(tǒng)默認(rèn)值)
SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
?
(2). 使用線性過濾技術(shù)(建議使用, 以點(diǎn)的2X2區(qū)域線性插值)
SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
?
(3). 使用各向異性過濾技術(shù)
SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
這時需要設(shè)置D3DSAMP_MAXANISOTROPY的等級,默認(rèn)為1
SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 4);
?
還有D3DTEXF_PYRAMIDALQUAD和D3DTEXF_GAUSSIANQUAD過濾技術(shù).
?
6.4.2.3 mip-map設(shè)置
?
???mip-map的過濾器平滑等級和上面的相同, 也是通過SetSamplerState函數(shù)來設(shè)置. 系統(tǒng)默認(rèn)是D3DTEXF_NONE, 即沒有過濾. 同時需要設(shè)置mip-map的層數(shù)
SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, 4);
SetSamplerState(0, D3DSAMP_MAXMIPLEVEL, 4);
?
6.4.2.4 texture-address mode
?
紋理空間總是在[0, 1]之間的, 但是物體表面點(diǎn)映射的紋理坐標(biāo)可大于1, 這時, 對于大于1的紋理映射可使用紋理重復(fù), 鏡像, 最近點(diǎn)采樣, 邊界點(diǎn)采樣, 單次鏡像等. 通過函數(shù)SetSamplerState設(shè)置
SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
?
可以修改例子中物體表面點(diǎn)映射的紋理坐標(biāo)可大于1, 然后改變texture-address mode觀察物體表面紋理映射的區(qū)別.
下面的圖是使用[0, 2]的鏡像紋理技術(shù)的截圖
?
第六集 小結(jié)
?
這一集我們學(xué)習(xí)了要進(jìn)行DirectX Graphics 3D編程中的二維紋理映射技術(shù), 紋理空間總是在[0, 1]之間.
總結(jié)
- 上一篇: CRC校验详解
- 下一篇: C/C++ struct 区别