0.5 以下的金属混合 alpha

Metal blending alpha under 0.5

当我尝试与 alpha 为 0.5 或以下的颜色混合时,金属似乎会丢弃该颜色,就像它的 alpha 为 0 一样。当我将 alpha 设置为 0.51 时,我可以很好地看到它。当我将它设置为 0.5 时,它是不可见的。这是问题的简单实现:

@implementation Renderer
{
    id <MTLDevice> _device;
    id <MTLCommandQueue> _commandQueue;

    id<MTLLibrary> defaultLibrary;
    id <MTLBuffer> _vertexBuffer;
    id <MTLBuffer> _indexBuffer;
    
    id <MTLRenderPipelineState> _pipelineState;
}

-(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view;
{
    self = [super init];
    if(self)
    {
        _device = view.device;
        view.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;

        _commandQueue = [_device newCommandQueue];
        [self _createRenderObject];
    }

    return self;
}

-(void)_createRenderObject
{
    Vertex verts[4] = {
        simd_make_float2(-0.5f, -0.5f),
        simd_make_float2(0.5f,-0.5f),
        simd_make_float2(0.5f,0.5f)
    };

    uint16_t indices[3] = {0,1,2};

    _vertexBuffer = [_device newBufferWithBytes:&verts length:sizeof(verts) options:MTLResourceStorageModeShared];

    _indexBuffer = [_device newBufferWithBytes:&indices length:sizeof(indices) options:MTLResourceStorageModeShared];
    
    // Create Pipeline State
    defaultLibrary = [_device newDefaultLibrary];
    MTLRenderPipelineDescriptor *pd = [[MTLRenderPipelineDescriptor alloc] init];

    pd.vertexFunction = [defaultLibrary newFunctionWithName: @"VertShader"];
    pd.fragmentFunction = [defaultLibrary newFunctionWithName: @"FragShader"];
    pd.alphaToCoverageEnabled = YES;
    
    MTLRenderPipelineColorAttachmentDescriptor *cad = pd.colorAttachments[0];
    cad.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
    cad.blendingEnabled = YES;
    cad.alphaBlendOperation = MTLBlendOperationAdd;
    cad.sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha;
    cad.destinationAlphaBlendFactor = MTLBlendFactorDestinationAlpha;
    cad.rgbBlendOperation = MTLBlendOperationAdd;
    cad.sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
    cad.destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;

    NSError *error = NULL;
    _pipelineState = [_device newRenderPipelineStateWithDescriptor:pd error:&error];
}

- (void)drawInMTKView:(nonnull MTKView *)view
{
    id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

    MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;

    id <MTLRenderCommandEncoder> renderEncoder =
    [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

    [renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise];
    [renderEncoder setCullMode:MTLCullModeBack];
    [renderEncoder setRenderPipelineState:_pipelineState];

    [renderEncoder setVertexBuffer:_vertexBuffer offset:0 atIndex:0];

    [renderEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
                                  indexCount:3
                                   indexType:MTLIndexTypeUInt16
                                 indexBuffer:_indexBuffer
                           indexBufferOffset:0];
    

    [renderEncoder endEncoding];

    [commandBuffer presentDrawable:view.currentDrawable];
    
    [commandBuffer commit];
}
@end

Shader.metal:

typedef struct {
    float4 position [[position]];
} VertexOut;

vertex VertexOut
VertShader(const uint vertexID [[vertex_id]],
            constant Vertex *vertices [[buffer(0)]])
{
    VertexOut out;
    Vertex v = vertices[vertexID];
    
    out.position = (float4){v.position.x,v.position.y,0,1};
    return out;
}

fragment half4
FragShader(VertexOut in [[stage_in]])
{
    return half4(1,1,1,0.50f);
}

使用该代码,特别是具有 0.50f 作为 alpha 值的 FragShader,我得到一个空白 canvas:

如果我将 alpha 值更改为 0.51f:

fragment half4
FragShader(VertexOut in [[stage_in]])
{
    return half4(1,1,1,0.51f);
}

然后我得到这个:

感谢任何帮助!

已解决。问题是 alphaToCoverageEnabled was set to true, while the render target texture type was NOT MTLTextureType2DMultisample。看起来这两者是协同工作的,但我无法理解它们是如何协同工作的。

如果不使用多重采样,请将 alphaToCoverageEnabled 设置为 false。

否则,请确保渲染目标的类型为 MTLTextureType2DMultisample。

如果使用 MTKView,通过在 MTKView 对象上设置 sampleCount 来设置渲染目标纹理类型:

    _view = (MTKView *)self.view;
    _view.sampleCount = 2;

和管线状态的渲染管线描述符:

    MTLRenderPipelineDescriptor *pd = [[MTLRenderPipelineDescriptor alloc] init];
    pd.sampleCount = 2;