本文主要通过自己对 Metal By Example 理解编写,这一篇文章讲解 Metal 在纹理映射中使用 Mipmap。

Metal By Example Cover

Mipmap 理论

当纹理元素与屏幕像素不一致的时候,就需要过滤方式来处理两者之间的映射,纹理图像就可能会被拉升或缩小,Mipmap 先将纹理图像,按照每次比之前缩小 4/1 的方式处理,直到只有 1 个像素大小,每一次处理的图像称为 1 个等级,处理多少次就有多少个等级。

当 Mipmaped 的纹理图像在被 Sample 的时候,根据区域片段的大小来决定采用什么等级的纹理图像。

通过设置不同的 minFilter 和 mipFilter 可以产生 4 种组合:

选择最近的一个等级的纹理图像,Sample 的时候选择一个纹理元素:

MTLSamplerDescriptor *samplerDesc = [MTLSamplerDescriptor new];
samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
samplerDesc.mipFilter = MTLSamplerMipFilterNearest;

选择最近的两个等级的纹理图像,Sample 的时候从中各选一个纹理元素,再取平均得到最终结果:

MTLSamplerDescriptor *samplerDesc = [MTLSamplerDescriptor new];
samplerDesc.minFilter = MTLSamplerMinMagFilterNearest;
samplerDesc.mipFilter = MTLSamplerMipFilterLinear;

选择最近的一个等级的纹理图像,Sample 的时候从中各选四个纹理元素,再取平均得到最终结果:

MTLSamplerDescriptor *samplerDesc = [MTLSamplerDescriptor new];
samplerDesc.minFilter = MTLSamplerMinMagFilterLinear;
samplerDesc.mipFilter = MTLSamplerMipFilterNearest;

选择最近的两个等级的纹理图像,Sample 的时候从中各选四个纹理元素,再取平均得到最终结果:

MTLSamplerDescriptor *samplerDesc = [MTLSamplerDescriptor new];
samplerDesc.minFilter = MTLSamplerMinMagFilterLinear;
samplerDesc.mipFilter = MTLSamplerMipFilterLinear;

创建 Mipmap 各等级的纹理图像

创建纹理

mipmapped 为 YES

MTLTextureDescriptor *descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
                                                                                      width:size.width
                                                                                     height:size.height
                                                                                  mipmapped:YES];
id<MTLTexture> texture = [device newTextureWithDescriptor:descriptor];

通过 CG 创建每一等级的纹理图像

MTLRegion region = MTLRegionMake2D(0, 0, mipWidth, mipHeight);
[texture replaceRegion:region mipmapLevel:level withBytes:[mipData bytes] bytesPerRow:mipBytesPerRow];

通过 Blit Command Encoder 创建每一等级的纹理图像

id<MTLBlitCommandEncoder> commandEncoder = [commandBuffer blitCommandEncoder];
[commandEncoder generateMipmapsForTexture:texture];
[commandEncoder endEncoding];
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
    completionBlock(texture);
}];
[commandBuffer commit];

代码和效果

danjiang / mbe-sample-code / Mipmapping