如果转换为纹理,MTLTexture 会错误地显示 UIImages

MTLTexture displays UIImages incorrectly if converted to textures

我有这个方法

- (id<MTLTexture>) createTextureFromImage:(UIImage*) image device:(id<MTLDevice>) device
{
  CGImageRef imageRef = image.CGImage;

  //  size_t width = CGImageGetWidth(imageRef);
  //  size_t height = CGImageGetHeight(imageRef);

  size_t width = self.view.frame.size.width;
  size_t height = self.view.frame.size.height;

  size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
  size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);

  CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
  CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);

  NSLog(@"%@", colorSpace);

  CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | alphaInfo;
  NSLog(@"bitmap info %u", bitmapInfo);

  CGContextRef context = CGBitmapContextCreate( NULL, width, height, bitsPerComponent, (bitsPerPixel / 8) * width, colorSpace, bitmapInfo);
  if( !context )
  {
    NSLog(@"Failed to load image, probably an unsupported texture type");
    return nil;
  }

  CGContextDrawImage( context, CGRectMake( 0, 0, width, height ), image.CGImage );

  MTLPixelFormat format = MTLPixelFormatBGRA8Unorm;

  MTLTextureDescriptor *texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
                                                                                     width:width
                                                                                    height:height
                                                                                 mipmapped:NO];
  id<MTLTexture> texture = [device newTextureWithDescriptor:texDesc];

  [texture replaceRegion:MTLRegionMake2D(0, 0, width, height)
             mipmapLevel:0
               withBytes:CGBitmapContextGetData(context)
             bytesPerRow:4 * width];

  return texture;
}

当我将图像传递给它并使用生成的纹理时,它使整个图像呈现红色调。我的 MTLRenderPipelineDescriptor 的像素格式设置为 MTLPixelFormatBGRA8Unorm,我感觉问题就出在这里。我附上了图像样本,让您了解它应该是什么,以及它显示的样子。我知道这不是图像,就好像我只是将图像直接添加到视图中一样工作正常,但这也可能是因为视图使用 RGBA8 颜色值而金属视图使用 BGRA8 颜色值。但是,如果我将像素格式更改为 RGBA8,我的应用程序会崩溃并显示警告

For color attachment 0, the render pipeline's pixelFormat (MTLPixelFormatRGBA8Unorm) does not match the framebuffer's pixelFormat (MTLPixelFormatBGRA8Unorm).

唯一的问题是我看不到我在哪里设置帧缓冲区像素格式。

谢谢!

编辑 - 这是我的图像在将其拆分为像素阵列后所经过的处理代码。

-(UIImage *) script:(NSArray*)pixelArray {

  float width = self.view.frame.size.width;
  float height = self.view.frame.size.height;

  //create drawing context
  UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), NO, 0.0f);
  CGContextRef context = UIGraphicsGetCurrentContext();

  int k = 0;
  do {
    for (int y = 0; y < height + 1; y++)
    {
      for (int x = 1; x < width; x++)
      {


        if (k < array.count){
          UIColor* pixelColor = array[k];


          CGContextSetFillColorWithColor(context,  pixelColor.CGColor);
          CGContextFillRect(context, CGRectMake(x, y, 1.0f, 1.0f));
        }
      } else {

        CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor);
        CGContextFillRect(context, CGRectMake(x, y, 1.0f, 1.0f));
      }
      k++;
    }
    k++;
  }
} while (k < 62791);

//capture resultant image
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

这里有两个不同的问题:匹配渲染管线状态和帧缓冲区之间的像素格式,以及匹配图像数据和从中创建的纹理像素格式之间的像素格式。

在第一种情况下,使用 MTLPixelFormatBGRA8Unorm 是明智的。这必须在您的 CAMetalLayer(或 MTKView)以及渲染管道状态的原色附件上设置。如果他们不同意,你会得到你提到的例外。此配置看起来像这样:

metalView.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
// and elsewhere...
pipelineStateDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;

在后一种情况下,重要的是图像数据的组件顺序与您为纹理选择的格式相匹配。您从 UIImage 获得的色彩空间是 sRGB,因此您需要指定 MTLPixelFormatRGBA8Unorm 作为纹理的像素格式(您当前在纹理创建代码中有 MTLPixelFormatBGRA8Unorm),或者交换字节手动进入红色和蓝色通道。

视图的像素格式和从图像创建的纹理不必相同。当您采样纹理和写入帧缓冲区时,Metal 会混合颜色,因此在您的着色器中,您始终可以假定 RGBA 顺序。