2

我有一个 MTKView 设置为使用 MTLPixelFormat.rgba16Float。我遇到了显示问题,下图可以很好地描述这些问题:

在此处输入图像描述

因此,预期的 UIColor 会被淘汰,但仅在它显示在 MTKView 中时。当我通过 CIIMage 将可绘制纹理转换回图像以在 UIView 中显示时,我恢复了原始颜色。这是我创建该输出的方式:

let colorSpace = CGColorSpaceCreateDeviceRGB()
let kciOptions = [kCIImageColorSpace: colorSpace,
                  kCIContextOutputPremultiplied: true,
                  kCIContextUseSoftwareRenderer: false] as [String : Any]  
let strokeCIImage = CIImage(mtlTexture: metalTextureComposite, options: kciOptions)!.oriented(CGImagePropertyOrientation.downMirrored)
let imageCropCG = cicontext.createCGImage(strokeCIImage, from: bbox, format: kCIFormatABGR8, colorSpace: colorSpace) 

其他相关设置:

uiColorBrushDefault: UIColor = UIColor(red: 0.92, green: 0.79, blue: 0.18, alpha: 1.0)
self.colorPixelFormat = MTLPixelFormat.rgba16Float
renderPipelineDescriptor.colorAttachments[0].pixelFormat = self.colorPixelFormat

// below is the colorspace for the texture which is tinted with UIColor
let colorSpace = CGColorSpaceCreateDeviceRGB()
let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.rgba8Unorm, width: Int(width), height: Int(height), mipmapped: isMipmaped)
target = texDescriptor.textureType
texture = device.makeTexture(descriptor: texDescriptor)

一些帖子暗示 sRGB 被假定在某处,但没有具体说明如何禁用它。

我希望我在 MTKView 上显示的颜色与输入相匹配(无论如何都尽可能接近它),并且仍然能够将该纹理转换为我可以在 ImageView 中显示的东西。我已经在 iPad Air 和新的 iPad Pro 上对此进行了测试。相同的行为。任何帮助,将不胜感激。

4

2 回答 2

2

因此,看起来您非常接近完整的解决方案。但是你所拥有的并不完全正确。这是一个 Metal 函数,它将从 sRGB 转换为线性值,然后您可以将其写入 Metal 着色器(我仍然建议您写入 sRGB 纹理,但您也可以写入 16 位纹理)。请注意,sRGB 不是简单的 2.2 伽马曲线。

// Convert a non-linear log value to a linear value.
// Note that normV must be normalized in the range [0.0 1.0].

static inline
float sRGB_nonLinearNormToLinear(float normV)
{
  if (normV <= 0.04045f) {
    normV *= (1.0f / 12.92f);
  } else {
    const float a = 0.055f;
    const float gamma = 2.4f;
    //const float gamma = 1.0f / (1.0f / 2.4f);
    normV = (normV + a) * (1.0f / (1.0f + a));
    normV = pow(normV, gamma);
  }

  return normV;
}
于 2019-03-29T23:15:11.070 回答
0

事实证明,关键在于撤消固有地嵌入在 UIColor 中的伽马校正

let colorSRGB = UIColor(red: 0.92, green: 0.79, blue: 0.18, alpha: 1.0)
let rgbaSRGB = colorSRGB.getRGBAComponents()!
let gammapower : CGFloat = 2.2

let r = pow(rgbaSRGB.red, gammapower)
let g = pow(rgbaSRGB.green, gammapower)
let b = pow(rgbaSRGB.blue, gammapower)
let a = pow(rgbaSRGB.alpha, gammapower)

let colorNoGamma: UIColor = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: CGFloat(a))

一旦我通过colorNoGamma在 MKTView 中应用MTLPixelFormat.rgba16Float,结果将匹配 UIView 显示colorSRGB。一旦你仔细考虑它就很有意义......感谢@MoDJ引导我走上正确的道路。

请注意,在 MTKView 方面,我可以保持最初定义的纹理设置,即:

let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.rgba8Unorm, width: Int(width), height: Int(height), mipmapped: isMipmaped)
target = texDescriptor.textureType

而且,如果我想将 currentDrawable 转换为要在 UIImageView 中显示的纹理,那么我想确保我不应用任何颜色空间设置。IE:

let strokeCIImage = CIImage(mtlTexture: metalTextureComposite, options: [:])!.oriented(CGImagePropertyOrientation.downMirrored)
let imageCropCG = cicontext.createCGImage(strokeCIImage, from: box, format: kCIFormatABGR8, colorSpace: colorSpace)  // kCIFormatRGBA8 gives same result. Not sure what's proper
let layerStroke = CALayer()
layerStroke.frame = bbox
layerStroke.contents = imageCropCG

注意这个参数options: [:]意味着我没有传递色彩空间/预乘法/渲染器设置,就像我在我定义的原始帖子中所做的那样kciOptions

于 2019-03-29T18:43:55.133 回答