查看一些默认 CIFilter 的实现?
See implementation of some default CIFilter's?
更新:根据@FrankSchlegel 回答主要问题 - 不,无法检查系统 CIFilter
的工作方式。
是否可以查看一些 CIFilter
默认过滤器是如何实现的,例如 CIDissolveTransition
或 CISwipeTransition
?我想构建一些自定义转换过滤器,并希望在可能的情况下查看一些 Metal Shading Language
示例。在互联网上真的找不到任何用 MSL 完成的转换过滤器的例子,只能在常规的 Metal 管道中找到。
更新 1:
这是我希望移植到 MSL 的淡入淡出过滤器的示例:
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
struct TwoInputVertexIO
{
float4 position [[position]];
float2 textureCoordinate [[user(texturecoord)]];
float2 textureCoordinate2 [[user(texturecoord2)]];
};
typedef struct
{
float tweenFactor;
} FadeTransitionUniform;
fragment half4 fadeTransition(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant FadeTransitionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler;
half4 textureColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
half4 textureColor2 = inputTexture2.sample(quadSampler, fragmentInput.textureCoordinate2);
return half4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * half(uniform.tweenFactor)), textureColor.a);
}
这是我移植它的失败尝试:
float4 fadeTransition(sampler fromTexture, sampler toTexture, float time) {
float2 uv = fromTexture.coord();
float4 textureColor = fromTexture.sample(uv);
float4 textureColor2 = toTexture.sample(uv);
return float4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * float(time)), textureColor.a);
}
目前这似乎没有做任何事情,我认为我没有添加 constexpr sampler quadSampler
等价物,我错过了一些东西。
更新二:
下面是我如何初始化 CIKernel
:
import CoreImage
class TestTransitionFilter: CIFilter {
private let kernel: CIKernel
@objc dynamic var inputImage: CIImage?
@objc dynamic var inputImage2: CIImage?
@objc dynamic var inputTime: CGFloat = 0
override init() {
let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
let data = try! Data(contentsOf: url)
kernel = try! CIKernel(functionName: "fadeTransition", fromMetalLibraryData: data)
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func outputImage() -> CIImage? {
guard let inputImage = inputImage else {return nil}
guard let inputImage2 = inputImage2 else {return nil}
return kernel.apply(extent: inputImage.extent, roiCallback: {
(index, rect) in
return rect.insetBy(dx: -1, dy: -1)
}, arguments: [inputImage, inputImage2, inputTime])
}
}
我是这样调用它的:
public class TestTransition: NoneTransition {
override public func renderImage(foregroundImage: CIImage, backgroundImage: CIImage, forTweenFactor tween: Float64, renderSize: CGSize) -> CIImage {
// This DOES work
/*
if let crossDissolveFilter = CIFilter(name: "CIFlashTransition") {
crossDissolveFilter.setValue(backgroundImage, forKey: "inputImage")
crossDissolveFilter.setValue(CIVector(x: backgroundImage.extent.width / 2.0, y: backgroundImage.extent.height / 2.0), forKey: "inputCenter")
crossDissolveFilter.setValue(foregroundImage, forKey: "inputTargetImage")
crossDissolveFilter.setValue(tween, forKey: "inputTime")
if let outputImage = crossDissolveFilter.outputImage {
return outputImage
}
}*/
// This DOESN'T work
let filter = TestTransitionFilter()
filter.inputImage = backgroundImage
filter.inputImage2 = foregroundImage
filter.inputTime = CGFloat(tween)
if let outputImage = filter.outputImage {
return outputImage
}
return super.renderImage(foregroundImage: foregroundImage, backgroundImage: backgroundImage, forTweenFactor: tween, renderSize: renderSize)
}
}
最终更新:
感觉很傻,但是过了几天我发现了错误。在我调用 CIKernel 的地方,我使用了 filter.outputImage
而不是 filter.outputImage()
因此一开始就没有效果。
虽然错误是您自己发现的,但这里是您的解决方案的补充:
您应该覆盖 CIFilter
的现有 属性 而不是 func outputImage() -> CIImage? { ... }
,因为这是获取过滤器输出的标准方式:
override var outputImage: CIImage? {
// your computation here
}
还有一个提示:对于这种内核,您可以使用 CIColorKernel
,因为您只需要对单个像素进行采样,而无需查看它们的邻居。内核将如下所示:
float4 fadeTransition(sample_t foreground, sample_t background, float time) {
return float4(mix(foreground.rgb, background.rgb, background.a * float(time)), foreground.a);
}
然后将内核初始化为CIColorKernel
而不是CIKernel
。
说到这一点,您也不需要为每个过滤器实例初始化一个新内核,而是使用一个静态内核。 (这是我在 WWDC 实验室期间与 Apple 工程师交谈时推荐的)。这就是整个过滤器的样子:
class TestTransitionFilter: CIFilter {
@objc dynamic var inputImage: CIImage?
@objc dynamic var inputImage2: CIImage?
@objc dynamic var inputTime: CGFloat = 0
private static var kernel: CIColorKernel {
let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
let data = try! Data(contentsOf: url)
return try! CIColorKernel(functionName: "fadeTransition", fromMetalLibraryData: data)
}
override var outputImage: CIImage? {
guard let inputImage = inputImage, let inputImage2 = inputImage2 else {return nil}
return Self.kernel.apply(extent: inputImage.extent, arguments: [inputImage, inputImage2, inputTime])
}
}
更新:根据@FrankSchlegel 回答主要问题 - 不,无法检查系统 CIFilter
的工作方式。
是否可以查看一些 CIFilter
默认过滤器是如何实现的,例如 CIDissolveTransition
或 CISwipeTransition
?我想构建一些自定义转换过滤器,并希望在可能的情况下查看一些 Metal Shading Language
示例。在互联网上真的找不到任何用 MSL 完成的转换过滤器的例子,只能在常规的 Metal 管道中找到。
更新 1: 这是我希望移植到 MSL 的淡入淡出过滤器的示例:
#include <metal_stdlib>
using namespace metal;
#include "../../Vender/Render/Base/OperationShaderTypes.h"
struct TwoInputVertexIO
{
float4 position [[position]];
float2 textureCoordinate [[user(texturecoord)]];
float2 textureCoordinate2 [[user(texturecoord2)]];
};
typedef struct
{
float tweenFactor;
} FadeTransitionUniform;
fragment half4 fadeTransition(TwoInputVertexIO fragmentInput [[stage_in]],
texture2d<half> inputTexture [[texture(0)]],
texture2d<half> inputTexture2 [[texture(1)]],
constant FadeTransitionUniform& uniform [[ buffer(1) ]])
{
constexpr sampler quadSampler;
half4 textureColor = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
half4 textureColor2 = inputTexture2.sample(quadSampler, fragmentInput.textureCoordinate2);
return half4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * half(uniform.tweenFactor)), textureColor.a);
}
这是我移植它的失败尝试:
float4 fadeTransition(sampler fromTexture, sampler toTexture, float time) {
float2 uv = fromTexture.coord();
float4 textureColor = fromTexture.sample(uv);
float4 textureColor2 = toTexture.sample(uv);
return float4(mix(textureColor.rgb, textureColor2.rgb, textureColor2.a * float(time)), textureColor.a);
}
目前这似乎没有做任何事情,我认为我没有添加 constexpr sampler quadSampler
等价物,我错过了一些东西。
更新二:
下面是我如何初始化 CIKernel
:
import CoreImage
class TestTransitionFilter: CIFilter {
private let kernel: CIKernel
@objc dynamic var inputImage: CIImage?
@objc dynamic var inputImage2: CIImage?
@objc dynamic var inputTime: CGFloat = 0
override init() {
let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
let data = try! Data(contentsOf: url)
kernel = try! CIKernel(functionName: "fadeTransition", fromMetalLibraryData: data)
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func outputImage() -> CIImage? {
guard let inputImage = inputImage else {return nil}
guard let inputImage2 = inputImage2 else {return nil}
return kernel.apply(extent: inputImage.extent, roiCallback: {
(index, rect) in
return rect.insetBy(dx: -1, dy: -1)
}, arguments: [inputImage, inputImage2, inputTime])
}
}
我是这样调用它的:
public class TestTransition: NoneTransition {
override public func renderImage(foregroundImage: CIImage, backgroundImage: CIImage, forTweenFactor tween: Float64, renderSize: CGSize) -> CIImage {
// This DOES work
/*
if let crossDissolveFilter = CIFilter(name: "CIFlashTransition") {
crossDissolveFilter.setValue(backgroundImage, forKey: "inputImage")
crossDissolveFilter.setValue(CIVector(x: backgroundImage.extent.width / 2.0, y: backgroundImage.extent.height / 2.0), forKey: "inputCenter")
crossDissolveFilter.setValue(foregroundImage, forKey: "inputTargetImage")
crossDissolveFilter.setValue(tween, forKey: "inputTime")
if let outputImage = crossDissolveFilter.outputImage {
return outputImage
}
}*/
// This DOESN'T work
let filter = TestTransitionFilter()
filter.inputImage = backgroundImage
filter.inputImage2 = foregroundImage
filter.inputTime = CGFloat(tween)
if let outputImage = filter.outputImage {
return outputImage
}
return super.renderImage(foregroundImage: foregroundImage, backgroundImage: backgroundImage, forTweenFactor: tween, renderSize: renderSize)
}
}
最终更新:
感觉很傻,但是过了几天我发现了错误。在我调用 CIKernel 的地方,我使用了 filter.outputImage
而不是 filter.outputImage()
因此一开始就没有效果。
虽然错误是您自己发现的,但这里是您的解决方案的补充:
您应该覆盖 CIFilter
的现有 属性 而不是 func outputImage() -> CIImage? { ... }
,因为这是获取过滤器输出的标准方式:
override var outputImage: CIImage? {
// your computation here
}
还有一个提示:对于这种内核,您可以使用 CIColorKernel
,因为您只需要对单个像素进行采样,而无需查看它们的邻居。内核将如下所示:
float4 fadeTransition(sample_t foreground, sample_t background, float time) {
return float4(mix(foreground.rgb, background.rgb, background.a * float(time)), foreground.a);
}
然后将内核初始化为CIColorKernel
而不是CIKernel
。
说到这一点,您也不需要为每个过滤器实例初始化一个新内核,而是使用一个静态内核。 (这是我在 WWDC 实验室期间与 Apple 工程师交谈时推荐的)。这就是整个过滤器的样子:
class TestTransitionFilter: CIFilter {
@objc dynamic var inputImage: CIImage?
@objc dynamic var inputImage2: CIImage?
@objc dynamic var inputTime: CGFloat = 0
private static var kernel: CIColorKernel {
let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
let data = try! Data(contentsOf: url)
return try! CIColorKernel(functionName: "fadeTransition", fromMetalLibraryData: data)
}
override var outputImage: CIImage? {
guard let inputImage = inputImage, let inputImage2 = inputImage2 else {return nil}
return Self.kernel.apply(extent: inputImage.extent, arguments: [inputImage, inputImage2, inputTime])
}
}