将OpenGL着色器转换为Metal(Swift)以在CIFilter中使用 [英] Convert OpenGL shader to Metal (Swift) to be used in CIFilter
问题描述
我对OpenGL
/Metal
还是很陌生,我想了解一些基本概念.
在我们的应用程序中,我们使用CIFilter
过滤视频.我看过2017年的WWDC
视频,解释了您可以将CIFilter
与Metal
包装在一起并将其用作常规过滤器.
我试图了解如何将OpenGL
视频效果转换为Metal
,以便可以将其用作将来效果的参考点.
I'm quite new to OpenGL
/Metal
and I'm trying to understand some fundamental concepts.
Within our app, we are using CIFilter
to filter videos. I saw a WWDC
video from 2017 explaining that you can wrap CIFilter
with Metal
and use it as a regular filter.
I'm trying to understand how to convert this OpenGL
video effect to Metal
so I can use it as a reference point for future effects.
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
float amount = sin(iTime) * 0.1;
// uv coords
vec2 uv = fragCoord / iResolution.xy;
amount *= 0.3;
float split = 1. - fract(iTime / 2.);
float scanOffset = 0.01;
vec2 uv1 = vec2(uv.x + amount, uv.y);
vec2 uv2 = vec2(uv.x, uv.y + amount);
if (uv.y > split) {
uv.x += scanOffset;
uv1.x += scanOffset;
uv2.x += scanOffset;
}
float r = texture(iChannel0, uv1).r;
float g = texture(iChannel0, uv).g;
float b = texture(iChannel0, uv2).b;
fragColor = vec4(r, g, b, 1.);
}
哪个产生:
将OpenGL
代码转换为Metal
之后,我正在使用CIFilter
包装器将其与AVPlayerItem
一起使用:
After converting the OpenGL
code to Metal
I'm using the CIFilter
wrapper to use it with AVPlayerItem
:
class MetalFilter: CIFilter {
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private let kernel: CIKernel
var inputImage: CIImage?
override init() {
let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
let data = try! Data(contentsOf: url)
kernel = try! CIKernel(functionName: "vhs", fromMetalLibraryData: data)
super.init()
}
func outputImage() -> CIImage? {
guard let inputImage = inputImage else {return nil}
let sourceSize = inputImage.extent.size
let outputImage = kernel.apply(extent: CGRect(x: 0, y: 0, width: sourceSize.width, height: sourceSize.height), roiCallback: { index, destRect in
return destRect
}, arguments: [inputImage, NSNumber(value: Float(1.0 / sourceSize.width)), NSNumber(value: Float(1.0 / sourceSize.height)), NSNumber(value: 60.0)])
return outputImage
}
}
任何帮助将不胜感激!
Any help will be highly appreciated!
推荐答案
我尝试了一下.这是内核代码:
I gave it a try. Here's the kernel code:
#include <metal_stdlib>
using namespace metal;
#include <CoreImage/CoreImage.h>
extern "C" { namespace coreimage {
float4 vhs(sampler_h src, float time, float amount) {
const float magnitude = sin(time) * 0.1 * amount;
float2 greenCoord = src.coord(); // this is alreay in relative coords; no need to devide by image size
const float split = 1.0 - fract(time / 2.0);
const float scanOffset = 0.01;
float2 redCoord = float2(greenCoord.x + magnitude, greenCoord.y);
float2 blueCoord = float2(greenCoord.x, greenCoord.y + magnitude);
if (greenCoord.y > split) {
greenCoord.x += scanOffset;
redCoord.x += scanOffset;
blueCoord.x += scanOffset;
}
float r = src.sample(redCoord).r;
float g = src.sample(greenCoord).g;
float b = src.sample(blueCoord).b;
return float4(r, g, b, 1.0);
}
}}
以下是对过滤器中的outputImage
的一些细微调整:
And here some slight adjustments to outputImage
in your filter:
override var outputImage: CIImage? {
guard let inputImage = self.inputImage else { return nil }
// could be filter parameters
let inputTime: NSNumber = 60
let inputAmount: NSNumber = 0.3
// You need to tell the kernel the region of interest of the input image,
// i.e. what region of input pixels you need to read for a given output region.
// Since you sample pixels to the right and below the center pixel, you need
// to extend the ROI accordingly.
let magnitude = CGFloat(sin(inputTime.floatValue) * 0.1 * inputAmount.floatValue)
let inputExtent = inputImage.extent
let roiCallback: CIKernelROICallback = { _, rect -> CGRect in
return CGRect(x: rect.minX, y: rect.minY,
width: rect.width + (magnitude + 0.01) * inputExtent.width, // scanOffset
height: rect.height + magnitude * inputExtent.height)
}
return self.kernel.apply(extent: inputExtent,
roiCallback: roiCallback,
arguments: [inputImage, inputTime, inputAmount])
}
这篇关于将OpenGL着色器转换为Metal(Swift)以在CIFilter中使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!