Metal框架详细解析(二十二) —— 基本课程之基本纹理(二)

版本记录

版本号 时间
V1.0 2018.10.08 星期一

前言

很多做视频和图像的,相信对这个框架都不是很陌生,它渲染高级3D图形,并使用GPU执行数据并行计算。接下来的几篇我们就详细的解析这个框架。感兴趣的看下面几篇文章。
1. Metal框架详细解析(一)—— 基本概览
2. Metal框架详细解析(二) —— 器件和命令(一)
3. Metal框架详细解析(三) —— 渲染简单的2D三角形(一)
4. Metal框架详细解析(四) —— 关于GPU Family 4(一)
5. Metal框架详细解析(五) —— 关于GPU Family 4之关于Imageblocks(二)
6. Metal框架详细解析(六) —— 关于GPU Family 4之关于Tile Shading(三)
7. Metal框架详细解析(七) —— 关于GPU Family 4之关于光栅顺序组(四)
8. Metal框架详细解析(八) —— 关于GPU Family 4之关于增强的MSAA和Imageblock采样覆盖控制(五)
9. Metal框架详细解析(九) —— 关于GPU Family 4之关于线程组共享(六)
10. Metal框架详细解析(十) —— 基本组件(一)
11. Metal框架详细解析(十一) —— 基本组件之器件选择 - 图形渲染的器件选择(二)
12. Metal框架详细解析(十二) —— 基本组件之器件选择 - 计算处理的设备选择(三)
13. Metal框架详细解析(十三) —— 计算处理(一)
14. Metal框架详细解析(十四) —— 计算处理之你好,计算(二)
15. Metal框架详细解析(十五) —— 计算处理之关于线程和线程组(三)
16. Metal框架详细解析(十六) —— 计算处理之计算线程组和网格大小(四)
17. Metal框架详细解析(十七) —— 工具、分析和调试(一)
18. Metal框架详细解析(十八) —— 工具、分析和调试之Metal GPU Capture(二)
19. Metal框架详细解析(十九) —— 工具、分析和调试之GPU活动监视器(三)
20. Metal框架详细解析(二十) —— 工具、分析和调试之关于Metal着色语言文件名扩展名、使用Metal的命令行工具构建库和标记Metal对象和命令(四)
21. Metal框架详细解析(二十一) —— 基本课程之基本缓冲区(一)

Basic Texturing - 基本纹理

演示如何加载图像数据和纹理四边形。

Basic Buffers示例中,您学习了如何在Metal中渲染基本几何体。

在此示例中,您将学习如何通过将纹理应用于单个四边形来渲染2D图像。 特别是,您将学习如何配置纹理属性,解释纹理坐标以及访问片段函数中的纹理。


Images and Textures - 图像和纹理

任何图形技术的一个关键特性是处理和绘制图像的能力。 Metal以包含图像数据的纹理形式支持此功能。 与常规2D图像不同,纹理可以以更具创造性的方式使用,并应用于更多表面类型。 例如,纹理可用于替换选定的顶点位置,或者它们可以完全包裹在3D对象周围。 在此示例中,图像数据被加载到纹理中,应用于单个四边形,并呈现为2D图像。


Load Image Data - 加载图像数据

Metal框架不提供直接将文件中的图像数据加载到纹理的API。 相反,Metal应用程序或游戏依赖于自定义代码或其他框架(如Image I / O,MetalKit,UIKitAppKit)来处理图像文件。 Metal本身仅分配纹理资源,然后使用先前加载到内存中的图像数据填充它们。

在此示例中,为简单起见,自定义AAPLImage类将图像数据从文件(Image.tga)加载到内存(NSData)中。

注意:AAPLImage类不是此示例的焦点,因此不对其进行详细讨论。 该类演示了基本的图像加载操作,但不以任何方式使用或依赖于Metal框架。 其唯一目的是便于加载该特定示例的图像数据。

此示例使用TGA文件格式以简化。 该文件由描述元数据的标题(例如图像尺寸)和图像数据本身组成。 这种文件格式的关键点是图像数据的存储器布局;特别是每个像素的布局。

Metal要求使用特定的MTLPixelFormat值格式化所有纹理。 像素格式描述了每个纹理像素(其纹理像素)的布局。 要使用图像数据填充Metal纹理,其像素数据必须已经采用Metal兼容的像素格式进行格式化,该格式由单个MTLPixelFormat枚举值定义。 此示例使用MTLPixelFormatBGRA8Unorm像素格式,表示每个像素具有以下内存布局:

此像素格式使用每像素32位,按照蓝色,绿色,红色和alpha顺序排列为每个组件8位。 每个像素使用32位的TGA文件已经以这种格式排列,因此不需要进一步的转换操作。 但是,此示例使用每像素24位BGR图像,需要为每个像素添加额外的8位alpha分量。 由于alpha通常定义图像的不透明度,并且样本的图像完全不透明,因此32位BGRA像素的附加8位alpha分量设置为255。

AAPLImage类加载图像文件之后,可以通过对data属性的查询来访问图像数据。

// 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 - 创建纹理

MTLTextureDescriptor对象用于配置MTLTexture对象的纹理尺寸和像素格式等属性。 然后调用newTextureWithDescriptor:方法创建一个空纹理容器并为纹理数据分配足够的内存。

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];

与存储多种自定义数据的MTLBuffer对象不同,MTLTexture对象专门用于存储格式化的图像数据。 尽管MTLTextureDescriptor对象指定了足够的信息来分配纹理内存,但还需要其他信息来填充空纹理容器。 通过replaceRegion:mipmapLevel:withBytes:bytesPerRow:方法用MTLTexture对象填充图像数据。

图像数据通常按行组织。 此示例计算每行的字节数,即每个像素的字节数乘以图像宽度。 这种类型的图像数据被认为是紧密打包(tightly packed)的,因为后续像素行的数据紧跟在前一行之后。

NSUInteger bytesPerRow = 4 * image.width;

纹理具有已知的尺寸,可以解释为像素区域。 MTLRegion结构用于标识纹理的特定区域。 此示例使用图像数据填充整个纹理;因此,覆盖整个纹理的像素区域等于纹理的尺寸。

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

注意:要指定纹理的子区域,MTLRegion结构必须具有非零原点值或任何纹理尺寸的较小尺寸值。

每行和特定像素区域的字节数是用于使用图像数据填充空纹理容器的必需参数。 调用replaceRegion:mipmapLevel:withBytes:bytesPerRow:方法通过将image.data.bytes指针中的数据复制到_texture对象来执行此操作。

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

Texture Coordinates - 纹理坐标

片段函数的主要任务是处理传入的片段数据并计算可绘制像素的颜色值。此示例的目标是通过将纹理应用于单个四边形来显示屏幕上每个纹素的颜色。因此,样本的片段函数必须能够读取每个纹素并输出其颜色。

纹理不能单独渲染;它必须对应于顶点函数输出的一些几何表面,并由光栅化器变成碎片。此关系由纹理坐标定义:浮点位置,将纹理图像上的位置映射到几何表面上的位置。

对于2D纹理,纹理坐标在x和y方向上的值均为0.0到1.0。值(0.0,0.0)映射到图像数据的第一个字节(图像的左下角)处的纹素。值(1.0,1.0)映射到图像数据的最后一个字节(图像的右上角)处的纹素。遵循这些规则,访问图像中心的纹素需要指定纹理坐标(0.5,0.5)


Map the Vertex Texture Coordinates - 映射顶点纹理坐标

要渲染完整的2D图像,必须将包含图像数据的纹理映射到定义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顶点函数通过将这些值写入RasterizerData输出结构的textureCoordinate成员中,沿管道传递这些值。 这些值在四边形的三角形片段中进行插值,类似于Hello Triangle样本中的插值颜色值。

out.textureCoordinate = vertexArray[vertexID].textureCoordinate;

Sample Texels - 示例特征

samplingShader片段函数的签名包括colorTexture参数,该参数具有texture2d类型并使用[[texture(index)]]属性限定符。 此参数是对MTLTexture对象的引用,用于读取其纹素。

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

读取纹素也称为采样。 片段函数使用内置纹理sample()函数来对texel数据进行采样。 sample()函数有两个参数:一个采样器(textureSampler)和一个纹理坐标(in.textureCoordinate)。 采样器用于计算纹理元素的颜色,纹理坐标用于定位特定纹理元素。

当渲染的区域与纹理的大小不同时,采样器可以使用不同的算法来精确计算sample()函数应返回的texel颜色。 mag_filter模式指定当区域大于纹理大小时,采样器应如何计算返回的颜色;min_filter模式指定当区域小于纹理大小时,采样器应如何计算返回的颜色。 为两个滤镜设置线性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 - 设置片段纹理

此示例使用AAPLTextureIndexBaseColor索引来标识Objective-CMetal Shading Language代码中的纹理。 片段函数也采用与顶点函数类似的参数,你可以调用setFragmentTexture:atIndex:方法在特定索引处设置纹理。

[renderEncoder setFragmentTexture:_texture
                          atIndex:AAPLTextureIndexBaseColor];

在此示例中,您学习了如何通过将纹理应用于单个四边形来渲染2D图像。

后记

本篇主要讲述了基本纹理,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容