基于Metal Camera開發(fā)1:讀取渲染結(jié)果生成UIImage及GPUImage的LookupFilter濾鏡,本文檔通過Metal compute shader對攝像頭當(dāng)前捕獲的畫面進(jìn)行一左一右疊加兩個(gè)濾鏡唉窃,并根據(jù)手勢修改濾鏡的作用區(qū)域。
文檔結(jié)構(gòu):
- 移植GPUImageLookupFilter Shader至Metal Computer Shader
- 手勢改變?yōu)V鏡作用區(qū)域
- 討論:Metal Compute Shader紋理坐標(biāo)及MTKTextureLoader默認(rèn)生成sRGB紋理
1. 移植GPUImageLookupFilter Shader至Metal Computer Shader
值得注意的是鸥咖,對于OpenGL (ES),紋理采樣使用歸一化的坐標(biāo)值,而Metal Computer Shader使用原始坐標(biāo)值蜡塌。移植過程中我在這一問題上花了一些時(shí)間才定位到蜜另,也怪自己沒看Metal相關(guān)文檔适室。參考代碼如下。
float4 lookup(texture2d<float, access::read> 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. 手勢改變?yōu)V鏡作用區(qū)域
讀取當(dāng)前觸摸點(diǎn)在視圖的坐標(biāo)并提供給Metal即可举瑰。讀取坐標(biāo)的過程中捣辆,因不熟悉Swift語法,花了些時(shí)間還是編譯失敗嘶居。在此感謝@江夏沐幫忙實(shí)現(xiàn)了CGFloat到Float的轉(zhuǎn)換罪帖。
獲取屏幕觸摸點(diǎn)坐標(biāo)的參考代碼如下。
override func touchesMoved(_ touches: Set<UITouch>, 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根據(jù)手勢改變?yōu)V鏡作用區(qū)域的參考代碼如下邮屁。
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紋理坐標(biāo)及MTKTextureLoader默認(rèn)生成sRGB紋理
3.1. Metal Compute Shader紋理坐標(biāo)
第1節(jié)移植GPUImageLookupFilter Shader至Metal Computer Shader已說明計(jì)算著色器默認(rèn)情況下給的是整數(shù)坐標(biāo)整袁,說明它是原始像素坐標(biāo),區(qū)別于OpenGL ES佑吝。移植GLSL時(shí)應(yīng)該格外注意坐昙。
3.2. MTKTextureLoader默認(rèn)生成sRGB紋理
使用MTKTextureLoader加載顏色查找表(Lookup Table)圖像時(shí),默認(rèn)情況下它生成sRGB顏色范圍的紋理芋忿,即使圖像元數(shù)據(jù)中并不聲明sRGB炸客。如果這影響了濾鏡的表現(xiàn),將MTKTextureLoaderOptionSRGB
設(shè)置為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.")
}