创建mip纹理链
(1)
我們要做的是,根據(jù)原始紋理T0創(chuàng)建一系列的紋理(通常使用平均濾波):T1、T2…Tn,其中每個(gè)紋理的大小都是前一個(gè)紋理的1/4,即長度和寬度減半,如圖12.40所示。
要根據(jù)前一個(gè)mip紋理計(jì)算當(dāng)前紋理中紋素的值,可以使用平均濾波器,即在RGB空間中,計(jì)算紋素(x,y)、(x+1,y)、(x+1,y+1)和(x,y+1)的平均值,然后將結(jié)果寫入到當(dāng)前紋理中,如圖12.41所示。
| (點(diǎn)擊查看大圖)圖12.40? 根據(jù)原始紋理創(chuàng)建Mip紋理鏈 |
| (點(diǎn)擊查看大圖)圖12.41? 用于創(chuàng)建mip紋理的平均濾波器 |
創(chuàng)建Mip紋理鏈時(shí)需要遵守一些規(guī)則。首先,所有紋理都必須是方形的,且邊長為2的冪。這樣,可以在兩個(gè)軸上按相同的比例縮小,Mip紋理鏈末尾的最后一個(gè)紋理總是1×1的。另一個(gè)約定是,原始紋理為mip等級(jí)0,然后依次為mip等級(jí)1、2、3、4…n。例如,如果原始紋理的大小為256×256,則mip鏈中各個(gè)紋理的大小如表12.4所示。
表12.4?原始紋理為256×256時(shí),各個(gè)Mip紋理的大小
| Mip紋理 | 紋理大小 | Mip紋理 | 紋理大小 |
| T0 | 256×256 | T5 | 8×8 |
| T1 | 128×128 | T6 | 4×4 |
| T2 | 64×64 | T7 | 2×2 |
| T3 | 32×32 | T8 | 1×1 |
| T4 | 16×16 | ? | ? |
除原始紋理外,mip等級(jí)數(shù)為log2T0的邊長。
在這個(gè)例子中,為log2256 = 8。要計(jì)算總紋理數(shù),只需將上述結(jié)果加1。因此計(jì)算總紋理數(shù)的公式為:log2T0的邊長+1。在這個(gè)例子中,總紋理數(shù)為9。
(2)
這里紋理要占用多少內(nèi)存呢?圖12.42是實(shí)際的mip紋理,其中每個(gè)紋理的大小都是前一個(gè)紋理的1/4。由于每次都將紋理縮小到原來的1/4,因此占用的內(nèi)存很少。下面計(jì)算在前一個(gè)例子中,紋理需要占用多少內(nèi)存。
| 圖12.42? 實(shí)際的mip紋理 |
初始紋理的大小為256×256,需要占用2562個(gè)字。Mip紋理鏈需要的內(nèi)存量如下:
要計(jì)算x為原始紋理所需內(nèi)存量的多少倍,將其除以2562:
?
?
換句話說,加上mip紋理時(shí),每個(gè)紋理需要的內(nèi)存量只增加33%,代價(jià)很小,收益卻很高。
知道如何創(chuàng)建mip紋理以及它們占用的內(nèi)存量后,需要拿出一種將其加入到引擎中的方法。作者苦思悶想,考慮過再次修改物體/多邊形數(shù)據(jù)結(jié)構(gòu),最后終于柳暗花明:只需將紋理指針指向mip紋理鏈而不是單個(gè)紋理即可。然而,在每個(gè)多邊形中設(shè)置一個(gè)Mipmapping標(biāo)記,并在渲染多邊形時(shí),將紋理指針視為執(zhí)行位圖數(shù)組的指針,而不是單個(gè)位圖的指針。這樣,便可以使用原來的數(shù)據(jù)結(jié)構(gòu)。請看圖12.43,以理解這里討論的方法。
| ?? |
| 圖12.43? 支持mip紋理鏈的紋理指針數(shù)組 |
?
需要對紋理進(jìn)行Mipmapping時(shí),原來的texture指針指向0級(jí)別mip紋理。據(jù)此分兩步創(chuàng)建mip紋理鏈:首先,分配一個(gè)位圖指針數(shù)組,并將原來的texture指針指向該數(shù)組;然后為每個(gè)mip紋理分配內(nèi)存、生成mip紋理,并將位圖指針數(shù)組中的每個(gè)元素指向相應(yīng)mip紋理的位圖。看起來令人迷惑,其實(shí)并非如此。
這種方案存在的唯一問題是,我們強(qiáng)行將物體/多邊形的位圖圖像指針指向了一個(gè)位圖指針數(shù)組。因此,必須非常小心,避免不知道紋理已被mipmap化的函數(shù)將0級(jí)紋理視為普通紋理;而知道紋理已被mipmap化的函數(shù)必須從mip紋理鏈中選擇正確的紋理。
(3)
int Generate_Mipmaps(BITMAP_IMAGE_PTR source, // 原始紋理 BITMAP_IMAGE_PTR *mipmaps, // 指向mip紋理數(shù)組的指針 float gamma) // gamma修正因子 { // 這個(gè)函數(shù)創(chuàng)建一個(gè)mip紋理鏈 // 調(diào)用該函數(shù)時(shí),mipmap指向原始紋理 // 該函數(shù)退出時(shí),mipmap指向一個(gè)指針數(shù)組,其中包含指向各個(gè)mip級(jí)紋理的指針 // 另外,該函數(shù)返回mip等級(jí)數(shù),如果發(fā)生錯(cuò)誤,則返回-1 // 最后一個(gè)參數(shù)gamma用于提高mip紋理的亮度,因?yàn)槠骄鶠V波器會(huì)降低亮度 // 1.01通常是不錯(cuò)的選擇 // 該參數(shù)大于1.0時(shí),將提高亮度;小于1.0時(shí)將降低亮度;為1.0時(shí)沒有影響 BITMAP_IMAGE_PTR *tmipmaps; // 局部變量,指向指針數(shù)組的指針 // 第一步:計(jì)算mip等級(jí)數(shù) int num_mip_levels = logbase2ofx[source->width] + 1; // 為指針數(shù)組分配內(nèi)存 tmipmaps = (BITMAP_IMAGE_PTR *)malloc(num_mip_levels * sizeof(BITMAP_IMAGE_PTR) ); // 將元素0指向原始紋理 tmipmaps[0] = source; // 設(shè)置寬度和高度(它們相同) int mip_width = source->width; int mip_height = source->height; // 使用平均濾波器生成各個(gè)mip紋理 for (int mip_level = 1; mip_level < num_mip_levels; mip_level++) { // 計(jì)算下一個(gè)mip紋理的大小 mip_widthmip_width = mip_width / 2; mip_heightmip_height = mip_height / 2; // 為位圖對象分配內(nèi)存 tmipmaps[mip_level] = (BITMAP_IMAGE_PTR)malloc(sizeof(BITMAP_IMAGE) ); // 創(chuàng)建用于存儲(chǔ)mip紋理的位圖 Create_Bitmap(tmipmaps[mip_level],0,0, mip_width, mip_height, 16); // 讓位圖可用于渲染 SET_BIT(tmipmaps[mip_level]->attr, BITMAP_ATTR_LOADED); // 遍歷前一個(gè)mip紋理,使用平均濾波器創(chuàng)建當(dāng)前mip紋理 for (int x = 0; x < tmipmaps[mip_level]->width; x++) { for (int y = 0; y < tmipmaps[mip_level]->height; y++) { // 需要計(jì)算4個(gè)紋素的平均值,這些紋素在前一個(gè)mip紋理中的位置如下: // (x*2, y*2)、(x*2+1, y*2)、(x*2,y*2+1)、(x*2+1,y*2+1) // 然后將計(jì)算結(jié)果寫入到當(dāng)前mip紋理的(x, y)處 float r0, g0, b0, // 4個(gè)樣本紋素的R、G、B分量 r1, g1, b1, r2, g2, b2, r3, g3, b3; int r_avg, g_avg, b_avg; // 用于存儲(chǔ)平均值 USHORT *src_buffer = (USHORT *)tmipmaps[mip_level-1]->buffer, *dest_buffer = (USHORT *)tmipmaps[mip_level]->buffer; // 提取每個(gè)紋素的R、G、B值 _RGB565FROM16BIT( src_buffer[(x*2+0) + (y*2+0)*mip_width*2] , &r0, &g0, &b0); _RGB565FROM16BIT( src_buffer[(x*2+1) + (y*2+0)*mip_width*2] , &r1, &g1, &b1); _RGB565FROM16BIT( src_buffer[(x*2+0) + (y*2+1)*mip_width*2] , &r2, &g2, &b2); _RGB565FROM16BIT( src_buffer[(x*2+1) + (y*2+1)*mip_width*2] , &r3, &g3, &b3); // 計(jì)算平均值,并考慮gamma參數(shù) r_avg = (int)(0.5f + gamma*(r0+r1+r2+r3)/4); g_avg = (int)(0.5f + gamma*(g0+g1+g2+g3)/4); b_avg = (int)(0.5f + gamma*(b0+b1+b2+b3)/4); // 根據(jù)5.6.5格式,對R、G、B值進(jìn)行截取 if (r_avg > 31) r_avg = 31; if (g_avg > 63) g_avg = 63; if (b_avg > 31) b_avg = 31; // 寫入數(shù)據(jù) dest_buffer[x + y*mip_width] = _RGB16BIT565(r_avg,g_avg,b_avg); } // end for y } // end for x } // end for mip_level // 讓mipmaps指向指針數(shù)組 *mipmaps = (BITMAP_IMAGE_PTR)tmipmaps; // 成功返回 return(num_mip_levels); } // end Generate_Mipmaps(4)
該函數(shù)接受三個(gè)參數(shù),它們有些棘手。第一個(gè)參數(shù)是位圖指針source,可以是指向任何有效BITMAP_IMAGE對象的指針,切記它是一個(gè)指針。第二個(gè)參數(shù)要復(fù)雜些:
這是一個(gè)指向BITMAP_IMAGE指針的指針。假設(shè)物體(或多邊形)有一個(gè)texture指針,它指向一個(gè)BITMAP_IMAGE。現(xiàn)在的問題是,當(dāng)mip紋理函數(shù)生成所有的mip紋理時(shí),我們要將該指針指向mip紋理鏈本身,為此,需要知道該指針的地址,以便能夠修改它,因此需要一個(gè)** BITMAP_IMAGE。這就是需要一個(gè)指向指針的指針作為參數(shù)的原因。我們將讓該參數(shù)指向mip紋理指針數(shù)組。
這個(gè)函數(shù)提供了這樣的靈活性:可以將同一個(gè)對象作為參數(shù)source和mipmap。對于source參數(shù),將其設(shè)置為指向該對象的指針;對于參數(shù)mipmaps,需要將其設(shè)置為指向該對象的指針的地址,這樣函數(shù)才能對其進(jìn)行修改。假設(shè)您這樣調(diào)用該函數(shù):
其中obj的類型為OBJECT4DV2_PTR,它有一個(gè)texture字段,該字段是一個(gè)指向BITMAP_IMAGE的指針(BITMAP_IMAGE_PTR)。仔細(xì)查看上述調(diào)用可以發(fā)現(xiàn):我們將該指針作為第一個(gè)參數(shù),同時(shí)將其地址作為第二個(gè)參數(shù),因此對第二個(gè)參數(shù)進(jìn)行修改時(shí),將修改obj->texture指向的實(shí)際值,從而丟失它指向的原始數(shù)據(jù)。但事實(shí)上,并不會(huì)丟失任何數(shù)據(jù),我們將把obj->texture指向的原始位圖用作mip紋理鏈中的第一個(gè)紋理。在刪除mip紋理鏈時(shí),我們重新將該指針指向原始位圖。圖12.44說明了整個(gè)處理過程。
| (點(diǎn)擊查看大圖)圖12.44? 讓obj.texture指向mip紋理鏈和重新指向T0 |
回到mip紋理生成函數(shù)--它創(chuàng)建mip紋理鏈:為紋理指針數(shù)組分配內(nèi)存;然后計(jì)算下一個(gè)mip等級(jí)的大小,為該mip等級(jí)分配內(nèi)存,在RGB空間使用平均濾波器來創(chuàng)建該mip等級(jí)。這一過程不斷重復(fù),直到mip等級(jí)的大小為1×1為止,然后函數(shù)結(jié)束。該函數(shù)的最后一個(gè)參數(shù)(默認(rèn)值為1.01)用于設(shè)置gamma值。使用平均過濾器來不斷縮小圖像時(shí),其亮度會(huì)逐漸降低;因此通常使用gamma因子來提高每個(gè)mip等級(jí)的亮度,確保所有mip等級(jí)的亮度一致。為說明這一點(diǎn),作者編寫了一個(gè)演示程序,名為DEMOII12_10.CPP|H。用戶可以選擇不同的紋理圖,該程序?qū)?dòng)態(tài)地創(chuàng)建mip紋理,并顯示它們,如圖12.45所示。另外,用戶還可以修改gamma值,以查看其影響。該程序的控制方式如下:
| 圖12.45? mip紋理實(shí)時(shí)生成程序的屏幕截圖 |
總結(jié)
- 上一篇: 10年都够用!传比亚迪在非洲购买了6座锂
- 下一篇: UE4打包后如何调试