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

7

当我将MTKView的内容捕获成UIImage时,生成的图像质量会有所不同,如下所示:

MTKView与UIImageView相比

我用以下代码生成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的方向(.oriented(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
3个回答

4
我在几篇帖子中看到了这个问题,但没有明确的答案。以下是我发现的内容:
首先,
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颜色空间。

非常感谢您的分享! - Nestor
添加颜色空间选项对我没有起作用。此外,现在不允许添加CIContextOption。 - user5865651
2
在获取CIImage之前,将纹理格式转换为bgra8Unorm_srgb对我很有帮助。例如: let tex = drawable!.texture.makeTextureView(pixelFormat: .bgra8Unorm_srgb)! - user5865651
谢谢,我也在构建一个绘图引擎,你提出的所有问题都帮了我很多。 - Ebarella

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)

4
如果您能解释一下这段代码为什么有效,那么您的回答会更好,但还是谢谢您的贡献! - rst-2cv

2

你的CGColorSpace.sRGB,但是你的renderPipelineDescriptorpixelFormat.bgra8Unorm。试着将那行代码改为:

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

1
谢谢@hartw。你的回答几乎是颜色差异的原因。经过很多痛苦,我发现了一个微妙的问题,我将在下面发布。再次感谢。 - Plutovman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接