导读:本期聚焦于张立峰创作的《如何使用Core Image实现iOS端图像分形火焰颜色映射?》,敬请观看详情。分形火焰迭代函数系统生成的点云本身没有颜色,直接渲染只是灰白噪点。Core Image提供了自定义内核与渐变滤镜能力,可将迭代坐标的直方图统计结果映射为平滑火焰渐变。本文说明如何利用CIFilter封装颜色映射算法,把IFS迭代产生的密度场转换成红黄蓝交织的火焰视觉效果,并对比CPU绘制与GPU内核在移动设备上的帧率差异,指出常见色彩带断裂的成因与修复方式。

分形火焰(Fractal Flame)是一种基于迭代函数系统(IFS)的非线性分形绘图方法,它通过多个仿射变换与变异函数不断迭代点坐标,并将落点累积到二维直方图网格中。原始算法只记录每个像素被命中次数,渲染出来是单调灰度图。要在iOS上获得绚丽的火焰效果,必须引入颜色映射:给每次迭代分配渐变色,再按密度做色调混合。Core Image的CIFilter与CIKernel允许我们用Metal或GLSL风格代码在GPU上完成这一映射,避免主线程卡顿。

如何使用Core Image实现iOS端图像分形火焰颜色映射?

分形火焰迭代函数系统与颜色累积原理

迭代函数系统由一组变换组成,每个变换包含仿射矩阵与变异函数(如正弦、球面、漩涡)。算法从一个随机点出发,以一定概率选择某个变换计算新坐标,重复数十万次。传统灰度火焰只统计命中频次,而彩色火焰在每次迭代时,根据所选变换的预设颜色权重,将当前点的RGB值按系数累加到对应像素的颜色缓冲,同时累加频次缓冲。

最终每个像素拥有两个量:总命中次数与颜色总和。真实颜色等于颜色总和除以命中次数,再经过对数色调映射压缩动态范围。这种先累积后归一的方式能让稀疏区域也呈现平滑渐变,而不是被高频点洗白。在iOS中,如果我们用Swift在CPU端做这套统计,1080p画布迭代50万次往往需要数百毫秒,因此必须借助Core Image把映射放到GPU。

下面是一段简化的IFS迭代与颜色权重累积伪代码,用于说明数据形态。实际放入CIKernel时,我们会把直方图存为浮点纹理,在并行着色器中累加。

// 简化IFS迭代示例(CPU参考,非最终GPU内核)
struct Transform {
    var a: Float, b: Float, c: Float, d: Float, e: Float, f: Float
    var color: (r: Float, g: Float, b: Float)
    var prob: Float
}
func iterate(transforms: [Transform], iterations: Int) -> [Pixel] {
    var x = 0.0, y = 0.0
    var pixels: [Pixel] = []
    for _ in 0..<iterations {
        let t = pickTransform(transforms)
        let nx = t.a * x + t.b * y + t.c
        let ny = t.d * x + t.e * y + t.f
        x = nx; y = ny
        let px = Int((x + 1) * 500)
        let py = Int((y + 1) * 500)
        pixels[px][py].count += 1
        pixels[px][py].r += t.color.r
        pixels[px][py].g += t.color.g
        pixels[px][py].b += t.color.b
    }
    return pixels
}

基于Core Image的渐变颜色映射滤镜封装

Core Image并不直接提供分形火焰滤镜,但我们可以继承CIFilter,用CIKernel(或CIColorKernel)编写映射逻辑。常见做法是先用另一个滤镜或Metal计算管线生成密度与颜色累积纹理,再写一个色彩映射内核:输入为密度图与颜色和图,输出为映射后的RGBA。内核中读取像素的颜色和除以密度得到平均色,再用对数公式 brightness = log(1+density)/log(1+maxDensity) 做亮度缩放。

渐变本身可以用一维颜色查找纹理(Gradient LUT)实现。我们将火焰调色板(黑-红-橙-黄-白)写入一张 256x1 的 CGImage,转为CIImage输入内核。内核根据亮度值采样LUT,得到最终火焰色。这样更换调色板只需换图,不需改算法。以下代码展示CIFilter子类的内核调用框架:

import CoreImage
class FlameColorMapFilter: CIFilter {
    var inputDensity: CIImage?
    var inputColorSum: CIImage?
    var inputPalette: CIImage?
    static var kernel: CIKernel = {
        let src = """
        kernel vec4 flameMap(__sample density, __sample colorSum, __sample palette) {
            float d = density.r;
            if (d < 0.0001) return vec4(0.0);
            vec3 avg = colorSum.rgb / d;
            float bright = log(1.0 + d) / log(1.0 + 1000.0);
            vec3 col = sample(palette, vec2(bright, 0.5)).rgb;
            col *= avg;
            return vec4(col, 1.0);
        }
        """
        return CIKernel(source: src)!
    }()
    override var outputImage: CIImage? {
        guard let d = inputDensity, let c = inputColorSum, let p = inputPalette else { return nil }
        return FlameColorMapFilter.kernel.apply(extent: d.extent,
            arguments: [d, c, p])
    }
}

这种封装的好处是滤镜可串联到CIFilterChain中,前面接直方图累积滤镜,后面接CIExposureAdjust做整体提亮。在iPhone上用Metal后端跑 1024x1024 迭代纹理,映射阶段仅耗时约 3-5 毫秒,远快于CPU回读。需要注意的是,内核里除法要避免密度为零,否则出现NaN导致黑屏,务必加极小值保护。

火焰效果渲染优化与常见色彩断层排查

即使算法正确,移动端常出现火焰颜色带断裂或颗粒过重。第一类问题是迭代次数不足,密度图噪声大,对数映射后暗部出现斑驳。解决方法是提升迭代到 100 万次以上,或在累积阶段做多次子帧叠加。第二类问题是Gradient LUT精度不够,256级在平缓过渡区产生可见色阶,可改为 1024 级或改用平滑插值函数。

另一常见误区是直接在 sRGB 空间做颜色平均,导致中间调偏灰。分形火焰应在线性空间累积颜色,输出前再转 sRGB。Core Image默认工作于线性化色彩空间,但如果我们用CGColorSpaceCreateDeviceRGB创建LUT,会被当成非线性,造成火焰发黄。正确做法是明确指定 kCGColorSpaceLinearSRGB 生成调色板图。

最后,Core Image的CIContext建议复用而非每次新建,并开启allowsParallelRendering。若用SwiftUI预览,应将滤镜输出转 UIImage 放在后台调度,避免阻塞。下面代码展示上下文复用与渲染调用:

let context = CIContext(options: [.workingColorSpace: CGColorSpace(name: CGColorSpace.linearSRGB)!])
func renderFlame(density: CIImage, colorSum: CIImage, palette: CIImage) -> UIImage? {
    let filter = FlameColorMapFilter()
    filter.inputDensity = density
    filter.inputColorSum = colorSum
    filter.inputPalette = palette
    guard let out = filter.outputImage else { return nil }
    if let cg = context.createCGImage(out, from: out.extent) {
        return UIImage(cgImage: cg)
    }
    return nil
}

通过上述步骤,我们能在iOS端利用Core Image高效完成分形火焰的颜色映射与渲染。核心在于把IFS密度统计与GPU着色解耦,用累积纹理加LUT内核实现灵活火焰渐变,并注意色彩空间与数值稳定性,便可在手机上实时生成流动般的火焰分形图。

Core_Image分形火焰颜色映射修改时间:2026-08-17 08:06:15

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。