Longitudinal chromatic aberration

Why different wavelengths can focus at different distances behind a lens — and how channel-specific defocus produces coloured fringes around the same image features.

Visible effect

Colour appears in the blur, even on the optical axis

Fine detail can carry coloured softness even at the optical axis. One side of focus may show a red or magenta fringe around bright structure; cross the focused plane and the colour balance reverses.

The channels expand around the same feature rather than sliding sideways. That is the useful visual distinction from lateral chromatic aberration, whose RGB image positions separate increasingly toward the edge of the frame.

Physics

Dispersion separates focal planes along the optical axis

Different wavelengths focus at different distances behind a lens Red, green, and blue ray bundles pass through one lens and converge at three positions along the optical axis. A sensor placed at the green focus records wider red and blue footprints.lenssensor at green focusbluegreenred
Dispersion changes optical power with wavelength, so the channel focal planes separate along the axis. At the green plane, green is a point while red and blue still occupy finite circles of confusion.

Glass refractive index changes with wavelength. A lens therefore has slightly different optical power for red, green, and blue light. Without complete achromatic correction, one object point becomes three ray bundles whose narrowest waists occur at different axial positions.

Mathematics

Three axial distances become three blur radii

Let green define the reference focal plane, s be the symmetric channel separation, and d be the signed sensor offset from green focus. We calibrate axial distance directly in output-pixel blur units for this image-space model:

At d = 0, green is sharp while red and blue have equal footprints. Moving through focus makes one outer channel tighter and the other wider. At s = 0, all three radii are equal and the model reduces to ordinary achromatic defocus.

Shader

GLSL channel-specific disk gather

Every fragment keeps one image position, gathers a different disk radius for each channel, and recombines the normalized linear-light averages.

longitudinalChromaticGatherGLSL
// WHAT: Give red, green, and blue different defocus radii for axial colour.
// HOW: Convert signed focus offset and axial separation into three radii, gather
// each channel over the same golden-angle disk, and average in linear light.
// WHY: Longitudinal chromatic aberration is a focus difference between
// wavelengths, so channel-specific blur is more explanatory than a colour shift.
const int LONGITUDINAL_CA_MAX_SAMPLES = 96;
const float LONGITUDINAL_CA_GOLDEN_ANGLE = 2.39996323;

float longitudinalCaSrgbToLinear(float value) {
  if (value <= 0.04045) return value / 12.92;
  return pow((value + 0.055) / 1.055, 2.4);
}

vec3 longitudinalCaChannelRadii(
  float focusOffsetPx,
  float axialSeparationPx
) {
  // Green is the reference plane; red and blue lie on opposite sides.
  float separation = max(axialSeparationPx, 0.0);
  return vec3(
    abs(focusOffsetPx - separation),
    abs(focusOffsetPx),
    abs(focusOffsetPx + separation)
  );
}

vec3 longitudinalChromaticGather(
  sampler2D source,
  vec2 sourceUv,
  vec2 sampleStep,
  float focusOffsetPx,
  float axialSeparationPx,
  int sampleCount
) {
  vec3 radiiPx = longitudinalCaChannelRadii(
    focusOffsetPx,
    axialSeparationPx
  );
  float maximumRadius = max(radiiPx.r, max(radiiPx.g, radiiPx.b));
  // The widest channel determines the local tap budget shared by all channels.
  int localSampleCount = min(
    sampleCount,
    max(1, int(ceil(maximumRadius * 8.0)))
  );
  float count = float(localSampleCount);
  vec3 accumulated = vec3(0.0);

  for (int index = 0; index < LONGITUDINAL_CA_MAX_SAMPLES; index += 1) {
    if (index >= localSampleCount) break;
    // Equal-area samples give every channel the same stable disk pattern.
    float fraction = (float(index) + 0.5) / count;
    float sampleRadius = sqrt(fraction);
    float angle = float(index) * LONGITUDINAL_CA_GOLDEN_ANGLE;
    vec2 disk = vec2(cos(angle), sin(angle)) * sampleRadius;
    vec2 direction = disk * sampleStep;
    // Each channel samples its own radius and mip-prefilter level.
    vec3 encoded = vec3(
      textureLod(
        source,
        sourceUv + direction * radiiPx.r,
        max(0.0, log2(max(radiiPx.r, 1.0)) - 2.0)
      ).r,
      textureLod(
        source,
        sourceUv + direction * radiiPx.g,
        max(0.0, log2(max(radiiPx.g, 1.0)) - 2.0)
      ).g,
      textureLod(
        source,
        sourceUv + direction * radiiPx.b,
        max(0.0, log2(max(radiiPx.b, 1.0)) - 2.0)
      ).b
    );
    accumulated += vec3(
      longitudinalCaSrgbToLinear(encoded.r),
      longitudinalCaSrgbToLinear(encoded.g),
      longitudinalCaSrgbToLinear(encoded.b)
    );
  }

  // Normalize after decoding so the defocus average preserves channel energy.
  return accumulated / count;
}
Processing pipelineBoxes mark actual render-pass boundaries.
  1. Source texturesRGB
  2. Per-channel defocus gatherOne fullscreen render pass
    • Apply the signed sensor offset
    • Calculate RGB blur radii
    • Linear-light channel gathers · parallel
      Gather and average redGather and average greenGather and average blue
    • Mix and encode to sRGB
  3. Display output

Why these steps are here

  1. Use a signed focus offset. Crossing the reference plane must reverse which outer channel is closest to focus.
  2. Keep the sample centre fixed. Expanding channel footprints without shifting their centres preserves the axial character of the effect.
  3. Gather disks independently. Each wavelength has a different circle of confusion, not a coloured radial translation.
  4. Average in linear light. The normalized kernel preserves constant colour fields and avoids gamma-darkened blur.
  5. Bound the cost. All channels share one deterministic ninety-six-tap limit and use mip prefiltering for wider footprints.

Notes

  • The grid reveals the invariant: channel edges soften around the same geometry instead of separating radially across the frame.
  • This playground has only one RGB image and no depth buffer. Physical foreground and background points have different signed defocus, so a real scene can show opposite fringe colours on opposite sides of the focused distance. The global sensor offset can demonstrate either side, but not both at once.
  • Real longitudinal chromatic aberration depends on lens prescription, aperture, focus distance, spectral response, and the wavelength variation of both spherical aberration and defocus.
  • Bright clipped highlights and sharpening can change the apparent fringe. They are signal-processing consequences, not part of this isolated optical model.

References

Edmund Optics — How aberrations affect imaging lenses — manufacturer reference for spherical, astigmatic, field-curvature, and chromatic aberrations.

Nikon MicroscopyU — Introduction to microscope objectives — manufacturer educational reference on coma, astigmatism, field curvature, chromatic correction, and flare.