The field kept current: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: room (line 36), field (line 81), live (line 97).
1/// The irradiance field kept current by the renderer: a few probes a frame
2/// look at the room again, so the bounce follows a wall that changes colour.
3///
4/// Quoted by `irradiance_updates.md` and shown whole in the Source tab.
5library;
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_showcase/src/demo/demo.dart';
9import 'package:vector_math/vector_math.dart';
11final class IrradianceUpdatesDemo extends ShowcaseDemo {
12 int wallColour = 0;
13 double probesPerFrame = 2;
14 double hysteresis = 0.9;
16 late final Material _paint;
17 late final IrradianceField _field;
19 static final List<Vector4> _colours = <Vector4>[
20 Vector4(0.85, 0.08, 0.08, 1.0),
21 Vector4(0.08, 0.7, 0.12, 1.0),
22 Vector4(0.1, 0.2, 0.85, 1.0),
23 ];
26 void configureView(DemoContext context) {
27 context.orbit
28 ..distance = 6.0
29 ..pitch = 0.35
30 ..yaw = 0.5;
31 context.orbit.target.setValues(-0.5, 1.0, 0.0);
32 }
35 Scene build(DemoContext context) {
36 final Material floor = Material(
37 name: 'floor',
38 baseColor: Vector4(0.55, 0.55, 0.55, 1.0),
39 roughness: 0.95,
40 doubleSided: true,
41 );
42 _paint = Material(
43 name: 'painted wall',
44 baseColor: _colours[wallColour].clone(),
45 roughness: 0.95,
46 doubleSided: true,
47 );
48 final Scene scene = Scene()
49 ..ambientIntensity = 1.0
50 ..add(
51 MeshNode(
52 DeviceMesh.upload(
53 context.device,
54 const PlaneShape(width: 6, depth: 6).build(),
55 ),
56 floor,
57 name: 'floor',
58 ),
59 )
60 ..add(
61 MeshNode(
62 DeviceMesh.upload(
63 context.device,
64 const PlaneShape(width: 3, depth: 4).build(),
65 ),
66 _paint,
67 name: 'wall',
68 )
69 ..setPosition(-2.0, 1.5, 0.0)
70 ..setRotation(Quaternion.axisAngle(Vector3(0.0, 0.0, 1.0), -1.5708)),
71 )
72 ..add(
73 LightNode(
74 name: 'lamp',
75 type: LightType.point,
76 intensity: 30.0,
77 range: 12.0,
78 )..setPosition(-0.8, 2.5, 0.0),
79 );
81 _field =
82 IrradianceField(
83 origin: Vector3(-1.0, 0.5, -1.0),
84 spacing: Vector3(1.0, 1.0, 1.0),
85 countX: 2,
86 countY: 2,
87 countZ: 2,
88 )
89 ..gpuUpdates = probesPerFrame.round()
90 ..fillGutters();
91 scene.irradianceField = _field;
92 return scene;
93 }
96 void update(DemoContext context, double dt) {
97 _paint.baseColor.setFrom(_colours[wallColour]);
98 _field
99 ..gpuUpdates = probesPerFrame.round()
100 ..hysteresis = hysteresis;
101 }
104 List<DemoControl> controls(DemoContext context) => <DemoControl>[
105 ChoiceControl(
106 'Wall colour',
107 options: const <String>['Red', 'Green', 'Blue'],
108 index: () => wallColour,
109 onChanged: (int i) => wallColour = i,
110 ),
111 SliderControl(
112 'Probes a frame',
113 min: 1,
114 max: 8,
115 divisions: 7,
116 value: () => probesPerFrame,
117 onChanged: (double v) => probesPerFrame = v,
118 format: (double v) => '${v.round()}',
119 ),
120 SliderControl(
121 'Hysteresis',
122 min: 0,
123 max: 0.99,
124 value: () => hysteresis,
125 onChanged: (double v) => hysteresis = v,
126 ),
127 ];
130 void verify(Scene scene, FrameResult frame) {
131 final PassSkip? skipped = frame.skipReasonOf('irradiance update');
132 if (skipped != null) {
133 throw StateError('the probe update was skipped: $skipped');
134 }
135 if (!frame.passes.any((FramePass p) => p.name == 'irradiance update')) {
136 throw StateError('no probe was updated this frame');
137 }
138 }
139}