如果转换为纹理,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 顺序。
- (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 顺序。