LensFlare

The LensFlare kernel generates a lens flare whose properties - including size, spread, position, orientation and brightness - can be controlled by the user. It does not require any inputs.

This kernel demonstrates that user-defined functions can be called from within a kernel’s standard functions, such as init() and process(), just as they could in C++.

// Copyright (c) 2024 The Foundry Visionmongers Ltd.  All Rights Reserved.

/// LensFlare kernel: Generates a lens flare asset for use in compositing.
kernel LensFlare : ImageComputationKernel<ePixelWise>
{
  Image<eWrite, eAccessPoint> dst;

param:
  float2 flareHandle;
  float2 centre;
  float  size;
  float  spread;
  float  brightness;
  int    nDots;
  int    seed;

local:
  #define kMaxDots 16
  SampleType(dst) colours[kMaxDots];
  float2 dotCentres[kMaxDots];
  float invSizeSqr[kMaxDots];
  int actualDots;

  void define() {
    defineParam(centre,      "Centre",      float2(400.0f, 400.0f), eParamProxyScale);
    defineParam(flareHandle, "FlareHandle", float2(800.0f, 800.0f), eParamProxyScale);
    defineParam(size,        "Size",        150.0f, eParamProxyScale);
    defineParam(brightness,  "Brightness",  1.0f);
    defineParam(spread,      "Spread",      0.3f);
    defineParam(nDots,       "NDots",       5);
    defineParam(seed,        "Seed",        0);
  }

  #define kRandMax 32767

  /// Platform-consistent PRNG based on SGI rand().
  inline int RandI(unsigned int seed) {
    unsigned int next = seed;
    int result;

    next *= 1103515245;
    next += 12345;
    result = (unsigned int) (next / 65536) % 256;

    next *= 1103515245;
    next += 12345;
    result <<= 7;
    result ^= (unsigned int) (next / 65536) % 256;
    return result;
  }

  inline float RandF(unsigned int seed) {
    return float(RandI(seed)) / float(kRandMax);
  }

  void init() {
    // Restrict the number of flares we can produce
    actualDots = clamp(nDots, 0, kMaxDots);

    float2 dist = centre - flareHandle;
    for (int i = 0; i < actualDots; ++i) {
      // Define the flare centres
      dotCentres[i] = flareHandle + dist * spread * RandF(seed + i);
      // Define the flare sizes
      float thisSize =  RandF(seed + i + 1000) * size;
      invSizeSqr[i] = 1.0f / (thisSize * thisSize);
      // Define the flare colours
      for (int c = 0; c < dst.kComps; ++c) {
        colours[i][c] = RandF(seed + i + (c + 2) * 1000);
      }
    }
  }

  /// Calculate the flares based on the distance from their centres
  SampleType(dst) flareValue(float2 posf, int index) {
    float2 delta = posf - dotCentres[index];
    float dotDelta = dot(delta, delta) * invSizeSqr[index];
    float value = clamp(1.0f - dotDelta, 0.0f, 1.0f);
    value *= value;
    return colours[index] * value;
  }

  void process(int2 pos) {
    float2 posf(pos.x, pos.y);

    SampleType(dst) sample(0.0f);

    // Calculate the dots for the flares with their overlaps
    for (int i = 0; i < actualDots; ++i) {
      sample += flareValue(posf, i);
    }

    // Adjust the brightness and write out to the dst image
    dst() = sample * brightness;
  }

};

Footnotes

Note that kernels which call user-defined functions from within kernel() currently cannot be vectorized on the CPU.