Metal - 纹理(二)

纹理(二)

sRGB 颜色空间

sRGB 是一种标准的颜色格式,它是站在我们人类肉眼的对不同颜色的可分辨度和敏感度去定义的一种颜色格式。如果用 sRGB 去表示灰度颜色数据的话,你会发现 sRGB 的颜色渐变并不是线性的。我们会看到暗色区域变化更快,而大部分的渐变区域都是浅色。因为对于人眼来说,对浅色的敏感度其实是高于深色的,所以我们会用更多的值去表示浅色。相对于 Abobe RGB 来说,sRGB 的色域更窄,但是由于 sRGB 作为标准的 RGB,使得让它可以保证在不同设备上的颜色表现都是一致的。

在 textureLoaderOptions 配置设置的时候,其实是可以选择设置是否 sRGB 的。

      let textureLoaderOptions : [MTKTextureLoader.Option : Any] =
            [.origin:
                MTKTextureLoader.Origin.bottomLeft, .SRGB: false];

设置成非 sRGB 之后,跑出来的效果会比原来亮很多~

GPU frame capture

GPU frame capture 就是 GPU 的 debug 工具。
在调试工具栏中,相机图标的就是它啦!

gpu_capture_frame.jpg

GPU frame capture 可以帮助我们拿到渲染管线中每一步的操作、信息以及对应的渲染结果,比如 setFragmentBytes setRenderPipelineState 等。

gpuCapturePic1.jpg

选中 GPU 堆栈中的 drawIndexedPrimitives 指令,我们可以看到如下的界面:

gpuCapturePic2.jpg

那我们如何来看这个数据信息呢?这张表格都告诉了我们什么?

  • MDL_OBJ-Indices: 顶点的索引、offset等

  • Buffer 0x: 顶点缓冲区信息,包含了顶点的位置、法线、纹理坐标数据等,跟 shader 中定义的 struct 结构成员变量相对应。

  • Vertex Bytes:矩阵信息

  • Vertex Attributes: 顶点属性信息,表示从顶点处理函数中返回的顶点数据。

  • Vertex_mainP:顶点处理函数

  • xxx.png Texture0:指在位置0的纹理贴图

  • Fragment Bytes:光照信息队列以及光照数量

  • MTKView Depth:深度缓冲信息,黑色表示更近,白色则表示更远

Samplers 采样器

在之前的工程中,我们已经用到了 Sampler,但是也是最基础的初始化。sampler 的初始化还可以设置 filter 和 address 参数。其中 filter 表示 sampler 将如何从原图中采样,是线性采样还是最近邻采样,前者比较顺滑,后者是偏向像素风。而 address 参数则表示,纹理将以何种方式去填充缓冲区。其中 repeat 表示重复平铺满缓冲区,mirrored_repeat 表示每行用镜像的方式平铺。clamp_to_edge 表示当图像大小不够撑满整个缓冲区时,将其最边缘的像素扩展填充满整个空间。clamp_to_zero 就表示不进行任何处理,不够就不够,放在那儿就行。

Sampler states

在上述的介绍中,我们的采样器 Samplers 相关设置或初始化都是写在片元处理函数 fragment_main 中。那么,也就是说所有用这个片元处理函数的模型在进行纹理采样的时候,都是用这同一种采样器。那如何做到对不同的模型使用不同的采样器呢?

在这里 Sampler State 就发挥作用了。不同的模型使用不同的采样器,最好的方式就是将采样器信息封装在模型中。所以,这里我们在 Model 类中添加一个 samplerState:MTLSamplerState 的属性,然后再添加一个 buildSamplerState 的方法来对属性进行初始化。

let samplerState: MTLSamplerState?

private static func buildSamplerState() -> MTLSamplerState? {
  let descriptor = MTLSamplerDescriptor()
  descriptor.sAddressMode = .repeat
  descriptor.tAddressMode = .repeat
  let samplerState =
          Renderer.device.makeSamplerState(descriptor: descriptor)
  return samplerState
}

然后,在 Model 中添加好 SamplerState 属性之后,我们需要在 Renderer 类中将该信息传递到 GPU。所以,在 in draw(in:) 函数中,加入以下代码:

renderEncoder.setFragmentSamplerState(model.samplerState, index: 0)

最后,需要在 shader 的 fragement_main 中添加对应的 sampler 参数来接受信息:

sampler textureSampler [[sampler(0)]],

然后把原来 fragment_main 中有关 sampler 初始化的代码删掉,就好啦。

Mipmaps

当采样器取到的纹素数量比像素量更多的时候,比如我们观察远距离的物体贴图的时候,会发现物体的表面会产生锯齿。可以想象到的是,这个问题的产生明显带来了两个弊端:第一,产生图像锯齿一定是我们不想看到的。第二,远距离的物体在做纹理贴图的时候,其实并不需要完全将原贴图的数据采样渲染出来,也就是说浪费了性能并且影响到渲染的速度。而 Mipmap 的产生就是为了解决这个问题。

Mipmap 就是为了加快渲染速度和减少图像锯齿,将原来的贴图处理成一系列优化过的图片的文件。Mipmap 中每个层级的小图都是由原图的一个特定比例的缩小细节的复制,当贴图被缩小或者只需要从远距离去观看时,mipmap 就会转换到适当的层级。由于 mipmap 贴图需要被读取的像素远小于普通贴图,所以渲染的速度就得到了提升,并且 mipmap 的图片是已经经过抗锯齿处理的,同时也减少了实时渲染的负担。

Mipmaps 中的图片包含了在原图尺寸基础上,所有小于原图尺寸的,2的n次幂的等级。我们假设原图是一张 64 * 64 尺寸的纹理,那么整个 mipmap 的集合将是 Level 0: 64 x 64,1: 32 x 32, 2: 16 x 16, 3: 8 x 8, 4: 4 x 4, 5: 2 x 2, 6: 1

如图所示就是一个 mipmap 如何存储的例子:

mipmap.jpg

那么在 Metal 中,我们可以简单的通过如下代码在对应纹理第一次被加载的时候来创建纹理的 mipmap。首先在我们加载 Texture 的地方,初始化一个 textureLoaderOption:[MTKTextureLoader.Option: Any].

let textureLoaderOptions: [MTKTextureLoader.Option: Any] =
      [.origin: MTKTextureLoader.Origin.bottomLeft,
       .SRGB: false,
       .generateMipmaps: NSNumber(booleanLiteral: true)]

相对应的,我们需要在 Model 类中,生成 SamplerState 的函数中,也就是上面的 buildSamplerState() 将 descriptor 的 mipFilter 属性改为线性 .linear。因为 mipFilter 属性的默认值是不使用 mipmap 的,也就是 .notMipmapped 。除了 .linear还有另外的 .nearest 枚举,在进行对应设置的时候,GPU 都会根据配置对正确的 mipmap 进行采样。

最后

本章主要介绍纹理相关的理论知识以及调试工具的基本用法,后续如有完善会继续补充~

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

推荐阅读更多精彩内容