在 iOS 上,您可以将多个 CIFilter 添加到 SpriteKit 节点吗?

On iOS, can you add multiple CIFilters to a SpriteKit node?

在 iOS 上,你可以将多个 CIFilter 添加到 SKEffectsNode 吗?

CIFilterGenerator 似乎是我想要的,但它在 iOS 上不可用。

我知道您可以使用 multiple filters on an image 将一个节点的输出作为下一个节点的输入传递,但是如果您想影响非图像节点,那将无济于事。

这是否意味着我必须创建一个人为的 SKEffectNode 层次结构并为每个层次结构添加一个过滤器,并将我的实际内容放在最底部?有没有更好的方法?

很难或不可能 "chain" 一起调用多个 CIFilter 来达到预期的效果 - 可能是因为 class 有一个 属性,一个解决这个问题的方法是执行以下操作:

  • Subclass CIFilter,覆盖你需要的一切。这可能包括 attributessetValue(forKey:),最重要的是,outputImage
  • Subclass CIFilterConstructor,并创建一个 registerFilter() 方法。

例如,假设您希望结合高斯模糊,然后向图像添加单色红色调。在最基本的情况下,您可以这样做:

class BlurThenColor:CIFilter {

    let blurFilter = CIFilter(name: "CIGaussianBlur")

    override public var attributes: [String : Any] {
        return [
            kCIAttributeFilterDisplayName: "Blur then Color",

            "inputImage": [kCIAttributeIdentity: 0,
                           kCIAttributeClass: "CIImage",
                           kCIAttributeDisplayName: "Image",
                           kCIAttributeType: kCIAttributeTypeImage]
        ]
    }
    override init() {
        super.init()
    }
    @available(*, unavailable) required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override public func setValue(_ value: Any?, forKey key: String) {
        switch key {
        case "inputImage":
            blurFilter?.setValue(inputImage, forKey: "inputImage")
        default:
            break
        }
    }
    override public var  outputImage: CIImage {
        return (blurFilter?.outputImage)! .applyingFilter("CIColorMonochrome", parameters: ["inputColor": CIColor(red: 1.0, green: 0.0, blue: 0.0)])
    }
}

如果您希望公开更多属性,您只需将它们添加到 attributessetValue(forKey:) 覆盖以及添加变量和 setDefaults。这里我只是使用默认值。

现在您已经将您的效果链接到一个自定义过滤器中,您可以注册并使用它:

let CustomFilterCategory = "CustomFilter"

public class CustomFilterConstructor: NSObject, CIFilterConstructor {
    static public func registerFilter() {
        CIFilter.registerName(
            "BlurThenColor",
            constructor: CustomFilterConstructor(),
            classAttributes: [
                kCIAttributeFilterCategories: [CustomFilterCategory]
            ])
    }
    public func filter(withName name: String) -> CIFilter? {
        switch name {
        case "BlurThenColor":
            return BlurThenColor()
        default:
            return nil
        }
    }
}

要使用它,请务必注册过滤器(如果可能,我倾向于将我的过滤器放在 AppDelegate 中):

CustomFilterConstructor.registerFilter()

从那里,您可以像使用其他 CIFilter 一样使用 BlurThenColor。实例化它,使用 setValue,然后调用 outputImage.

请注意,此代码 inputImage and/or 拼写错误的强制展开而崩溃。我相信你可以让它更安全——但请放心,我已经测试过它并且它有效。 (我创建了这个自定义过滤器,并在一个不会发生强制展开的应用程序中替换了它。)

根据 dfd 的有用建议,我最终选择了这个简单的子类。我将他的回答标记为正确,因为 a) 他建议了这种方法,我想赞扬他,并且 b) 它有更多关于使用 CIFilterConstructor 注册过滤器的一般使用信息。

有用的参考: - Apple Docs - - Free Core Image eBook

class MyChainFilter: CIFilter {
    let chainedFilters: [CIFilter]
    @objc dynamic var inputImage: CIImage?

    init(filters: [CIFilter]) {
        self.chainedFilters = filters
        super.init()
    }

    // run filters in order on the specified source image
    override var outputImage: CIImage? {
        get {
            let imageKey = "inputImage"
            var workingImage = self.inputImage
            for filter in chainedFilters {
                assert(filter.inputKeys.contains(imageKey))
                filter.setValue(workingImage, forKey: imageKey)
                guard let result = filter.outputImage else {
                    assertionFailure("filter failed: \(filter.name)")
                    return nil
                }
                workingImage = result
            }
            return workingImage
        }
    }

    required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}