Metal框架詳細解析(二十二) —— 基本課程之基本紋理(二)

版本記錄

版本號 時間
V1.0 2018.10.08 星期一

前言

很多做視頻和圖像的乐设,相信對這個框架都不是很陌生,它渲染高級3D圖形姻采,并使用GPU執(zhí)行數(shù)據(jù)并行計算。接下來的幾篇我們就詳細的解析這個框架爵憎。感興趣的看下面幾篇文章慨亲。
1. Metal框架詳細解析(一)—— 基本概覽
2. Metal框架詳細解析(二) —— 器件和命令(一)
3. Metal框架詳細解析(三) —— 渲染簡單的2D三角形(一)
4. Metal框架詳細解析(四) —— 關(guān)于GPU Family 4(一)
5. Metal框架詳細解析(五) —— 關(guān)于GPU Family 4之關(guān)于Imageblocks(二)
6. Metal框架詳細解析(六) —— 關(guān)于GPU Family 4之關(guān)于Tile Shading(三)
7. Metal框架詳細解析(七) —— 關(guān)于GPU Family 4之關(guān)于光柵順序組(四)
8. Metal框架詳細解析(八) —— 關(guān)于GPU Family 4之關(guān)于增強的MSAA和Imageblock采樣覆蓋控制(五)
9. Metal框架詳細解析(九) —— 關(guān)于GPU Family 4之關(guān)于線程組共享(六)
10. Metal框架詳細解析(十) —— 基本組件(一)
11. Metal框架詳細解析(十一) —— 基本組件之器件選擇 - 圖形渲染的器件選擇(二)
12. Metal框架詳細解析(十二) —— 基本組件之器件選擇 - 計算處理的設(shè)備選擇(三)
13. Metal框架詳細解析(十三) —— 計算處理(一)
14. Metal框架詳細解析(十四) —— 計算處理之你好,計算(二)
15. Metal框架詳細解析(十五) —— 計算處理之關(guān)于線程和線程組(三)
16. Metal框架詳細解析(十六) —— 計算處理之計算線程組和網(wǎng)格大斜摹(四)
17. Metal框架詳細解析(十七) —— 工具刑棵、分析和調(diào)試(一)
18. Metal框架詳細解析(十八) —— 工具、分析和調(diào)試之Metal GPU Capture(二)
19. Metal框架詳細解析(十九) —— 工具愚铡、分析和調(diào)試之GPU活動監(jiān)視器(三)
20. Metal框架詳細解析(二十) —— 工具蛉签、分析和調(diào)試之關(guān)于Metal著色語言文件名擴展名、使用Metal的命令行工具構(gòu)建庫和標記Metal對象和命令(四)
21. Metal框架詳細解析(二十一) —— 基本課程之基本緩沖區(qū)(一)

Basic Texturing - 基本紋理

演示如何加載圖像數(shù)據(jù)和紋理四邊形茂附。

Basic Buffers示例中正蛙,您學習了如何在Metal中渲染基本幾何體。

在此示例中营曼,您將學習如何通過將紋理應(yīng)用于單個四邊形來渲染2D圖像乒验。 特別是,您將學習如何配置紋理屬性蒂阱,解釋紋理坐標以及訪問片段函數(shù)中的紋理锻全。


Images and Textures - 圖像和紋理

任何圖形技術(shù)的一個關(guān)鍵特性是處理和繪制圖像的能力。 Metal以包含圖像數(shù)據(jù)的紋理形式支持此功能录煤。 與常規(guī)2D圖像不同鳄厌,紋理可以以更具創(chuàng)造性的方式使用,并應(yīng)用于更多表面類型妈踊。 例如了嚎,紋理可用于替換選定的頂點位置,或者它們可以完全包裹在3D對象周圍廊营。 在此示例中歪泳,圖像數(shù)據(jù)被加載到紋理中,應(yīng)用于單個四邊形露筒,并呈現(xiàn)為2D圖像呐伞。


Load Image Data - 加載圖像數(shù)據(jù)

Metal框架不提供直接將文件中的圖像數(shù)據(jù)加載到紋理的API。 相反慎式,Metal應(yīng)用程序或游戲依賴于自定義代碼或其他框架(如Image I / O伶氢,MetalKit趟径,UIKitAppKit)來處理圖像文件。 Metal本身僅分配紋理資源癣防,然后使用先前加載到內(nèi)存中的圖像數(shù)據(jù)填充它們蜗巧。

在此示例中,為簡單起見劣砍,自定義AAPLImage類將圖像數(shù)據(jù)從文件(Image.tga)加載到內(nèi)存(NSData)中惧蛹。

注意:AAPLImage類不是此示例的焦點,因此不對其進行詳細討論刑枝。 該類演示了基本的圖像加載操作,但不以任何方式使用或依賴于Metal框架迅腔。 其唯一目的是便于加載該特定示例的圖像數(shù)據(jù)装畅。

此示例使用TGA文件格式以簡化。 該文件由描述元數(shù)據(jù)的標題(例如圖像尺寸)和圖像數(shù)據(jù)本身組成沧烈。 這種文件格式的關(guān)鍵點是圖像數(shù)據(jù)的存儲器布局掠兄;特別是每個像素的布局。

Metal要求使用特定的MTLPixelFormat值格式化所有紋理锌雀。 像素格式描述了每個紋理像素(其紋理像素)的布局蚂夕。 要使用圖像數(shù)據(jù)填充Metal紋理,其像素數(shù)據(jù)必須已經(jīng)采用Metal兼容的像素格式進行格式化腋逆,該格式由單個MTLPixelFormat枚舉值定義婿牍。 此示例使用MTLPixelFormatBGRA8Unorm像素格式,表示每個像素具有以下內(nèi)存布局:

此像素格式使用每像素32位惩歉,按照藍色等脂,綠色,紅色和alpha順序排列為每個組件8位撑蚌。 每個像素使用32位的TGA文件已經(jīng)以這種格式排列上遥,因此不需要進一步的轉(zhuǎn)換操作。 但是争涌,此示例使用每像素24位BGR圖像粉楚,需要為每個像素添加額外的8位alpha分量。 由于alpha通常定義圖像的不透明度亮垫,并且樣本的圖像完全不透明模软,因此32位BGRA像素的附加8位alpha分量設(shè)置為255。

AAPLImage類加載圖像文件之后包警,可以通過對data屬性的查詢來訪問圖像數(shù)據(jù)撵摆。

// Initialize a source pointer with the source image data that's in BGR form
uint8_t *srcImageData = ((uint8_t*)fileData.bytes +
                         sizeof(TGAHeader) +
                         tgaInfo->IDSize);

// Initialize a destination pointer to which you'll store the converted BGRA
// image data
uint8_t *dstImageData = mutableData.mutableBytes;

// For every row of the image
for(NSUInteger y = 0; y < _height; y++)
{
    // For every column of the current row
    for(NSUInteger x = 0; x < _width; x++)
    {
        // Calculate the index for the first byte of the pixel you're
        // converting in both the source and destination images
        NSUInteger srcPixelIndex = 3 * (y * _width + x);
        NSUInteger dstPixelIndex = 4 * (y * _width + x);

        // Copy BGR channels from the source to the destination
        // Set the alpha channel of the destination pixel to 255
        dstImageData[dstPixelIndex + 0] = srcImageData[srcPixelIndex + 0];
        dstImageData[dstPixelIndex + 1] = srcImageData[srcPixelIndex + 1];
        dstImageData[dstPixelIndex + 2] = srcImageData[srcPixelIndex + 2];
        dstImageData[dstPixelIndex + 3] = 255;
    }
}
_data = mutableData;

Create a Texture - 創(chuàng)建紋理

MTLTextureDescriptor對象用于配置MTLTexture對象的紋理尺寸和像素格式等屬性。 然后調(diào)用newTextureWithDescriptor:方法創(chuàng)建一個空紋理容器并為紋理數(shù)據(jù)分配足夠的內(nèi)存害晦。

MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];

// Indicate that each pixel has a blue, green, red, and alpha channel, where each channel is
// an 8-bit unsigned normalized value (i.e. 0 maps to 0.0 and 255 maps to 1.0)
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;

 // Set the pixel dimensions of the texture
textureDescriptor.width = image.width;
textureDescriptor.height = image.height;

// Create the texture from the device by using the descriptor
_texture = [_device newTextureWithDescriptor:textureDescriptor];

與存儲多種自定義數(shù)據(jù)的MTLBuffer對象不同暑中,MTLTexture對象專門用于存儲格式化的圖像數(shù)據(jù)鲫剿。 盡管MTLTextureDescriptor對象指定了足夠的信息來分配紋理內(nèi)存,但還需要其他信息來填充空紋理容器灵莲。 通過replaceRegion:mipmapLevel:withBytes:bytesPerRow:方法用MTLTexture對象填充圖像數(shù)據(jù)雕凹。

圖像數(shù)據(jù)通常按行組織政冻。 此示例計算每行的字節(jié)數(shù),即每個像素的字節(jié)數(shù)乘以圖像寬度明场。 這種類型的圖像數(shù)據(jù)被認為是緊密打包(tightly packed)的汽摹,因為后續(xù)像素行的數(shù)據(jù)緊跟在前一行之后。

NSUInteger bytesPerRow = 4 * image.width;

紋理具有已知的尺寸苦锨,可以解釋為像素區(qū)域。 MTLRegion結(jié)構(gòu)用于標識紋理的特定區(qū)域舟舒。 此示例使用圖像數(shù)據(jù)填充整個紋理;因此氏仗,覆蓋整個紋理的像素區(qū)域等于紋理的尺寸莺治。

MTLRegion region = {
    { 0, 0, 0 },                   // MTLOrigin
    {image.width, image.height, 1} // MTLSize
};

注意:要指定紋理的子區(qū)域,MTLRegion結(jié)構(gòu)必須具有非零原點值或任何紋理尺寸的較小尺寸值床佳。

每行和特定像素區(qū)域的字節(jié)數(shù)是用于使用圖像數(shù)據(jù)填充空紋理容器的必需參數(shù)榄审。 調(diào)用replaceRegion:mipmapLevel:withBytes:bytesPerRow:方法通過將image.data.bytes指針中的數(shù)據(jù)復(fù)制到_texture對象來執(zhí)行此操作。

[_texture replaceRegion:region
            mipmapLevel:0
              withBytes:image.data.bytes
            bytesPerRow:bytesPerRow];

Texture Coordinates - 紋理坐標

片段函數(shù)的主要任務(wù)是處理傳入的片段數(shù)據(jù)并計算可繪制像素的顏色值浪感。此示例的目標是通過將紋理應(yīng)用于單個四邊形來顯示屏幕上每個紋素的顏色饼问。因此,樣本的片段函數(shù)必須能夠讀取每個紋素并輸出其顏色峻堰。

紋理不能單獨渲染;它必須對應(yīng)于頂點函數(shù)輸出的一些幾何表面旦万,并由光柵化器變成碎片镶蹋。此關(guān)系由紋理坐標定義:浮點位置成艘,將紋理圖像上的位置映射到幾何表面上的位置淆两。

對于2D紋理牧氮,紋理坐標在x和y方向上的值均為0.0到1.0。值(0.0,0.0)映射到圖像數(shù)據(jù)的第一個字節(jié)(圖像的左下角)處的紋素。值(1.0,1.0)映射到圖像數(shù)據(jù)的最后一個字節(jié)(圖像的右上角)處的紋素光坝。遵循這些規(guī)則,訪問圖像中心的紋素需要指定紋理坐標(0.5,0.5)性含。


Map the Vertex Texture Coordinates - 映射頂點紋理坐標

要渲染完整的2D圖像商蕴,必須將包含圖像數(shù)據(jù)的紋理映射到定義2D四邊形的頂點上芝发。 在此示例中,每個四邊形的頂點指定一個紋理坐標格郁,將四邊形的角映射到紋理的角独悴。

static const AAPLVertex quadVertices[] =
{
    // Pixel positions, Texture coordinates
    { {  250,  -250 },  { 1.f, 0.f } },
    { { -250,  -250 },  { 0.f, 0.f } },
    { { -250,   250 },  { 0.f, 1.f } },

    { {  250,  -250 },  { 1.f, 0.f } },
    { { -250,   250 },  { 0.f, 1.f } },
    { {  250,   250 },  { 1.f, 1.f } },
};

vertexShader頂點函數(shù)通過將這些值寫入RasterizerData輸出結(jié)構(gòu)的textureCoordinate成員中,沿管道傳遞這些值决采。 這些值在四邊形的三角形片段中進行插值坟奥,類似于Hello Triangle樣本中的插值顏色值拇厢。

out.textureCoordinate = vertexArray[vertexID].textureCoordinate;

Sample Texels - 示例特征

samplingShader片段函數(shù)的簽名包括colorTexture參數(shù)旺嬉,該參數(shù)具有texture2d類型并使用[[texture(index)]]屬性限定符厨埋。 此參數(shù)是對MTLTexture對象的引用,用于讀取其紋素雨效。

fragment float4
samplingShader(RasterizerData in [[stage_in]],
               texture2d<half> colorTexture [[ texture(AAPLTextureIndexBaseColor) ]])

讀取紋素也稱為采樣废赞。 片段函數(shù)使用內(nèi)置紋理sample()函數(shù)來對texel數(shù)據(jù)進行采樣。 sample()函數(shù)有兩個參數(shù):一個采樣器(textureSampler)和一個紋理坐標(in.textureCoordinate)据悔。 采樣器用于計算紋理元素的顏色耘沼,紋理坐標用于定位特定紋理元素。

當渲染的區(qū)域與紋理的大小不同時菠隆,采樣器可以使用不同的算法來精確計算sample()函數(shù)應(yīng)返回的texel顏色狂秘。 mag_filter模式指定當區(qū)域大于紋理大小時,采樣器應(yīng)如何計算返回的顏色破衔;min_filter模式指定當區(qū)域小于紋理大小時碧查,采樣器應(yīng)如何計算返回的顏色。 為兩個濾鏡設(shè)置線性linear模式可使采樣器平均給定紋理坐標周圍的紋素顏色传惠,從而使輸出圖像更平滑稻扬。

constexpr sampler textureSampler (mag_filter::linear,
                                  min_filter::linear);

// Sample the texture to obtain a color
const half4 colorSample = colorTexture.sample(textureSampler, in.textureCoordinate);

Set a Fragment Texture - 設(shè)置片段紋理

此示例使用AAPLTextureIndexBaseColor索引來標識Objective-CMetal Shading Language代碼中的紋理泰佳。 片段函數(shù)也采用與頂點函數(shù)類似的參數(shù)尘吗,你可以調(diào)用setFragmentTexture:atIndex:方法在特定索引處設(shè)置紋理浇坐。

[renderEncoder setFragmentTexture:_texture
                          atIndex:AAPLTextureIndexBaseColor];

在此示例中,您學習了如何通過將紋理應(yīng)用于單個四邊形來渲染2D圖像擒贸。

后記

本篇主要講述了基本紋理觉渴,感興趣的給個贊或者關(guān)注~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末案淋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子誉碴,更是在濱河造成了極大的恐慌瓣距,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異白华,居然都是意外死亡贩耐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門管搪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铡买,“玉大人奇钞,你說我怎么就攤上這事【鞍#” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵驯绎,是天一觀的道長谋旦。 經(jīng)常有香客問我,道長赴叹,這世上最難降的妖魔是什么指蚜? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮绽媒,結(jié)果婚禮上免猾,老公的妹妹穿的比我還像新娘。我一直安慰自己获三,他們只是感情好锨苏,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布伞租。 她就那樣靜靜地躺著,像睡著了一般葵诈。 火紅的嫁衣襯著肌膚如雪作喘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天沪斟,我揣著相機與錄音,去河邊找鬼择吊。 笑死槽奕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的粤攒。 我是一名探鬼主播夯接,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晴弃!你這毒婦竟也來了逊拍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤芍阎,失蹤者是張志新(化名)和其女友劉穎缨恒,沒想到半個月后骗露,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年驹暑,在試婚紗的時候發(fā)現(xiàn)自己被綠了辨赐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡帆焕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出财饥,到底是詐尸還是另有隱情,我是刑警寧澤奴拦,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜还蹲,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一豁鲤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锅论,春花似錦楣号、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鄙早,卻和暖如春椅亚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背弥虐。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留珠插,地道東北人粥庄。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓惜互,卻偏偏與公主長得像,于是被迫代替她去往敵國和親训堆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容