Я хотел бы переключить камеру, над которой я работаю, с GL (GPUImage) на Metal.
Я попытался выяснить, что может быть не так с шейдером Metal, который я использую, чтобы применить поисковый фильтр к MTLtexture, но у меня есть некоторые проблемы с полосами, синие линии, особенно при отображении темных градиентов.
Эти проблемы с синей полосой появляются при применении металлического шейдера, а не при использовании CoreImage. Остальная часть изображения в порядке. Вот пример использования подстановочного изображения для получения высококонтрастного изображения:
Слева: CoreImage Shader; Справа: Metal Shader (исходное изображение точно такое же для двух примеров)
Как видите, Metal кажется менее точным, чем CoreImage, и дает эти странные синие полосы.
Я хотел бы знать, нормальная ли это проблема, учитывая, что оба шейдера являются адаптацией работы GPUImage Брэда Ларсона.
Металлический шейдер:
kernel void LookupFilterKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
texture2d<float, access::read> lutTexture [[texture(2)]],
uint2 gid [[thread_position_in_grid]]){
float4 color = sourceTexture.read(gid);
float blueColor = color.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) * color.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float4 newColor1 = lutTexture.read(uint2(texPos1.x * 512 , texPos2.y * 512));
float4 newColor2 = lutTexture.read(uint2(texPos2.x * 512, texPos2.y * 512 ));
float4 newColor = mix(newColor1, newColor2, fract(blueColor));
float4 finalColor = mix(color, float4(newColor.rgb, color.w), 1.0);
outTexture.write(finalColor, gid);
}
И шейдер CoreImage:
kernel vec4 LookupFilterKernel(sampler inputImage, sampler inputLUT, float intensity) {
vec4 textureColor = sample(inputImage,samplerCoord(inputImage));
textureColor = clamp(textureColor, vec4(0.0), vec4(1.0));
float blueColor = textureColor.b * 63.0;
vec2 quad1;
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
vec2 quad2;
quad2.y = floor(ceil(blueColor) / 8.0);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
vec2 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);
vec2 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);
texPos1.y = 1.0 - texPos1.y;
texPos2.y = 1.0 - texPos2.y;
vec4 inputLUTExtent = samplerExtent(inputLUT);
vec4 newColor1 = sample(inputLUT, samplerTransform(inputLUT, texPos1 * vec2(512.0) + inputLUTExtent.xy));
vec4 newColor2 = sample(inputLUT, samplerTransform(inputLUT, texPos2 * vec2(512.0) + inputLUTExtent.xy));
vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
return mix(textureColor, vec4(newColor.rgb, textureColor.a), intensity);
}
Исходная текстура - это MTLTexture, которую я получаю от функции captureOutput камеры.
Если вы понимаете, почему у меня возникла эта проблема или вам нужно больше код / пояснения, дайте мне знать.
Большое спасибо!
РЕДАКТИРОВАТЬ 1:
Хорошо, после ответа Фрэнка я попытался исправить это гамма проблемы. Итак, я линеаризовал текстуру LUT и исходную текстуру, обновив шейдер:
static float linearTosRGB(float c) {
return powr(c, 1./2.2);
}
static float srgbToLinear(float c) {
if (c <= 0.04045)
return c / 12.92;
else
return powr((c + 0.055) / 1.055, 2.4);
}
kernel void LookupFilterKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
texture2d<float, access::read> lutTexture [[texture(2)]],
uint2 gid [[thread_position_in_grid]]){
float4 color = sourceTexture.read(gid);
color.r = srgbToLinear(color.r);
color.g = srgbToLinear(color.g);
color.b = srgbToLinear(color.b);
float blueColor = color.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) * color.r);
texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float2 texPos2;
texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);
texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);
float4 newColor1 = lutTexture.read(uint2(texPos1.x * 512 , texPos2.y * 512));
float4 newColor2 = lutTexture.read(uint2(texPos2.x * 512, texPos2.y * 512 ));
float4 newColor = mix(newColor1, newColor2, fract(blueColor));
float4 finalColor = mix(color, float4(newColor.rgb, color.w), 1.0);
finalColor.r = linearTosRGB(finalColor.r);
finalColor.g = linearTosRGB(finalColor.g);
finalColor.b = linearTosRGB(finalColor.b);
outTexture.write(finalColor, gid);
}
Текстура LUT предварительно преобразована в линейную гамму с помощью этого шейдера:
kernel void sRGBToLinearKernel(texture2d<float, access::read> sourceTexture [[texture(0)]],
texture2d<float, access::write> outTexture [[texture(1)]],
uint2 gid [[thread_position_in_grid]]){
float4 color = sourceTexture.read(gid);
color.r = srgbToLinear(color.r);
color.g = srgbToLinear(color.g);
color.b = srgbToLinear(color.b);
outTexture.write(color, gid);
}
И ... он ничего не исправил. У меня все еще есть эта странная полоса. Может, я что-то делаю не так?
EDIT 2:
Я пытался поместить формат пикселей в MTLPixelFormatRGBA16Float, но результат все равно неверный. Я понял, что если я изменил эту строку в шейдере:
float4 newColor = mix(newColor1, newColor2, fract(blueColor));
на:
float4 newColor = mix(newColor1, newColor2, 1.0);
, синие полосы исчезнут. Я предполагаю, что расчет quad1 неверен и дает эти полосы. Итак, мой вопрос: почему это происходит только в металлических шейдерах, а не в CoreImage или GPUImage?