【问题标题】:how to display MTKView with rgba16Float MTLPixelFormat如何使用 rgba16Float MTLPixelFormat 显示 MTKView
【发布时间】:2019-03-27 18:57:06
【问题描述】:

我有一个 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 上对此进行了测试。相同的行为。任何帮助将不胜感激。

【问题讨论】:

  • 因此,您应该做的第一件事是从处理链中删除 CoreImage,以确保您实际上将正确的颜色值输入到 MKTView 中。一种简单的方法是通过 MKTView 中的 Metal 直接使用硬编码的清晰颜色。我建议你先让它与 sRGB 一起工作,然后尝试将工作代码转换为支持线性 16 位颜色。
  • 我认为MTKView 在内部将颜色空间转换为colorspace 属性中设置的颜色空间,因此您没有得到想要的结果。
  • 我不知道swift,但通常类似的问题是由于:颜色空间:Imageview可能会将数据显示为sRGB(更明智的选择,如果没有颜色配置文件),但你选择CGColorSpaceCreateDeviceRGB ,现在 [现代机器] 可以是 DCI-P3 而不是 sRGB。第二:线性/vs伽玛校正。浮点数通常用于线性空间。
  • 谢谢@MoDJ。非常明智的建议。将 CoreImage 排除在等式之外只会让我得到 brighter 颜色。听起来我需要提供 corrected UIColors,当显示时,它将与原始颜色匹配......你知道我应该应用什么样的更正吗? (数学方面)
  • sRGB 颜色是伽马编码的字节范围值,当您使用线性 16 位值定义颜色时,这些颜色不是伽马编码的(因为它们是线性的)。这可能是您看到的问题的原因。您应该做的是确保将颜色指示为 sRGB,然后将这些 sRGB 值写入使用 16 位像素存储的纹理中。 Metal 通常会为您完成转换。

标签: swift metal color-space cgcolorspace mtlbuffer


【解决方案1】:

所以,看起来您已经非常接近完整的解决方案了。但是你所拥有的并不完全正确。这是一个 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;
}

【讨论】:

  • 谢谢@MoDJ!作为测试,我已经在 swift 方面实现了这个函数的一个版本,并将我写入的纹理更改为 MTLPixelFormat.bgra8Unorm_srgb 。我从一开始就没有看到任何明显的差异,但我还没有进行详尽的测试。我假设这种线性化需要单独应用于 r,g,b,a,对吗?
  • 是的,R、G、B 需要分别通过非线性转线性函数发送。如果您只转换一个像素的值,那么运行时性能将不是问题。如果要转换图像中的所有像素,出于性能原因,应该在 Metal 中完成。有关此函数来源的更多背景信息:stackoverflow.com/questions/53911662/…
  • 只是为了澄清,R,G,B ONLY?如果我不适用于 A,那么我的不透明度设置似乎不太正确。
  • 是的,您最初描述了一种不透明的颜色,所以我认为您仍在这样做。您也不应该尝试将可变不透明度(alpha 通道)预乘法混入其中,您已经有足够的复杂性来处理。在处理字节值时,将 alpha 1.0 保留在 metal 或 0xFF 中。
  • 您对复杂性的看法是正确的...大致上,我的应用程序是这样工作的:我选择一种颜色,根据铅笔压力分配不透明度,将 RGBA 传递给顶点着色器,然后将该颜色乘以邮票纹理(带 alpha)。结果是(希望)优雅纹理的笔触......顺便说一句,我确实注意到将 MTLTextureDescriptor 设置为 bgra8Unorm_srgb 会导致我的 png 邮票纹理中的边缘振铃(它会乘以相关颜色)。一旦我切换回 bgra8Unorm,这个工件就会消失。我会继续修修补补...谢谢,感谢您的所有见解。
【解决方案2】:

关键在于撤消固有地嵌入在 UIColor 中的 gamma 校正

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 应用到具有MTLPixelFormat.rgba16Float 的MKTView 中,结果将匹配显示colorSRGB 的UIView。一旦你仔细考虑它就会有意义......感谢@MoDJ引导我走上正确的道路。

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

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

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

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 的原始帖子中所做的那样

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-25
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 2019-08-12
    • 2018-11-12
    • 2017-12-30
    • 2019-03-21
    相关资源
    最近更新 更多