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;
当我尝试与 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;