Radial

The RadialKernel generates a radial shape around a specified center with customizable transitions, defined by parameters such as size, center position, and edge smoothness.

Note

The RadialKernel functions as a generator and does not utilize an input image. When used within the timeline, a dummy input must be provided since every soft effect requires an input. However, in the node graph, the kernel can be modified to operate without needing an external input.

kernel Radial : ImageComputationKernel<ePixelWise> {
  // Input and output images
  Image<eRead, eAccessRandom, eEdgeClamped> src;  // randomly accessing and edge clamping
  Image<eWrite, eAccessPoint> dst;

  // Parameters are made available to the user as knobs.
  param:
    float rotation;   // rotation in degrees
    float width;      // width
    float height;     // height
    float2 centre;    // centre
    float smoothStep; // step

  // Local variables can be initialized once in init() and used from all pixel positions.
  local:
    float angle;
    float2 scaleFactors;
    float2 scaledCentre;
    float size;
    float scaledStep;

  // Size and Centre are affected by the proxy scaling.
  void define() {
    defineParam(rotation, "Rotation", 180.0f);
    defineParam(width, "Width", 240.0f, eParamProxyScale);
    defineParam(height, "Height", 120.0f, eParamProxyScale);
    defineParam(centre, "Centre", float2(120.0f, 120.0f), eParamProxyScale);
    defineParam(smoothStep, "Step", 80.0f, eParamProxyScale);
  }
  
  float smoothstep(float edge0, float edge1, float x)
  {
    float denom = edge1 - edge0;

    // Replace max(denom, eps) with a ternary operator
    float eps = float(1e-6f);
    denom = max(denom,eps);

    // Ensure denominator is not zero to avoid division by zero
    float t = clamp((x - edge0) / denom, 0.0f, 1.0f);
    return t * t * (3.0f - 2.0f * t);
  }

  // The init() function is run once before any calls to process().
  void init() {
    const float imgW = dst.bounds.x2 - dst.bounds.x1;
    const float imgH = dst.bounds.y2 - dst.bounds.y1;

    float minImgWH = min(imgW, imgH);
    size = max(1.0f, minImgWH);

    width = max(width, 1.0f);
    height = max(height, 1.0f);

    scaleFactors = float2(width / size, height / size);
    scaledCentre = float2(centre.x / size, centre.y / size);

    // Replace smoothStep / size with direct division (assuming size >= 1.0f from above)
    scaledStep = smoothStep / size;
    
    const float toRadians = PI / 180.0f;
    angle = rotation * toRadians;
  }

  // The process() function runs over all pixel positions of the output image.
  // The int2 pos parameter is used to indicate positions x, y.
  void process(int2 pos) {
    // Calculate the distance from the centre.
    const float x = float(pos.x) / size - scaledCentre.x;
    const float y = float(pos.y) / size - scaledCentre.y;
    
    const float sine = sin(angle);
    const float cosine = cos(angle);
    
    const float x2 = x * cosine + y * sine;
    const float y2 = y * cosine - x * sine;
    const float2 st = float2(x2, y2);

    const float2 e = float2(st.x / scaleFactors.x, st.y / scaleFactors.y);
    const float d = sqrt(e.x * e.x + e.y * e.y);

    // Calculate color with smooth step transition
    const float color = smoothstep(d, d + scaledStep, 1.0f);
    const float4 result = float4(color, color, color, 1.0f);

    for(int i = 0; i < dst.kComps ; i++){
      dst(i) = result[i];
    }

  }
};