从 MTKView 创建的 UIImage 导致颜色/不透明度差异

Posted

技术标签:

【中文标题】从 MTKView 创建的 UIImage 导致颜色/不透明度差异【英文标题】:UIImage created from MTKView results in color/opacity differences 【发布时间】:2018-08-19 19:39:58 【问题描述】:

当我将 MTKView 的内容捕获到 UIImage 中时,生成的图像看起来质量不同,如下所示:

我用来生成 UIImage 的代码如下:

let kciOptions = [kCIContextWorkingColorSpace: CGColorSpace(name: CGColorSpace.sRGB)!,
                         kCIContextOutputPremultiplied: true,
                         kCIContextUseSoftwareRenderer: false] as [String : Any]
let lastDrawableDisplayed = self.currentDrawable! // needed to hold the last drawable presented to screen
drawingUIView.image = UIImage(ciImage: CIImage(mtlTexture: lastDrawableDisplayed.texture, options: kciOptions)!)

由于我不修改 ciImage 方向 (.orient(CGImagePropertyOrientation.downMirrored)),因此生成的图像是颠倒的,如上图所示。我保留镜像方向,以便指出两个图像捕获之间的颜色差异。

无论我如何更改 kciOptions 参数(例如,甚至将色彩空间更改为灰度),我都没有看到生成的 UIImage 有任何变化,它看起来比原始图像更暗淡/不饱和。有人对我如何准确地将我在 MTKView 上绘制的内容捕获到 UIImage 有任何建议吗?任何建议将不胜感激。

以下是我的 MTKView 设置,可能证明是相关的:

let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
renderPipelineDescriptor.vertexFunction = vertexProgram
renderPipelineDescriptor.sampleCount = self.sampleCount
renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm
renderPipelineDescriptor.colorAttachments[0].isBlendingEnabled = true
renderPipelineDescriptor.colorAttachments[0].rgbBlendOperation = .add
renderPipelineDescriptor.colorAttachments[0].alphaBlendOperation = .add
renderPipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = .sourceAlpha         renderPipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha            renderPipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha              renderPipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
self.isOpaque = false // makes MTKView bg transparent

【问题讨论】:

【参考方案1】:

我在几个帖子中看到过这个问题,但没有明确的答案。这是我发现的:

对于初学者来说,

renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm

应该只设置为 MTKView 的原生像素格式

renderPipelineDescriptor.colorAttachments[0].pixelFormat = self.colorPixelFormat

其次,当我设置 CIImage 的选项时:

let kciOptions = [kCIContextWorkingColorSpace: CGColorSpace(name: CGColorSpace.sRGB)!,
                         kCIContextOutputPremultiplied: true,
                         kCIContextUseSoftwareRenderer: false] as [String : Any]

我将 kCIContextWorkingColorSpace 设置为什么并不重要,无论我使用什么,我都没有看到任何视觉差异。我真正需要设置的属性叫做KCIImageColorSpace。所以更新后的 kciOptions 看起来像:

let kciOptions = [kCIImageColorSpace: CGColorSpaceCreateDeviceRGB(),
                      kCIContextOutputPremultiplied: true,
                      kCIContextUseSoftwareRenderer: false] as [String : Any]

在使用视图的本机像素格式的类似方式中,调用 CGColorSpaceCreateDeviceRGB() 创建特定于正在使用的设备的 RGB 颜色空间。

【讨论】:

非常感谢您分享这个! 添加色彩空间选项对我不起作用。另外现在不允许添加CIContextOption。 在 CIImage 为我工作之前转换纹理的格式。比如:let tex = drawable!.texture.makeTextureView(pixelFormat: .bgra8Unorm_srgb)! 谢谢,我也在做一个绘图引擎,你提出的所有问题都有很大帮助【参考方案2】:

您的CGColorSpace.sRGB,但您的renderPipelineDescriptor 的pixelFormat 是.bgra8Unorm。尝试将该行更改为:

renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormat.bgra8Unorm_srgb

【讨论】:

谢谢@hartw。您的回答几乎是造成色差的原因。在经历了很多心痛之后,我发现了一个微妙的发现,我将在下面发布。再次感谢您【参考方案3】:
let context = CIContext()
let texture = metalView.currentDrawable!.texture
let cImg = CIImage(mtlTexture: texture, options: nil)!
let cgImg = context.createCGImage(cImg, from: cImg.extent)!
let uiImg = UIImage(cgImage: cgImg)

【讨论】:

如果您能解释为什么此代码有效,您的答案会更好,但感谢您的贡献!

以上是关于从 MTKView 创建的 UIImage 导致颜色/不透明度差异的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MTKView 中使用多重采样?

从 CMSampleBufferRef 转换的 UIImage,导致 UIImage 无法正确渲染

MTKView 绘图性能

从 UIImage 读取像素会导致 BAD_ACCESS

显示 MTKView 的当前帧率

将 Metal MTKView 实时捕获为电影?