在 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
,覆盖你需要的一切。这可能包括 attributes
、setValue(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)])
}
}
如果您希望公开更多属性,您只需将它们添加到 attributes
和 setValue(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") }
}
在 iOS 上,你可以将多个 CIFilter
添加到 SKEffectsNode
吗?
CIFilterGenerator
似乎是我想要的,但它在 iOS 上不可用。
我知道您可以使用 multiple filters on an image 将一个节点的输出作为下一个节点的输入传递,但是如果您想影响非图像节点,那将无济于事。
这是否意味着我必须创建一个人为的 SKEffectNode
层次结构并为每个层次结构添加一个过滤器,并将我的实际内容放在最底部?有没有更好的方法?
很难或不可能 "chain" 一起调用多个 CIFilter
来达到预期的效果 - 可能是因为 class 有一个 属性,一个解决这个问题的方法是执行以下操作:
- Subclass
CIFilter
,覆盖你需要的一切。这可能包括attributes
、setValue(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)])
}
}
如果您希望公开更多属性,您只需将它们添加到 attributes
和 setValue(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
-
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") }
}