Metal 加载纹理

2020-08-28  本文已影响0人  Maji1

本案例的讲解是在 Metal 绘制网格 案例基础上的讲解。

首先说明下改案例需要的几个类:

一、CQTexture.metal代码:

#include <metal_stdlib>
using namespace metal;

#import "CQTexture.h"

struct VertexOut {
    float4 clipSpacePosition [[position]];
    float2 textureCoordinate;
};

vertex VertexOut vertexShaderTexture(uint vertexIndex [[vertex_id]],
                              constant CQVertexTextureInput *vertexInput [[buffer(CQVInTextureIndexVertices)]],
                              constant vector_uint2 *viewportSizePointer [[buffer(CQVInIndexViewportSize)]]) {
    //取出顶点坐标的xy,该案例中的位置是在像素维度中指定的。
    float2 pixelSpacePosition = vertexInput[vertexIndex].position.xy;
    vector_float2 viewportSize = vector_float2 (*viewportSizePointer);
    float2 xy = pixelSpacePosition / (viewportSize / 2.0);
    
    VertexOut out;
    out.clipSpacePosition.xy = xy;
    out.clipSpacePosition.zw = vector_float2(0.0, 1.0);
    out.textureCoordinate = vertexInput[vertexIndex].textureCoordinate;
    return out;
}

fragment float4 fragmentShaderTexture(VertexOut in [[stage_in]],
                                      texture2d<half> colorTexture [[texture(CQTextureIndexBaseColor)]]) {
    constexpr sampler textureSampler(min_filter::linear,
                                     mag_filter::linear);
    const half4 colorSampler = colorTexture.sample(textureSampler, in.textureCoordinate);
    return float4(colorSampler);
}

该案例中顶点函数vertexShaderTexture中的代码跟Metal 绘制网格 中的顶点函数代码一致。
片元函数fragmentShaderTexture中我们传了一个参数:colorTexture

二、CQTextureRender.m代码:

看下纹理渲染的准备工作:

- (instancetype)initWithMetalKitView:(MTKView *)mtkView {
    self = [super init];
    if(self) {
        _mtkView = mtkView;
        [self setupVertices];
        [self setupPipeline];
        [self loadTGATexture];
    }
    
    return self;
}

2.1设置顶点:

- (void)setupVertices {
    static const CQVertexTextureInput vertices[] = {
        //像素坐标,纹理坐标
        { {  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 } },
    };
    
    _verticesBuffer = [_mtkView.device newBufferWithBytes:vertices length:sizeof(vertices) options:MTLResourceStorageModeShared];
    _verticesNumber = sizeof(vertices)/sizeof(CQVertexTextureInput);
}

2.设置管线:

- (void)setupPipeline {
    //加载所有的metal文件
    id<MTLLibrary> defaultLibrary = [_mtkView.device newDefaultLibrary];
    id<MTLFunction> vertexShaderTexture = [defaultLibrary newFunctionWithName:@"vertexShaderTexture"];
    id<MTLFunction> fragmentShaderTexture = [defaultLibrary newFunctionWithName:@"fragmentShaderTexture"];
    
    MTLRenderPipelineDescriptor *renderPipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
    renderPipelineDescriptor.vertexFunction = vertexShaderTexture;
    renderPipelineDescriptor.fragmentFunction = fragmentShaderTexture;
    renderPipelineDescriptor.colorAttachments[0].pixelFormat = _mtkView.colorPixelFormat;
    
    NSError *error;
    _renderPipelineState = [_mtkView.device newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error:&error];
    if (!_renderPipelineState) {
        NSLog(@"Make render pipeline state error:%@", error);
        return;
    }
    _commandQueue = [_mtkView.device newCommandQueue];   
}

3.1加载tga格式纹理:

- (void)loadTGATexture {
    //1.获取图片
    NSURL *imageUrl = [[NSBundle mainBundle] URLForResource:@"Image" withExtension:@"tga"];
    CCImage *image = [[CCImage alloc] initWithTGAFileAtLocation:imageUrl];
//    AAPLImage *image = [[AAPLImage alloc] initWithTGAFileAtLocation:imageUrl];
    if(!image) {
        NSLog(@"Failed to create the image from:%@",imageUrl.absoluteString);
    }
    //2.创建纹理描述符
    MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];
    textureDescriptor.width = image.width;
    textureDescriptor.height = image.height;
    textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
    //3.创建纹理
    //识别图像中的像素。MTLOrigin通常用作纹理区域的左上角。
    MTLOrigin origion = MTLOriginMake(0, 0, 0);
    MTLSize size = MTLSizeMake(image.width, image.height, 1);
    MTLRegion region = {origion, size};
    
    _texture = [_mtkView.device newTextureWithDescriptor:textureDescriptor];
    [_texture replaceRegion:region mipmapLevel:0 withBytes:image.data.bytes bytesPerRow:image.width * 4];
}
typedef struct  {
     MTLOrigin origin; //开始位置x,y,z
     MTLSize   size; //尺寸width,height,depth
 } MTLRegion;

- (void)replaceRegion:(MTLRegion)region mipmapLevel:(NSUInteger)level withBytes:(const void *)pixelBytes bytesPerRow:(NSUInteger)bytesPerRow;
region:像素区域在纹理中的位置
level:从零开始的值,指定哪个mipmap级别的目标。如果纹理没有mipmap,使用0。
pixelBytes:指向要复制图片的字节数。
bytesPerRow:每一行像素的所占字节大小。

3.2加载png、jpg格式纹理:

-(void)loadPNGTexture {
    UIImage *image = [UIImage imageNamed:@"scence.png"];
    MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];
    //表示每个像素有蓝色,绿色,红色和alpha通道.其中每个通道都是8位无符号归一化的值.(即0映射成0,255映射成1);
    textureDescriptor.pixelFormat = MTLPixelFormatRGBA8Unorm;
    textureDescriptor.width = image.size.width;
    textureDescriptor.height = image.size.height;
  
    _texture = [_mtkView.device newTextureWithDescriptor:textureDescriptor];

    MTLOrigin origion = MTLOriginMake(0, 0, 0);
    MTLSize size = MTLSizeMake(image.size.width, image.size.height, 1);
    MTLRegion region = {origion, size};
    
    //获取图片数据
    Byte *imageBytes = [self loadImage:image];
    
    //UIImage的数据需要转成二进制才能上传,且不用jpg、png的NSData
    if (imageBytes) {
        [_texture replaceRegion:region
                        mipmapLevel:0
                          withBytes:imageBytes
                        bytesPerRow:4 * image.size.width];
        free(imageBytes);
        imageBytes = NULL;
    }
}
- (Byte *)loadImage:(UIImage *)image {
    // 1.获取图片的CGImageRef
    CGImageRef spriteImage = image.CGImage;
    
    // 2.读取图片的大小
    size_t width = CGImageGetWidth(spriteImage);
    size_t height = CGImageGetHeight(spriteImage);
   
    //3.计算图片大小.rgba共4个byte
    Byte * spriteData = (Byte *)calloc(width * height * 4, sizeof(Byte));
    
    //4.创建画布
    CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
    
    //5.在CGContextRef上绘图
    CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
    
    //6.图片翻转过来
    CGRect rect = CGRectMake(0, 0, width, height);
    CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
    CGContextTranslateCTM(spriteContext, 0, rect.size.height);
    CGContextScaleCTM(spriteContext, 1.0, -1.0);
    CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
    CGContextDrawImage(spriteContext, rect, spriteImage);
    
    //7.释放spriteContext
    CGContextRelease(spriteContext);
    
    return spriteData;
}

4.MTKViewDelegate绘制纹理

- (void)drawInMTKView:(nonnull MTKView *)view {
    
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    commandBuffer.label = @"TextureCommandBuffer";
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    if (renderPassDescriptor) {
        id<MTLRenderCommandEncoder> renderCommandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        renderCommandEncoder.label = @"TextureRenderCommandEncoder";
        MTLViewport viewport = {0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0};
        [renderCommandEncoder setViewport:viewport];
        [renderCommandEncoder setRenderPipelineState:_renderPipelineState];
        [renderCommandEncoder setVertexBuffer:_verticesBuffer offset:0 atIndex:CQVInTextureIndexVertices];
        [renderCommandEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:CQVInIndexViewportSize];
        [renderCommandEncoder setFragmentTexture:_texture atIndex:CQTextureIndexBaseColor];
        
        [renderCommandEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:_verticesNumber];
        [renderCommandEncoder endEncoding];
        [commandBuffer presentDrawable:view.currentDrawable];
    }
    
    [commandBuffer commit];
}

- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
    _viewportSize.x = size.width;
    _viewportSize.y = size.height;
}

绘制效果:


官方案例 Creating and Sampling Textures

上一篇 下一篇

猜你喜欢

热点阅读