flutter3d
Showcase Changelog 38 packages API reference

Clustered lights: the source

since 0.8.0 Light and environment

Regions the guide quotes: floor (line 36), lights (line 49), settings (line 74).

1/// Clustered lights: every fragment is lit by the lights that reach its part
2/// of the view, so a floor under sixty-four lamps is lit by all of them.
3///
4/// Quoted by `clustered_lights.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/src/demo/demo.dart';
11import 'package:vector_math/vector_math.dart';
13final class ClusteredLightsDemo extends ShowcaseDemo {
14 bool clustered = true;
15 double range = 0.8;
17 final List<LightNode> _lights = <LightNode>[];
19 @override
20 void configureView(DemoContext context) {
21 context.orbit
22 ..distance = 9.8
23 ..pitch = 1.15
24 ..yaw = 0.0;
25 }
27 /// A saturated colour at [t] of the way round the hue circle.
28 static Vector3 _hue(double t) {
29 double channel(double offset) =>
30 (0.5 + 0.5 * math.cos(2 * math.pi * (t + offset))).clamp(0.0, 1.0);
31 return Vector3(channel(0.0), channel(2 / 3), channel(1 / 3));
32 }
34 @override
35 Scene build(DemoContext context) {
36 final Scene scene = Scene()
37 ..ambientIntensity = 0.03
38 ..add(
39 MeshNode(
40 DeviceMesh.upload(
41 context.device,
42 CuboidShape(size: Vector3(12.0, 0.1, 12.0)).build(),
43 ),
44 Material(name: 'floor', lighting: LightingModel.lambert),
45 name: 'floor',
46 )..setPosition(0.0, -0.05, 0.0),
47 );
49 for (var i = 0; i < 8; i++) {
50 for (var j = 0; j < 8; j++) {
51 final LightNode light = LightNode(
52 name: 'lamp $i $j',
53 type: LightType.point,
54 intensity: 1.5,
55 range: range,
56 color: _hue((i * 8 + j) / 64.0),
57 )..setPosition(i - 3.5, 0.3, j - 3.5);
58 _lights.add(light);
59 scene.add(light);
60 }
61 }
62 return scene;
63 }
65 @override
66 void update(DemoContext context, double dt) {
67 for (final LightNode light in _lights) {
68 light.range = range;
69 }
70 }
72 @override
73 RenderSettings settings(DemoContext context) => RenderSettings(
74 clusteredLights: clustered,
75 );
77 @override
78 List<DemoControl> controls(DemoContext context) => <DemoControl>[
79 ToggleControl(
80 'Clustered lights',
81 value: () => clustered,
82 onChanged: (bool v) => clustered = v,
83 ),
84 SliderControl(
85 'Light range',
86 min: 0.4,
87 max: 3.0,
88 value: () => range,
89 onChanged: (double v) => range = v,
90 format: (double v) => '${v.toStringAsFixed(1)} m',
91 ),
92 ];
94 @override
95 void verify(Scene scene, FrameResult frame) {
96 if (scene.lights.length != 64) {
97 throw StateError('the grid should hold 64 lights');
98 }
99 if (!clustered) {
100 throw StateError('the page should start with clusteredLights on');
101 }
102 // The per-draw list reports the same loss with the cells on or off: 64
103 // lights less eight slots and a tail of twenty-four. Those 32 are the
104 // lamps the floor loses without the cells.
105 if (frame.lightsDropped != 32) {
106 throw StateError(
107 'a per-draw list should leave 32 of the 64 lamps out, '
108 'the frame reports ${frame.lightsDropped}',
109 );
110 }
111 }