Metal Camera开发2:手势划动切换滤镜

基于Metal Camera开发1:读取渲染结果生成UIImage及GPUImage的LookupFilter滤镜,本文档通过Metal compute shader对摄像头当前捕获的画面进行一左一右叠加两个滤镜,并根据手势修改滤镜的作用区域。

文档结构:

  1. 移植GPUImageLookupFilter Shader至Metal Computer Shader
  2. 手势改变滤镜作用区域
  3. 讨论:Metal Compute Shader纹理坐标及MTKTextureLoader默认生成sRGB纹理
Metal Camera开发2:手势划动切换滤镜_第1张图片
Metal Compute Shader手势划动切换滤镜操作示意

1. 移植GPUImageLookupFilter Shader至Metal Computer Shader

值得注意的是,对于OpenGL (ES),纹理采样使用归一化的坐标值,而Metal Computer Shader使用原始坐标值。移植过程中我在这一问题上花了一些时间才定位到,也怪自己没看Metal相关文档。参考代码如下。

float4 lookup(texture2d lookupTexture, float4 textureColor) {
    const float blueColor = textureColor.b * 63.0;
    
    float2 quad1;
    quad1.y = floor(floor(blueColor) / 8.0);
    quad1.x = floor(blueColor) - (quad1.y * 8.0);
    
    float2 quad2;
    quad2.y = floor(ceil(blueColor) / 8.0);
    quad2.x = ceil(blueColor) - (quad2.y * 8.0);
    
    float2 texPos1;
    texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
    texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
    
    float2 texPos2;
    texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
    texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
    
    float2 originCoord = float2(lookupTexture.get_width(), lookupTexture.get_height());
    
    texPos1 = clamp(texPos1, 0.0, 1.0);
    texPos2 = clamp(texPos2, 0.0, 1.0);
    uint2 mapTexPos1 = uint2(texPos1 * originCoord);
    uint2 mapTexPos2 = uint2(texPos2 * originCoord);
    
    const float4  newColor1 = lookupTexture.read(mapTexPos1);
    const float4  newColor2 = lookupTexture.read(mapTexPos2);
    const float4  outColor = mix(newColor1, newColor2, fract(blueColor));
    
    return outColor;
}

2. 手势改变滤镜作用区域

读取当前触摸点在视图的坐标并提供给Metal即可。读取坐标的过程中,因不熟悉Swift语法,花了些时间还是编译失败。在此感谢@江夏沐帮忙实现了CGFloat到Float的转换。

获取屏幕触摸点坐标的参考代码如下。

override func touchesMoved(_ touches: Set, with event: UIEvent?) {
    let location = touches.first?.location(in: self)
    filterPosition = Float(location?.x ?? 0) * Float(self.contentScaleFactor)
    print(filterPosition ?? "-1.0")
}

Meta Compute Shader根据手势改变滤镜作用区域的参考代码如下。

float4 inColor   = inTexture.read(gid);
float4 textureColor = clamp(inColor, 0.0, 1.0);

float4 outColor = float4(0.0);
    unsigned int pos = (unsigned int)(*filterPosition);
if (gid.x < pos) {
    outColor = lookup(leftLookupTexture, textureColor);
} else {
    outColor = lookup(rightLookupTexture, textureColor);
}

outTexture.write(outColor, gid);

3. 讨论:Metal Compute Shader纹理坐标及MTKTextureLoader默认生成sRGB纹理

3.1. Metal Compute Shader纹理坐标

第1节移植GPUImageLookupFilter Shader至Metal Computer Shader已说明计算着色器默认情况下给的是整数坐标,说明它是原始像素坐标,区别于OpenGL ES。移植GLSL时应该格外注意。

3.2. MTKTextureLoader默认生成sRGB纹理

使用MTKTextureLoader加载颜色查找表(Lookup Table)图像时,默认情况下它生成sRGB颜色范围的纹理,即使图像元数据中并不声明sRGB。如果这影响了滤镜的表现,将MTKTextureLoaderOptionSRGB设置为false让MTKTextureLoader按图像原始色彩空间加载即可,参考代码如下。

do {
    try rightLookupTexture = textureLoader?.newTexture(withContentsOf: Bundle.main.url(forResource: "lookup.png", withExtension: nil)!, options: [
         MTKTextureLoaderOptionSRGB : false as NSObject])
} catch {
    print("load right lookup png failed.")
}

你可能感兴趣的:(Metal Camera开发2:手势划动切换滤镜)