I added a new Scatter filter to Filterpedia yesterday. The filter mimics the effect of textured glass with a kernel that randomly offsets its sample coordinates based on a noise texture created with a CIRandomGenerator filter. Because the Core Image kernel for this filter needs to sample the noise texture to calculate an offset, it can't be implemented as a CIWarpKernel, it has to be the general version: a CIKernel

A warp kernel is the ideal solution for this, but "out-of-the-box" Core Image Kernel Language doesn't support any of the GLSL noise functions. A quick bit of surfing led me to this super simple pseudo random number generator over at Shadertoy

We can see it in action by creating a quick color kernel:


    let colorKernel = CIColorKernel(string:
        "float noise(vec2 co)" +
        "{ " +
        "    vec2 seed = vec2(sin(co.x), cos(co.y)); " +
        "    return fract(sin(dot(seed ,vec2(12.9898,78.233))) * 43758.5453); " +
        "} " +

        "kernel vec4 noiseField()" +
        "{" +
        "   float color = noise(destCoord());" +
        "   return vec4(color, color, color, 1.0); " +
        "}")

Applying the kernel:


    let extent = CGRect(x: 0, y: 0, width: 2048, height: 480)
    let noiseField = colorKernel?.applyWithExtent(extent, arguments: nil)

Produces an even noise image:



I found passing in the destCoord() directly into the calculation produced some repetition artefacts; adding the additional line to generate seed eliminates those.

Implementing the pseudo random number generator in a warp kernel to produce the scattering effect is a simple step:


    let kernel = CIWarpKernel(string:
        "float noise(vec2 co)" +
        "{ " +
        "    vec2 seed = vec2(sin(co.x), cos(co.y)); " +
        "    return fract(sin(dot(seed ,vec2(12.9898,78.233))) * 43758.5453); " +
        "} " +
            
        "kernel vec2 warpKernel(float radius)" +
        "{" +
        "   float offsetX = radius * (-1.0 + noise(destCoord()) * 2.0); " +
        "   float offsetY = radius * (-1.0 + noise(destCoord().yx) * 2.0); " +
        "   return vec2(destCoord().x + offsetX, destCoord().y + offsetY); " +
        "}"
    )

This kernel uses the coordinates of the pixel currently being calculated (destCoord()) to generate two random numbers in the range -1 through +1 multiplied by a radius argument. The return value of a warp kernel is a two component vector that specifies the coordinates in the source image that the current pixel in the destination image should sample from. Given a radius of 40 against a 640 x 640 image of the Mona Lisa, we get:



The killer question is, how much faster is this warp kernel implementation over my original general kernel version - especially considering the latter also has additional filters for generating the noise and I've added a Gaussian blur for optional smoothing. 

Well, the answer is the warp kernel implementation is actually a tiny bit slower! A small refactor to inline the random number generation and only call destCoord() once gives the warp kernel version a very slight edge in performance (at the expense of elegance):


    let kernel = CIWarpKernel(string:
        "kernel vec2 scatter(float radius)" +
        "{" +
        "    vec2 d = destCoord(); " +
        "    vec2 seed1 = vec2(sin(d.x), cos(d.y)); " +
        "    float rnd1 = fract(sin(dot(seed1 ,vec2(12.9898,78.233))) * 43758.5453); " +
        "    vec2 seed2 = vec2(sin(d.y), cos(d.x)); " +
        "    float rnd2 = fract(sin(dot(seed2 ,vec2(12.9898,78.233))) * 43758.5453); " +
        
        "   float offsetX = radius * (-1.0 + rnd1 * 2.0); " +
        "   float offsetY = radius * (-1.0 + rnd2 * 2.0); " +
        "   return vec2(destCoord().x + offsetX, destCoord().y + offsetY); " +
        "}"


Core Image for Swift

The code for both versions of this scatter filter are available here. However, if you'd like to learn more about writing custom Core Image kernels with Core Image Kernel Language, may I recommend my book, Core Image for Swift.

Core Image for Swift is available from both Apple's iBooks Store or, as a PDF, from Gumroad. IMHO, the iBooks version is better, especially as it contains video assets which the PDF version doesn't.




0

Add a comment

About Me
About Me
Labels
Labels
Blog Archive
Loading