Search code examples
iosmacostexture-mappingmetalporter-duff

In Metal what are the correct blendFactors for classic Porter-Duff "over" compositing?


In a Metal app for iOS I need to render a translucent texture attached to a simple quad. I cannot figure out the correct colorAttachment rgb and alpha blend factors.

My set up:

1) Red image created in Photoshop with 50% opacity. Saved as PNG with transparency. The image is stored in my project Assets folder.

enter image description here

2) I create a Metal texture by first creating a UIImage then using the .cgImage - CoreImage - field to extract the image data. This image is now in pre-multiplied format so classic Porter-Duff formulas can be applied. More on that later.

// load hero texture
do {

    let textureLoader = MTKTextureLoader(device: device)

    guard let image = UIImage(named:"red_translucent") else {
        fatalError("Error: Can not create UIImage")
    }

    if (image.cgImage?.alphaInfo == .premultipliedLast) {
        print("texture uses premultiplied alpha. Rock.")
    }

    heroTexture = try textureLoader.newTexture(with: image.cgImage!, options: nil)
} catch {
    fatalError("Error: Can not load texture")
}

3) Here is my rather boring texture fragment shader

fragment float4 textureFragmentShader(_Vertex_ vert [[ stage_in ]], texture2d<float> texas [[ texture(0) ]]) {

    constexpr sampler defaultSampler;

    float4 rgba = texas.sample(defaultSampler, vert.st).rgba;
    return rgba;
}

4) This is my colorAttachment blend factor settings that are the Porter-Duff "over" formula:

descriptor.colorAttachments[ 0 ].isBlendingEnabled = true

descriptor.colorAttachments[ 0 ].rgbBlendOperation = .add
descriptor.colorAttachments[ 0 ].alphaBlendOperation = .add

descriptor.colorAttachments[ 0 ].sourceRGBBlendFactor = .one
descriptor.colorAttachments[ 0 ].sourceAlphaBlendFactor = .one

descriptor.colorAttachments[ 0 ].destinationRGBBlendFactor = .oneMinusSourceAlpha
descriptor.colorAttachments[ 0 ].destinationAlphaBlendFactor = .oneMinusSourceAlpha

5) When I render a quad with this red translucent texture atop a white background the image is too dark. The incorrect rendering (right image) is rgb = (182, 127, 127). The correct image from Photoshop is rgb (255, 127, 127):

enter image description here

What are the correct blend functions?

UPDATE

If folks want to take a look at the code it is on Github here: https://github.com/turner/HelloMetal/tree/2_pass_render


Solution

  • The solution - per Warren's suggestion - was to set the MTKTextureLoaderOptionSRGB option to false.