如何用CIFilter实现SKSpriteNode着色效果适配SKEffectNode?
用CIFilter复现SKSpriteNode着色效果适配SKEffectNode
我刚好深入研究过SpriteKit的着色机制,你想要用CIFilter复现SKSpriteNode的着色效果来适配SKEffectNode的需求,完全可以通过匹配你现有CG实现的滤镜链来完成——而且这样能彻底避开CGImage和CIImage的兼容性问题。
首先,先拆解你提供的CG代码逻辑,它的核心步骤对应到Core Image的滤镜组合是完全可行的:
- 用颜色混合模式给原图叠加带透明度的着色色值
- 保留原图的Alpha通道,确保透明区域不受着色影响
对应CIFilter的滤镜链实现
下面是完全匹配SKSpriteNode着色逻辑的Core Image滤镜链,我会逐段解释:
1. 封装滤镜链函数
可以把这个逻辑封装成一个可复用的函数,直接生成能给SKEffectNode使用的CIFilter:
func createSpriteTintFilter(tintColor: UIColor, blendFactor: CGFloat) -> CIFilter { // 1. 创建带透明度的着色纯色图(对应你CG代码中设置的color.withAlphaComponent(colorBlendFactor)) let adjustedTint = tintColor.withAlphaComponent(blendFactor) let colorBackground = CIImage(color: CIColor(color: adjustedTint)) // 2. 颜色混合滤镜:对应CG中的.blendMode(.color),保留原图亮度/饱和度,替换色相 let colorBlendFilter = CIFilter(name: "CIColorBlendMode")! colorBlendFilter.setValue(colorBackground, forKey: kCIInputBackgroundImageKey) // 3. 保留Alpha通道滤镜:对应CG中的.blendMode(.destinationIn),确保原图透明区域不变 let preserveAlphaFilter = CIFilter(name: "CIDestinationInCompositing")! // 4. 组合滤镜链 let filterChain = CIFilter(name: "CIFilterChain")! filterChain.setValue([colorBlendFilter, preserveAlphaFilter], forKey: "inputFilters") // 设置滤镜间的输入关联:第二个滤镜的inputImage取第一个滤镜的输出 filterChain.setValue(["inputImage": kCIInputImageKey], forKey: "inputParameters") return filterChain }
2. 在SKEffectNode中使用
生成滤镜后,直接赋值给SKEffectNode的filter属性即可:
// 创建并配置SKEffectNode let effectNode = SKEffectNode() effectNode.shouldEnableEffects = true // 应用着色滤镜(比如红色,混合度0.5) effectNode.filter = createSpriteTintFilter(tintColor: .red, blendFactor: 0.5) // 添加需要着色的子节点(比如SKLabelNode、SKSpriteNode等) let label = SKLabelNode(text: "Hello SpriteKit") effectNode.addChild(label) // 将effectNode添加到场景 addChild(effectNode)
为什么这个滤镜链和SKSpriteNode效果一致?
- CIColorBlendMode的
color模式和SpriteKit/SKView底层使用的CG混合模式完全一致:只替换原图的色相,保留亮度和饱和度,同时着色颜色的透明度由blendFactor控制。 - CIDestinationInCompositing滤镜的作用是取混合后的颜色,同时保留原图的Alpha通道,完美对应你CG代码中最后一步用
.destinationIn绘制原图的逻辑,确保透明区域不会被着色填充。
替代方案:手动链接滤镜(不使用CIFilterChain)
如果你不想用CIFilterChain,也可以手动链接滤镜步骤,逻辑是一样的:
func applySpriteTint(to inputImage: CIImage, tintColor: UIColor, blendFactor: CGFloat) -> CIImage { let adjustedTint = tintColor.withAlphaComponent(blendFactor) let colorBackground = CIImage(color: CIColor(color: adjustedTint)) // 颜色混合 guard let colorBlend = CIFilter(name: "CIColorBlendMode") else { return inputImage } colorBlend.setValue(inputImage, forKey: kCIInputImageKey) colorBlend.setValue(colorBackground, forKey: kCIInputBackgroundImageKey) guard let blendedImage = colorBlend.outputImage else { return inputImage } // 保留Alpha通道 guard let preserveAlpha = CIFilter(name: "CIDestinationInCompositing") else { return blendedImage } preserveAlpha.setValue(blendedImage, forKey: kCIInputImageKey) preserveAlpha.setValue(inputImage, forKey: kCIInputBackgroundImageKey) return preserveAlpha.outputImage ?? blendedImage }
这个实现完全基于Core Image的标准滤镜,在iOS 8+都能稳定运行,没有CG/CIImage转换的兼容性问题,完美适配SKEffectNode及其他支持CIFilter的SKNode类型。
内容的提问来源于stack exchange,提问作者John Difool
相关产品推荐
相关产品推荐

