Swift 如何使用rgba16Float MTLPixelFormat显示MTKView

Swift 如何使用rgba16Float MTLPixelFormat显示MTKView,swift,metal,color-space,cgcolorspace,mtlbuffer,Swift,Metal,Color Space,Cgcolorspace,Mtlbuffer,我有一个MTKView集来使用MTLPixelFormat.rgba16Float。我有显示问题,可以用下图来最好地描述: 因此,预期的UIColor将被洗掉,但仅当它显示在MTKView中时。当我通过CIIMage将可绘制纹理转换回图像以在UIView中显示时,我得到了原始颜色。以下是我如何创建该输出: let colorSpace = CGColorSpaceCreateDeviceRGB() let kciOptions = [kCIImageColorSpace: colorSpace

我有一个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中显示的颜色。我已经在iPadAir和新的iPadPro上测试过了。同样的行为。任何帮助都将不胜感激。

关键在于撤销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
应用于带有
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
一样,因此,看起来您非常接近完整的解决方案。但你所说的并不完全正确。这是一个金属函数,它将从sRGB转换为线性值,然后可以写入金属着色器(我仍然建议您写入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;
}
//将非线性日志值转换为线性值。
//请注意,normV必须在[0.0 1.0]范围内标准化。
内联函数
浮动sRGB\U非线性或线性(浮动正常值)
{

如果(所以,首先要做的是将CoreImage从处理链中删除,以确保在MKTView中获得正确的颜色值。一种简单的方法是直接通过MKTView中的金属使用硬编码的清晰颜色。我建议您首先使用sRGB,然后尝试转换工作模式编码以支持线性16位颜色。我认为
MTKView
内部会将颜色空间转换为
colorspace
属性中设置的颜色空间,因此您不会得到想要的结果。我不知道swift,但通常会出现类似的问题:颜色空间:Imageview可能会将数据显示为sRGB(如果没有颜色配置文件,则更明智的选择),但您可以选择
cgColorSpaceCreateDeviceGB
,它现在是[现代机器]可以是DCI-P3而不是sRGB。第二:线性/vs gamma校正。浮点通常用于线性空间。谢谢you@MoDJ.非常明智的建议。从等式中去掉CoreImage只会让我得到更亮的颜色。听起来我需要输入校正后的UIC颜色,当显示时,将与原始颜色匹配…你知道吗我应该应用哪种校正?(数学方面)sRGB颜色是伽马编码的字节范围值,当您使用线性16位值定义颜色时,这些颜色不是伽马编码的(因为它们是线性的)。这可能是您看到的问题的原因。您应该做的是确保将颜色指示为sRGB,然后将这些sRGB值写入使用16位像素存储的纹理中。Metal通常会为您进行转换。谢谢@MoDJ!作为测试,我已经在ings,以及更改了我写入MTLPixelFormat.bgra8Unorm_srgb的纹理。我从一开始就没有看到任何明显的差异,但我还没有做详尽的测试。我假设这种线性化需要分别应用于r,g,b,a,对吗?是的,r,g,b需要通过非线性到线性函数发送。如果e仅转换一个像素的值,那么运行时性能将不会成为问题。如果要转换图像中的所有像素,出于性能原因,应使用金属进行转换。有关此函数来源的更多背景信息:仅用于澄清,R、G、B?如果我不应用于A,则我的不透明度设置似乎不太正确。是的,you最初描述了一种不透明的颜色,所以我认为这是您仍然在做的事情。您不应该尝试混合可变不透明(alpha通道)在进行乘法运算之前,您已经有足够的复杂性来处理。在处理字节值时,请将alpha 1.0保留为metal或0xFF。关于复杂性,您是对的……大致上,我的应用程序是这样工作的:我选择一种颜色,根据铅笔压力指定不透明度,将RGBA传递给顶点着色器,然后将该颜色乘以戳记纹理(使用alpha)。结果是(希望是)纹理优美的一笔……顺便说一句,我注意到将MTLTextureDescriptor设置为bgra8Unorm_srgb会导致我的png图章纹理中出现边缘振铃(这会乘以所讨论的颜色).一旦我切换回bgra8Unorm,这个工件就会消失。我会继续修补…谢谢,我感谢你的所有见解。