flutter3d
Showcase Changelog 38 packages API reference

Motion blur: the source

since 0.8.0 Post-processing

Regions the guide quotes: wheel (line 37), turn (line 87), settings (line 93), ran (line 135).

1/// Motion blur: what moved while the shutter was open, smeared along the way
2/// it moved.
3///
4/// Quoted by `motion_blur.md` and shown whole in the Source tab.
5library;
6
7import 'dart:math' as math;
8
9import 'package:flutter3d/flutter3d.dart';
10import 'package:flutter3d_showcase/pages/post/post_stage.dart';
11import 'package:flutter3d_showcase/src/demo/demo.dart';
12import 'package:vector_math/vector_math.dart';
14final class MotionBlurDemo extends ShowcaseDemo {
15 bool blur = true;
16 double shutter = 0.5;
17 double maxRadius = 20.0;
18 double speed = 8.0;
20 /// The wheel's angle, in radians, which [update] advances.
21 double _angle = 0.0;
23 late final SceneNode _wheel;
25 @override
26 void configureView(DemoContext context) {
27 context.orbit
28 ..distance = 5.0
29 ..pitch = 0.0
30 ..yaw = 0.0;
31 context.orbit.target.setValues(0.0, 0.0, 0.0);
32 }
34 @override
35 Scene build(DemoContext context) {
36 final GraphicsDevice device = context.device;
37 final DeviceMesh spoke = DeviceMesh.upload(
38 device,
39 CuboidShape(size: Vector3(3.0, 0.3, 0.3)).build(),
40 );
41 final List<Vector4> colours = <Vector4>[
42 Vector4(0.9, 0.8, 0.2, 1.0),
43 Vector4(0.2, 0.7, 0.9, 1.0),
44 Vector4(0.9, 0.3, 0.3, 1.0),
45 ];
46 _wheel = SceneNode(name: 'wheel');
47 for (int i = 0; i < 3; i++) {
48 _wheel.add(
49 MeshNode(spoke, Material(baseColor: colours[i]), name: 'spoke $i')
50 ..setRotationYawPitchRoll(0.0, 0.0, i * math.pi / 3),
51 );
52 }
53 _wheel.add(
54 MeshNode(
55 DeviceMesh.upload(
56 device,
57 const CylinderShape(
58 radiusTop: 0.35,
59 radiusBottom: 0.35,
60 height: 0.5,
61 ).build(),
62 ),
63 Material(baseColor: Vector4(0.5, 0.5, 0.5, 1.0)),
64 name: 'hub',
65 )..setRotationYawPitchRoll(0.0, math.pi / 2, 0.0),
66 );
68 final MeshNode backdrop = MeshNode(
69 DeviceMesh.upload(
70 device,
71 CuboidShape(size: Vector3(14.0, 9.0, 0.1)).build(),
72 ),
73 Material(baseColor: Vector4(0.35, 0.35, 0.38, 1.0), roughness: 0.9),
74 name: 'backdrop',
75 )..setPosition(0.0, 0.0, -1.0);
76 final LightNode sun = LightNode(name: 'sun', intensity: 3.0)
77 ..castsShadow = false
78 ..setLocalForward(Vector3(-2.0, -3.0, -4.0).normalized());
79 return Scene()
80 ..add(backdrop)
81 ..add(_wheel)
82 ..add(sun);
83 }
85 @override
86 void update(DemoContext context, double dt) {
87 _angle += speed * dt;
88 _wheel.setRotationYawPitchRoll(0.0, 0.0, _angle);
89 }
91 @override
92 RenderSettings settings(DemoContext context) => RenderSettings(
93 motionBlur: MotionBlurSettings(
94 enabled: blur,
95 shutterFraction: shutter,
96 maxRadius: maxRadius,
97 ),
98 );
100 @override
101 List<DemoControl> controls(DemoContext context) => <DemoControl>[
102 ToggleControl(
103 'Motion blur',
104 value: () => blur,
105 onChanged: (bool v) => blur = v,
106 ),
107 SliderControl(
108 'Shutter',
109 min: 0,
110 max: 1,
111 value: () => shutter,
112 onChanged: (double v) => shutter = v,
113 format: (double v) => '${(v * 360).round()}°',
114 ),
115 SliderControl(
116 'Streak each side',
117 min: 1,
118 max: 64,
119 value: () => maxRadius,
120 onChanged: (double v) => maxRadius = v,
121 format: (double v) => '${v.round()} px',
122 ),
123 SliderControl(
124 'Speed',
125 min: 0,
126 max: 20,
127 value: () => speed,
128 onChanged: (double v) => speed = v,
129 format: (double v) => '${v.toStringAsFixed(1)} rad/s',
130 ),
131 ];
133 @override
134 void verify(Scene scene, FrameResult frame) {
135 if (blur) {
136 expectPassOrDecline(frame, 'motion blur');
137 } else if (passRan(frame, 'motion blur')) {
138 throw StateError('motion blur is off and its pass ran');
139 }
140 }