Jex_and_Izzy
Jex_and_Izzy

Reputation: 53

Metal blending alpha under 0.5

When I try to blend with a color with an alpha of 0.5 or below, metal seemingly discards the color like it has an alpha of 0. When I set the alpha to 0.51, I can see it fine. When I set it to 0.5, it's invisible. Here is a simple implementation of the issue:

@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);
}

With that code, specifically the FragShader having 0.50f as the alpha value, I get a blank canvas: triangle not visible

If I change the alpha value to 0.51f:

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

I then get this: Triangle visible

Any help is appreciated!

Upvotes: 1

Views: 191

Answers (1)

Jex_and_Izzy
Jex_and_Izzy

Reputation: 53

Solved. The problem was that alphaToCoverageEnabled was set to true, while the render target texture type was NOT MTLTextureType2DMultisample. It appears the two work in tandem, but it's beyond my understanding how.

If not using multi-sampling, set alphaToCoverageEnabled to false.

Otherwise, make sure the render target is of type MTLTextureType2DMultisample.

If using MTKView, set the render target texture type by setting the sampleCount on the MTKView object:

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

and the render pipeline descriptor of the pipeline state:

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

Upvotes: 1

Related Questions