iOS: шейдеры Metal, CoreImage и GL - PullRequest
2 голосов
/ 13 июля 2020

Я хотел бы переключить камеру, над которой я работаю, с 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?

...