将 Android OpenGL 滤镜重写为 Metal(用于 CIFilter)
Rewriting an Android OpenGL filter to Metal (for CIFilter)
在 GLSL (ES) 中为我们应用程序的 Android 版本编写了数十个图像过滤器。从 iOS 12 开始,OpenGL 已弃用,CIFilter 内核必须用 Metal 编写。
我以前有一些 OpenGL 的背景,但是在 Metal 中编写 CIFilter 内核对我来说是新的。
这是其中一个过滤器。你能帮我把它翻译成 Metal 作为 CIFilter 内核吗?那会为我提供一个很好的例子,所以我可以翻译其他人。
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform float texelWidth;
uniform float texelHeight;
uniform float intensivity;
void main() {
float SIZE = 1.25 + (intensivity / 100.0)*2.0;
vec4 color;
float min = 1.0;
float max = 0.0;
float val = 0.0;
for (float x = -SIZE; x < SIZE; x++) {
for (float y = -SIZE; y < SIZE; y++) {
color = texture2D(sTexture, vTextureCoord + vec2(x * texelWidth, y * texelHeight));
val = (color.r + color.g + color.b) / 3.;
if (val > max) { max = val; } else if (val < min) { min = val; }
}
}
float range = 5. * (max - min);
gl_FragColor = vec4(pow(1. - range, SIZE * 1.5));
gl_FragColor = vec4((gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3. > 0.75 ? vec3(1.) : gl_FragColor.rgb, 1.);
}
这是试图复制您描述的过滤器的内核的 Metal 源代码:
#include <metal_stdlib>
#include <CoreImage/CoreImage.h>
using namespace metal;
extern "C" {
namespace coreimage {
float4 sketch(sampler src, float texelWidth, float texelHeight, float intensity40) {
float size = 1.25f + (intensity40 / 100.0f) * 2.0f;
float minVal = 1.0f;
float maxVal = 0.0f;
for (float x = -size; x < size; ++x) {
for (float y = -size; y < size; ++y) {
float4 color = src.sample(src.coord() + float2(x * texelWidth, y * texelHeight));
float val = (color.r + color.g + color.b) / 3.0f;
if (val > maxVal) {
maxVal = val;
} else if (val < minVal) {
minVal = val;
}
}
}
float range = 5.0f * (maxVal - minVal);
float4 outColor(pow(1.0f - range, size * 1.5f));
outColor = float4((outColor.r + outColor.g + outColor.b) / 3.0f > 0.75f ? float3(1.0f) : outColor.rgb, 1.0f);
return outColor;
}
}
}
我假设您已经熟悉 basics 如何将 Metal 着色器正确构建到可由 Core Image 加载的库中。
您可以在运行时通过加载默认的 Metal 库并请求 "sketch" 函数来实例化您的内核(名称是任意的,只要它与内核源代码匹配):
NSURL *libraryURL = [NSBundle.mainBundle URLForResource:@"default" withExtension:@"metallib"];
NSData *libraryData = [NSData dataWithContentsOfURL:libraryURL];
NSError *error;
CIKernel *kernel = [CIKernel kernelWithFunctionName:@"sketch" fromMetalLibraryData:libraryData error:&error];
然后您可以将此内核包装在您自己的 CIFilter
子类中,或者直接调用它,从而将其应用于图像:
CIImage *outputImage = [kernel applyWithExtent:CGRectMake(0, 0, width, height)
roiCallback:^CGRect(int index, CGRect destRect)
{ return destRect; }
arguments:@[inputImage, @(1.0f/width), @(1.0f/height), @(60.0f)]];
我已经尝试 select 每个参数的合理默认值(第一个应该是 CIImage
的一个实例),当然这些可以根据口味进行调整。
在 GLSL (ES) 中为我们应用程序的 Android 版本编写了数十个图像过滤器。从 iOS 12 开始,OpenGL 已弃用,CIFilter 内核必须用 Metal 编写。
我以前有一些 OpenGL 的背景,但是在 Metal 中编写 CIFilter 内核对我来说是新的。
这是其中一个过滤器。你能帮我把它翻译成 Metal 作为 CIFilter 内核吗?那会为我提供一个很好的例子,所以我可以翻译其他人。
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCoord;
uniform samplerExternalOES sTexture;
uniform float texelWidth;
uniform float texelHeight;
uniform float intensivity;
void main() {
float SIZE = 1.25 + (intensivity / 100.0)*2.0;
vec4 color;
float min = 1.0;
float max = 0.0;
float val = 0.0;
for (float x = -SIZE; x < SIZE; x++) {
for (float y = -SIZE; y < SIZE; y++) {
color = texture2D(sTexture, vTextureCoord + vec2(x * texelWidth, y * texelHeight));
val = (color.r + color.g + color.b) / 3.;
if (val > max) { max = val; } else if (val < min) { min = val; }
}
}
float range = 5. * (max - min);
gl_FragColor = vec4(pow(1. - range, SIZE * 1.5));
gl_FragColor = vec4((gl_FragColor.r + gl_FragColor.g + gl_FragColor.b) / 3. > 0.75 ? vec3(1.) : gl_FragColor.rgb, 1.);
}
这是试图复制您描述的过滤器的内核的 Metal 源代码:
#include <metal_stdlib>
#include <CoreImage/CoreImage.h>
using namespace metal;
extern "C" {
namespace coreimage {
float4 sketch(sampler src, float texelWidth, float texelHeight, float intensity40) {
float size = 1.25f + (intensity40 / 100.0f) * 2.0f;
float minVal = 1.0f;
float maxVal = 0.0f;
for (float x = -size; x < size; ++x) {
for (float y = -size; y < size; ++y) {
float4 color = src.sample(src.coord() + float2(x * texelWidth, y * texelHeight));
float val = (color.r + color.g + color.b) / 3.0f;
if (val > maxVal) {
maxVal = val;
} else if (val < minVal) {
minVal = val;
}
}
}
float range = 5.0f * (maxVal - minVal);
float4 outColor(pow(1.0f - range, size * 1.5f));
outColor = float4((outColor.r + outColor.g + outColor.b) / 3.0f > 0.75f ? float3(1.0f) : outColor.rgb, 1.0f);
return outColor;
}
}
}
我假设您已经熟悉 basics 如何将 Metal 着色器正确构建到可由 Core Image 加载的库中。
您可以在运行时通过加载默认的 Metal 库并请求 "sketch" 函数来实例化您的内核(名称是任意的,只要它与内核源代码匹配):
NSURL *libraryURL = [NSBundle.mainBundle URLForResource:@"default" withExtension:@"metallib"];
NSData *libraryData = [NSData dataWithContentsOfURL:libraryURL];
NSError *error;
CIKernel *kernel = [CIKernel kernelWithFunctionName:@"sketch" fromMetalLibraryData:libraryData error:&error];
然后您可以将此内核包装在您自己的 CIFilter
子类中,或者直接调用它,从而将其应用于图像:
CIImage *outputImage = [kernel applyWithExtent:CGRectMake(0, 0, width, height)
roiCallback:^CGRect(int index, CGRect destRect)
{ return destRect; }
arguments:@[inputImage, @(1.0f/width), @(1.0f/height), @(60.0f)]];
我已经尝试 select 每个参数的合理默认值(第一个应该是 CIImage
的一个实例),当然这些可以根据口味进行调整。