I'm using MTKView
to render some triangles. Everything works fine until I decrease the color's saturation and opacity value which makes the triangle completely transparent. Creating SwiftUI's Color with the same values shows correctly. This only happens for colors with "low" saturation, if the color has 100% saturation (like #FF0000), it still renders fine even with just 1% opacity.
I noticed if I change the colorPixelFormat
of the MTKView
, the result will change. So not sure if I only need to change the colorPixelFormat to fix this, in that case, I don't know which one either as I have limited knowledge about graphics. Here is an example for color #FF8888:
- bgra8Unorm: minimum 55% opacity for it to render
- bgra8Unorm_srgb: minimum 77% opacity for it to render and the color is a lot lighter than what it should be.
In Swift, I store the colors as [Float]
, in MSL, it will be converted to float4*
. Nothing fancy with the vertex and fragment functions, just returning the input. This is not very likely where the issue lies as other colors work.
Some code to show my setup:
// MTKView's setup
clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
isOpaque = false
layer.magnificationFilter = .nearest
layer.minificationFilter = .nearest
// State setup
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor)
// draw method setup
guard let vertexBuffer = vertexBuffer,
let indexBuffer = indexBuffer,
let indexCount = indexCount,
let colorBuffer = colorBuffer,
let pipelineState = pipelineState,
let discriptor = view.currentRenderPassDescriptor,
let commandBuffer = commandQueue.makeCommandBuffer(),
let commandEncoder = commandBuffer.makeRenderCommandEncoder(
descriptor: discriptor
),
let drawable = view.currentDrawable else {
return
}
commandEncoder.setRenderPipelineState(pipelineState)
commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
commandEncoder.setVertexBuffer(colorBuffer, offset: 0, index: 1)
commandEncoder.drawIndexedPrimitives(
type: .triangle,
indexCount: indexCount,
indexType: .uint32,
indexBuffer: indexBuffer,
indexBufferOffset: 0
)
commandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
Found this answer: https://stackoverflow.com/a/51414692/8355412
So basically, I only need to premultiply alpha to each of my rgb component