美文网首页图像处理
MetalPetal使用自定义Metal文件的注意事项

MetalPetal使用自定义Metal文件的注意事项

作者: One苹果OneDay | 来源:发表于2021-04-15 16:22 被阅读0次

    关于iOS的图像处理目前主流的有OpenGL ES和Metal,这里介绍一下Metal的第三方库,这个库简化了非常多的原生初始化方法,可以非常简便的实现各种滤镜效果,MetalPetal的自带滤镜使用很简单这里不展开讨论,作为一个初学者,简单记录一下学习MetalPetal库时遇到的的问题,重点是自定义Metal文件下如何使用MetalPetal
    设置三个属性
    //计算管线
    @property(nonatomic,strong) MTIComputePipelineKernel *customComputeKernel;
    //渲染管线
    @property(nonatomic,strong) MTIRenderPipelineKernel *customRenderKernel;
    //图像
    @property (nonatomic, strong) MTIImage *inputImage;
    首先是初始化一个MTIImage
    UIImage *img = [UIImage imageNamed:@"gakiki"];
    self.inputImage = [[MTIImage alloc] initWithCGImage:img.CGImage options:nil isOpaque:YES];
    初始化计算管线和渲染管线
    -(void)setupPipeline
    {
    //计算管线

    NSURL *defaultUrl = [[NSBundle mainBundle] URLForResource:@"default" withExtension:@"metallib"];
    MTIFunctionDescriptor *customKernelDes = [[MTIFunctionDescriptor alloc] initWithName:@"normalTestKernel" libraryURL:defaultUrl];
    
    
    //初始化计算管线
    self.customComputeKernel = [[MTIComputePipelineKernel alloc] initWithComputeFunctionDescriptor:customKernelDes];
    //渲染管线
    //顶点描述
    MTIFunctionDescriptor *VertexFunctionDescriptor = [[MTIFunctionDescriptor alloc] initWithName:@"normalvertexShader" libraryURL:defaultUrl];
    //片元描述
    MTIFunctionDescriptor *fragmentFunctionDescriptor = [[MTIFunctionDescriptor alloc] initWithName:@"normalfragmentShader" libraryURL:defaultUrl];
    //初始化渲染管线
    self.customRenderKernel = [[MTIRenderPipelineKernel alloc] initWithVertexFunctionDescriptor:VertexFunctionDescriptor fragmentFunctionDescriptor:fragmentFunctionDescriptor];
    

    }
    这里有第一个要注意的点,如果是自定义的.metal文件MTIFunctionDescriptor的描述必须要用libraryURL的方式去初始化,因为MetalPetal默认的library是自带的那些文件方法,如果不用libraryURL这个方式是读取不到你自己的方法的,剩下的就是根据方法名去初始化MTIFunctionDescriptor,记得MTIRenderPipelineKernel要顶点和片元两个描述,别漏了就行
    渲染管线和计算管线已经初始化好了,接下来就是应用了,首先看一下计算管线的使用

    • (void)drawViewWithCompute {
      MTIImage *outImage = [_customComputeKernel applyToInputImages:@[_inputImage] parameters:@{} outputTextureDimensions:MTITextureDimensionsMake2DFromCGSize(_inputImage.size) outputPixelFormat:MTLPixelFormatRGBA32Float];
      _renderView.image = outImage;
      }
      这里我们对比一下原生的metal
      id<MTLComputeCommandEncoder> commandEncoder = [commandBuffer computeCommandEncoder];
      //计算管线的配置
      [commandEncoder setComputePipelineState:self.pipeline];
      //传参给kernel函数
      [commandEncoder setTexture:inputTexture atIndex:0];
      [commandEncoder setTexture:self.internalTexture atIndex:1];
      原生的我们在index0和index1分别输入inputTexture和internalTexture,inputTexture的access::read作为处理的数据源纹理,internalTexture 的access::write作为输出纹理,然后self.internalTexture作为处理过的纹理作为参数传到渲染管线,输出到屏幕上,我们知道kernel函数的返回值必须是void,那为什么MetalPetal的计算方法会 有返回值呢?
      这里是第二个要注意的点,就是MetalPetal的applyToInputImages的参数问题,在MetalPetal里 计算管线的输出直接用一个MTIImageComputeRecipe接收然后[[MTIImage alloc] initWithPromise:receipt]作为一个MTIImage的返回值输出,看下metal里的计算方法
      kernel void
      normalKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
      texture2d<float, access::write> destTexture [[texture(1)]],
      uint2 grid [[thread_position_in_grid]])
      {
      float4 color = sourceTexture.read(grid);
      color.r += 0.5;
      destTexture.write(color, grid);
      }
      这里sourceTexture对应_inputImage,而destTexture是空的,是不需要外界传递的,所以在使用MetalPetal时直接输入一个纹理然后外界接收到的MTIImage就相当于internalTexture了,这个outImage就可以像internalTexture一样用于下面的渲染管线的渲染了

    MTIVertices *geometry = [[MTIVertices alloc] initWithVertices:(MTIVertex []){ { .position = {1, -1, 0.0, 1.0} , .textureCoordinate = { 1.f, 1.f } }, { .position = {-1, -1, 0.0, 1.0 } , .textureCoordinate = { 0.f, 1.f } }, { .position = {-1, 1, 0.0, 1.0} , .textureCoordinate = { 0.f, 0.f } }, { .position = {1, -1, 0.0, 1.0} , .textureCoordinate = { 1.f, 1.f } },{ .position = {-1, 1, 0.0, 1.0} , .textureCoordinate = { 0.f, 0.f } },{ .position = {1, 1, 0.0, 1.0} , .textureCoordinate = { 1.f, 0.f } } } count:6 primitiveType:MTLPrimitiveTypeTriangleStrip];
    //
    // //定点数据和纹理数据都传递给GPU

    MTIRenderCommand *command = [[MTIRenderCommand alloc] initWithKernel:_customRenderKernel geometry:geometry images:@[outImage] parameters:@{}];
    MTIRenderPassOutputDescriptor *outputDescriptor = [[MTIRenderPassOutputDescriptor alloc] initWithDimensions:MTITextureDimensionsMake2DFromCGSize(_outputImage.size) pixelFormat:_renderView.colorPixelFormat];
    self.renderView.image = [MTIRenderCommand imagesByPerformingRenderCommands:@[command]
    outputDescriptors:@[outputDescriptor]].firstObject;

    这里geometry是顶点数据跟Metal原生的格式一样的

    这里只是最基本的自定义metal的实现,如果想做更多的自定义功能,比如多重纹理什么的,还是要对metal原生框架有过深入了解,作为初学者,我会不断记录遇到的问题,以便日后随时查看

    相关文章

      网友评论

        本文标题:MetalPetal使用自定义Metal文件的注意事项

        本文链接:https://www.haomeiwen.com/subject/dggdlltx.html