我的参考纹理有四行四列。行分别是完全饱和的红色、绿色、蓝色和黑色。列的不透明度从100%不透明到25%不透明(按顺序为1、0.75、0.5、0.25 alpha)。
在我创建它的Pixelmator中,它看起来像这样:
如果在导出之前插入完全不透明的白色背景,它会呈现如下效果:
...然而,当我在Metal中将其贴图到一个四边形上,并在将背景清除为不透明白色(255, 255, 255, 255)后进行渲染,我得到了这个结果:
...这明显比不透明片段中应该的颜色更暗(白色背景应该“渗透”出来)。
实现细节
我将png文件作为纹理资源导入到Xcode中的我的应用程序资产目录中,并在运行时使用MTKTextureLoader
加载它。 .SRGB
选项似乎没有任何区别。
据我所知,着色器代码并没有做什么花哨的事情,但供参考:
#include <metal_stdlib>
using namespace metal;
struct Constants {
float4x4 modelViewProjection;
};
struct VertexIn {
float4 position [[ attribute(0) ]];
float2 texCoords [[ attribute(1) ]];
};
struct VertexOut {
float4 position [[position]];
float2 texCoords;
};
vertex VertexOut sprite_vertex_transform(device VertexIn *vertices [[buffer(0)]],
constant Constants &uniforms [[buffer(1)]],
uint vertexId [[vertex_id]]) {
float4 modelPosition = vertices[vertexId].position;
VertexOut out;
out.position = uniforms.modelViewProjection * modelPosition;
out.texCoords = vertices[vertexId].texCoords;
return out;
}
fragment float4 sprite_fragment_textured(VertexOut fragmentIn [[stage_in]],
texture2d<float, access::sample> tex2d [[texture(0)]],
constant Constants &uniforms [[buffer(1)]],
sampler sampler2d [[sampler(0)]]) {
float4 surfaceColor = tex2d.sample(sampler2d, fragmentIn.texCoords);
return surfaceColor;
}
在应用程序方面,我在我的渲染通道描述符上使用以下(相当标准的)混合因子和操作:
descriptor.colorAttachments[0].rgbBlendOperation = .add
descriptor.colorAttachments[0].alphaBlendOperation = .add
descriptor.colorAttachments[0].sourceRGBBlendFactor = .one
descriptor.colorAttachments[0].sourceAlphaBlendFactor = .sourceAlpha
descriptor.colorAttachments[0].destinationRGBBlendFactor = .oneMinusSourceAlpha
descriptor.colorAttachments[0].destinationAlphaBlendFactor = .oneMinusSourceAlpha
我尝试将
sourceRGBBlendFactor
从.one
更改为.sourceAlpha
,使其变得稍微暗一些。
如果我在红色背景(255, 0, 0, 255)上渲染图像,则会得到以下结果:
注意顶部一行向右逐渐变暗。它应该始终是同一种颜色,因为它正在混合具有相同RGB组件(255, 0, 0)的两种颜色。我已经将我的应用程序精简到最少,并在Github上放了一个演示项目;完整的Metal设置可以在存储库的源代码中看到。也许有些我没有提到的问题导致了这个问题,但我无法完全弄清楚是什么原因...
编辑:
如评论中@KenThomases所建议,我将MTKView
属性colorPixelFormat
的值从默认值.bgra8Unorm
更改为bgra8Unorm_srgb
,并将colorSpace
属性设置为与view.window?.colorSpace?.cgColorSpace
相同。 现在,半透明片段看起来明显不那么暗了,但颜色仍然不是预期的颜色:
(顶部行应完全“隐形”于红色背景中,从左到右。)
附录
我看到了苹果公司关于使用着色器调试器的文档,所以我决定查看我的应用程序在绘制精灵的右上角片段时片段着色器中发生了什么(这个片段应该是25%不透明度的完全饱和红色)。
有趣的是,从片段着色器返回的值(之后将应用 alpha 混合,基于颜色缓冲区的当前颜色和混合因子/函数),为[0.314, 0.0, 0.0, 0.596]
:
这个RGBA值似乎完全不受MTKTextureLoader.Option.SRGB
是true
,false
还是不存在的影响。
请注意,红色分量(0.314
)和alpha分量(0.596
)不相等,尽管(如果我没错的话)对于带预乘alpha的完全饱和的红色,它们应该是相等的。
我想这意味着我已经把问题缩小到了纹理加载阶段...?
也许我应该放弃方便的MTKTextureLoader
,自己动手处理...?
colorPixelFormat
设置为.bgra8Unorm_srgb
有帮助吗?将视图的colorspace
设置为view.window.colorSpace.cgColorSpace
有帮助吗? - Ken ThomasesMTKView
获取的值,调试器报告为.bgra8Unorm
。强制将其设置为.bgra8Unorm_srgb
会在setRenderPipelineState()
上导致崩溃。 - Nicolas Miari