flutter3d
Showcase Changelog 38 packages API reference

Depth of field

since 0.7.0 Post-processing

A camera lens has exactly one distance in perfect focus, and everything else softens by an amount that follows from the lens itself: how far away it is focused, how long the lens is, and how wide the aperture is open. This page takes the same three numbers a photographer already knows instead of a single blur slider.

Step 1: Something at many distances #

Six posts stand in a row, each one two metres further than the last, so the picture holds near, focused and far all at once.

final Scene scene = Scene();
final DeviceMesh post = DeviceMesh.upload(
  context.device,
  CuboidShape(size: Vector3(0.4, 1.6, 0.4)).build(),
);
for (var i = 0; i < 6; i++) {
  final double z = -1.0 - i * 2.0;
  scene.add(
    MeshNode(
      post,
      Material(
        name: 'post $i',
        baseColor: Vector4(0.85, 0.4 + i * 0.08, 0.2, 1.0),
      ),
      name: 'post $i',
    )..setPosition(0.0, 0.0, z),
  );
}

Step 2: Set the lens #

focusDistance is what the lens is focused on, in metres. focalLength is the lens length in metres, 0.085 for an 85mm portrait lens. aperture is the f-number: a small number like 1.4 is wide open and blurs hard, a large one like 16 is a pinhole and keeps almost everything sharp.

depthOfField: DepthOfFieldSettings(
  enabled: true,
  focusDistance: focusDistance,
  focalLength: focalLength,
  aperture: aperture,
),

Drag Focus distance and watch which post is sharp. Drag Aperture down toward 1.4 and the posts on either side of it soften quickly; drag it up toward 16 and the whole row sharpens. Focal length is the strongest of the three, because it enters the blur formula squared: a long lens throws the background out of focus at an aperture where a short one would keep it sharp.

Step 3: Confirm the lens ran #

if (!frame.passes.any((FramePass p) => p.name == 'depth of field')) {
  throw StateError('the depth of field pass did not run: ${frame.skipped}');
}

Note. The blur reads distance from the surface buffer's own depth channel, in real metres rather than a screen-space guess, which is why the numbers above behave the way a lens actually does.