Local exposure: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: room (line 35), outside (line 91), settings (line 112), ran (line 154).
1/// Local exposure: each part of the frame given the exposure that shows it
2/// best, so a dark room and its bright window can both be seen.
3///
4/// Quoted by `local_exposure.md` and shown whole in the Source tab.
5library;
7import 'package:flutter3d/flutter3d.dart';
8import 'package:flutter3d_showcase/pages/post/post_stage.dart';
9import 'package:flutter3d_showcase/src/demo/demo.dart';
10import 'package:vector_math/vector_math.dart';
12final class LocalExposureDemo extends ShowcaseDemo {
13 bool local = true;
14 double strength = 1.0;
15 double shadowStops = 2.0;
16 double highlightStops = 2.0;
19 void configureView(DemoContext context) {
20 context.orbit
21 ..distance = 5.7
22 ..pitch = 0.07
23 ..yaw = 0.18;
24 context.orbit.target.setValues(-0.2, 1.1, -3.0);
25 }
28 Scene build(DemoContext context) {
29 final GraphicsDevice device = context.device;
30 MeshNode slab(Vector3 size, Vector3 at, Material material) => MeshNode(
31 DeviceMesh.upload(device, CuboidShape(size: size).build()),
32 material,
33 )..setPositionFrom(at);
35 final Material plaster = Material(
36 name: 'plaster',
37 baseColor: Vector4(0.75, 0.72, 0.68, 1.0),
38 roughness: 0.9,
39 );
40 // A room six metres square and three high, with walls half a metre
41 // thick. Every slab runs past the ones it meets, so no corner is a seam.
42 const double t = 0.5;
43 const double outer = 6 + 2 * t;
44 const double side = 3 + t / 2;
45 // The window, a metre and a half wide, from 0.9 m to 2.3 m up.
46 const double half = 0.75, sill = 0.9, head = 2.3;
47 const double jamb = 3 + t - half;
48 final Scene scene = Scene()
49 ..add(slab(Vector3(outer, t, outer), Vector3(0, -t / 2, 0), plaster))
50 ..add(slab(Vector3(outer, t, outer), Vector3(0, 3 + t / 2, 0), plaster))
51 ..add(slab(Vector3(t, 3 + 2 * t, outer), Vector3(-side, 1.5, 0), plaster))
52 ..add(slab(Vector3(t, 3 + 2 * t, outer), Vector3(side, 1.5, 0), plaster))
53 // The back wall, in four pieces around the window.
54 ..add(
55 slab(
56 Vector3(jamb, 3 + 2 * t, t),
57 Vector3(-(half + jamb / 2), 1.5, -side),
58 plaster,
59 ),
60 )
61 ..add(
62 slab(
63 Vector3(jamb, 3 + 2 * t, t),
64 Vector3(half + jamb / 2, 1.5, -side),
65 plaster,
66 ),
67 )
68 ..add(
69 slab(
70 Vector3(2 * half, sill + t, t),
71 Vector3(0, (sill - t) / 2, -side),
72 plaster,
73 ),
74 )
75 ..add(
76 slab(
77 Vector3(2 * half, 3 + t - head, t),
78 Vector3(0, (head + 3 + t) / 2, -side),
79 plaster,
80 ),
81 )
82 // A crate in the dark half of the room.
83 ..add(
84 slab(
85 Vector3(0.8, 0.8, 0.8),
86 Vector3(-1.8, 0.4, -1.2),
87 Material(name: 'crate', baseColor: Vector4(0.6, 0.25, 0.15, 1.0)),
88 ),
89 );
91 final MeshNode sky = slab(
92 Vector3(8.0, 8.0, 0.1),
93 Vector3(0.0, 1.5, -5.0),
94 Material(
95 name: 'sky',
96 baseColor: Vector4(0.0, 0.0, 0.0, 1.0),
97 emissive: Vector3(5.0, 6.0, 8.0),
98 ),
99 )..shadowCasting = ShadowCastingMode.off;
100 // No lamp and no sun: the room has only a weak ambient light, and the
101 // engine is told not to add a light of its own to a scene that has none.
102 scene
103 ..ambientIntensity = 0.12
104 ..defaultLightWhenUnlit = false;
105 return scene..add(sky);
106 }
109 RenderSettings settings(DemoContext context) => RenderSettings(
110 exposure: 1.0,
111 bloom: const BloomSettings(enabled: false),
112 localExposure: LocalExposureSettings(
113 enabled: local,
114 strength: strength,
115 shadowStops: shadowStops,
116 highlightStops: highlightStops,
117 ),
118 );
121 List<DemoControl> controls(DemoContext context) => <DemoControl>[
122 ToggleControl(
123 'Local exposure',
124 value: () => local,
125 onChanged: (bool v) => local = v,
126 ),
127 SliderControl(
128 'Strength',
129 min: 0,
130 max: 1,
131 value: () => strength,
132 onChanged: (double v) => strength = v,
133 ),
134 SliderControl(
135 'Shadow stops',
136 min: 0,
137 max: 4,
138 value: () => shadowStops,
139 onChanged: (double v) => shadowStops = v,
140 format: (double v) => '+${v.toStringAsFixed(1)}',
141 ),
142 SliderControl(
143 'Highlight stops',
144 min: 0,
145 max: 4,
146 value: () => highlightStops,
147 onChanged: (double v) => highlightStops = v,
148 format: (double v) => '-${v.toStringAsFixed(1)}',
149 ),
150 ];
153 void verify(Scene scene, FrameResult frame) {
154 if (local && strength > 0) {
155 expectPassOrDecline(frame, 'local exposure');
156 } else if (passRan(frame, 'local exposure')) {
157 throw StateError('local exposure is off and its pass ran');
158 }
159 }
160}