Geoffrey Wall Geoffrey Wall - 2 months ago 16
iOS Question

metal compute shader: setting alpha to zero does not yield complete transparency

I am trying to modify this guy's code:

https://github.com/FlexMonkey/MetalVideoCapture

which takes in video from the iphone / ipad camera and filters it with some Metal GPU compute kernels in real time.

I would like to alter the Metal code to do some simple chroma keying, where we make a certain color in the video transparent (i.e. for that color alpha = 0).

The problem is that the resulting video output from the metal compute kernel never reaches full transparency, even if you force alpha to zero at the end of the metal compute kernel:

outTexture.write(float4(float3(rgb), 0.0), gid);


The resultant video is only partially transparent.

Note that I am setting
layer.opaque = false
for the MTKView where the rendering is being done.

Does anyone know why this might be happening?

This is the result with alpha = 0 and a red background for the UIView:



So Columbo's answer worked for me and here is the code for my chromakey filter:

kernel void ChromaKey(texture2d<float, access::read> yTexture [[texture(0)]],
texture2d<float, access::read> cbcrTexture [[texture(1)]],
texture2d<float, access::write> outTexture [[texture(2)]],
uint2 gid [[thread_position_in_grid]])
{
float2 green = float2(54.0/255.0, 34.0/255.0);
float threshold = 0.05;
float3 colorOffset = float3(-(16.0/255.0), -0.5, -0.5);
float3x3 colorMatrix = float3x3(
float3(1.164, 1.164, 1.164),
float3(0.000, -0.392, 2.017),
float3(1.596, -0.813, 0.000)
);

uint2 cbcrCoordinates = uint2(gid.x / 2, gid.y / 2);
float y = yTexture.read(gid).r;
float2 cbcr = cbcrTexture.read(cbcrCoordinates).rg;
float alpha = smoothstep(threshold, threshold + 0.5, distance(cbcr, green));
float3 ycbcr = float3(y, cbcr);
float4 rgba = alpha * float4(colorMatrix * (ycbcr + colorOffset), 1.0);
outTexture.write(rgba, gid);
}

Answer

My guess would be that your layer is using premultiplied alpha (mentioned here). If that's the case, then you have two easy solutions:

  1. Change the layer from premultiplied alpha compositing to use non-premultiplied alpha compositing.
  2. Change the compute kernels to perform the alpha premultiplication.

To do #2 you would change something like this:

float alpha = <something_or_other>;
float3 rgb = colorMatrix * (ycbcr + colorOffset);
outTexture.write(float4(float3(rgb), alpha), gid);

To something like this:

float alpha = <something_or_other>;
float3 rgb = (colorMatrix * (ycbcr + colorOffset)) * alpha;
outTexture.write(float4(float3(rgb), alpha), gid);