Shadow quality: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: ground (line 29), casters (line 47), sun (line 74), settings (line 83).
1/// The knobs that decide how good the sun's shadow looks: the size of its map,
2/// the bias that keeps it clean, how dark it is and how far it is fitted for.
3///
4/// Quoted by `shadow_settings.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 ShadowSettingsDemo extends ShowcaseDemo {
12 int resolutionChoice = 0;
13 double bias = 0.0015;
14 double strength = 1.0;
15 double viewDistance = 40.0;
17 static const List<int> _resolutions = <int>[256, 512, 1024, 2048];
20 void configureView(DemoContext context) {
21 context.orbit
22 ..distance = 9.0
23 ..pitch = 0.6
24 ..yaw = 0.6;
25 }
28 Scene build(DemoContext context) {
29 final Material stone = Material(
30 name: 'stone',
31 baseColor: Vector4(0.75, 0.73, 0.68, 1.0),
32 roughness: 0.9,
33 doubleSided: true,
34 );
35 final Scene scene = Scene()
36 ..add(
37 MeshNode(
38 DeviceMesh.upload(
39 context.device,
40 const PlaneShape(width: 14, depth: 14).build(),
41 ),
42 stone,
43 name: 'ground',
44 ),
45 );
47 final Material clay = Material(
48 name: 'clay',
49 baseColor: Vector4(0.85, 0.45, 0.3, 1.0),
50 roughness: 0.7,
51 );
52 scene
53 ..add(
54 MeshNode(
55 DeviceMesh.upload(
56 context.device,
57 CuboidShape(size: Vector3(1.2, 2.4, 1.2)).build(),
58 ),
59 clay,
60 name: 'column',
61 )..setPosition(-1.5, 1.2, 0.0),
62 )
63 ..add(
64 MeshNode(
65 DeviceMesh.upload(
66 context.device,
67 SphereShape(radius: 0.8, segments: 32, rings: 16).build(),
68 ),
69 clay,
70 name: 'ball',
71 )..setPosition(1.5, 0.8, 0.5),
72 );
74 scene.add(
75 LightNode(name: 'sun', intensity: 3.0, castsShadow: true)
76 ..setLocalForward(Vector3(-0.5, -0.9, -0.4)),
77 );
78 return scene;
79 }
82 RenderSettings settings(DemoContext context) => RenderSettings(
83 shadows: ShadowSettings(
84 cascades: 2,
85 resolution: _resolutions[resolutionChoice],
86 bias: bias,
87 strength: strength,
88 viewDistance: viewDistance,
89 ),
90 );
93 List<DemoControl> controls(DemoContext context) => <DemoControl>[
94 ChoiceControl(
95 'Map size',
96 options: <String>[for (final int r in _resolutions) '$r'],
97 index: () => resolutionChoice,
98 onChanged: (int i) => resolutionChoice = i,
99 ),
100 SliderControl(
101 'Bias',
102 min: 0,
103 max: 0.02,
104 value: () => bias,
105 onChanged: (double v) => bias = v,
106 format: (double v) => v.toStringAsFixed(4),
107 ),
108 SliderControl(
109 'Strength',
110 min: 0,
111 max: 1,
112 value: () => strength,
113 onChanged: (double v) => strength = v,
114 ),
115 SliderControl(
116 'Distance',
117 min: 3,
118 max: 40,
119 value: () => viewDistance,
120 onChanged: (double v) => viewDistance = v,
121 format: (double v) => '${v.round()} m',
122 ),
123 ];
126 void verify(Scene scene, FrameResult frame) {
127 if (!frame.passes.any((FramePass p) => p.name == 'directional shadows')) {
128 throw StateError('the sun drew no shadow map');
129 }
130 if (frame.shadowCasters < 2) {
131 throw StateError(
132 'expected the column and the ball in the map, '
133 'got ${frame.shadowCasters}',
134 );
135 }
136 }
137}