我最近尝试创建一个透明的
MTKView
,我可以在其中绘制一些几何图形,包括部分透明的几何图形,并将结果与该MTKView
后面的任何视图正确合成。我在尝试实现此目标时遇到了两个问题:
我创建了一个测试用例,它演示了“至少”第一个问题,也可能是第二个问题。其中,混合设置为:
colorAttachmentDescriptor.isBlendingEnabled = true
colorAttachmentDescriptor.pixelFormat = .bgra8Unorm_srgb
colorAttachmentDescriptor.rgbBlendOperation = .add
colorAttachmentDescriptor.alphaBlendOperation = .add
colorAttachmentDescriptor.sourceRGBBlendFactor = .sourceAlpha
colorAttachmentDescriptor.sourceAlphaBlendFactor = .sourceAlpha
colorAttachmentDescriptor.destinationRGBBlendFactor = .oneMinusSourceAlpha
colorAttachmentDescriptor.destinationAlphaBlendFactor = .oneMinusSourceAlpha
(我认为)这通常是我在网上看到的推荐。
我的测试用例在不同颜色的网格顶部渲染三个三角形。按照绘制顺序,首先渲染白色 (
float4(1, 1, 1, 1)
) 三角形,然后渲染蓝色 (
float4(0, 0, 1, 0.5)
),最后渲染红色 (float4(1, 0, 0, 0.5)
)。所有通道的 MTKView
的清晰颜色设置为 0。左边是我从测试用例中得到的输出,右边是我期望的输出(在插图程序中绘制):
如您所见,部分透明的三角形消除了不透明的白色三角形的不透明度。此外,我在左侧绘制的三角形看起来比应有的透明得多,对像素进行采样也证实了这一点。例如,我期望黑色瓷砖和蓝色三角形重叠的像素为
float3(0, 0, 0.5)
,但它看起来更接近
float3(0, 0, ~0.737)
。多重采样也发生了一些奇怪的事情:白色三角形与白色瓷砖相交并且只有一个透明三角形相交,似乎根本没有进行任何多重采样。
我在
这个答案中看到核心动画期望视图使用预乘alpha,并且确实将MTKView
的清晰颜色设置为
float4(1, 0, 0, 1)
确实给场景带来了微红的色调,而使用直接alpha我希望在那里没有区别。然而我担心的是,我有部分透明的几何体与不透明的几何体重叠,但同时什么也没有——从片段着色器输出预乘 alpha 的解决方案可以处理这种情况吗?也许通过调整混合模式参数?多重采样怎么样,它似乎无法解决使用预乘 alpha 的问题?我的测试用例可以在这里找到:https://github.com/Aquilosion/metal-composite
不要使用
srgb
MTKView
的像素格式和颜色附件描述符更改为
bgra8Unorm
(从 bgra8Unorm_srgb
)。我还在原始项目中对纹理进行了相同的更改。我认为这个特定的变化是让多重采样正确混合的原因。为什么这有帮助?我的猜测是,如果帧缓冲区的输出格式设置为线性 RGB(而不是 sRGB),则在绘制场景时混合计算会更加正确。由于屏幕上的结果似乎仍然是正确的 sRGB,我猜想 Core Animation 之后会将非 sRGB 场景转换为 sRGB(或屏幕正在使用的其他任何内容)以进行补偿。
不要使用预乘 alpha
(请注意,将 RGB 的透明颜色设置为非 0 值,但 0 alpha 似乎意味着它
确实需要预乘 alpha,所以我不能 100% 确定这一点,除了正在进行的混合确实反正看起来很准确。我想只要你的clear color全为0,就应该是准确的。) 调整混合参数
.isBlendingEnabled = true
.rgbBlendOperation = .add
.alphaBlendOperation = .add
.sourceRGBBlendFactor = .sourceAlpha
.sourceAlphaBlendFactor = .one
.destinationRGBBlendFactor = .oneMinusSourceAlpha
.destinationAlphaBlendFactor = .oneMinusSourceAlpha
我不知道为什么我在问题中包含的混合参数如此受欢迎,因为这些参数产生了更正确的结果,大多数人除了阿尔法混合之外将如何工作(我曾想过!)