Light shafts: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: doorway (line 37), sun (line 54), settings (line 79), ran (line 117).
1/// Light shafts: a view ray marched through the sun's own shadow map, so a
2/// beam through a doorway has the doorway's shape.
3///
4/// Quoted by `light_shafts.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 LightShaftsDemo extends ShowcaseDemo {
12 // A density per metre since 0.7.4 — a dusty doorway rather than a clear
13 // day — and the phase that puts the glow towards the sun.
14 double strength = 0.03;
15 double anisotropy = 0.6;
16 double distance = 30.0;
19 void configureView(DemoContext context) {
20 context.orbit
21 ..distance = 8.0
22 ..pitch = 0.15
23 ..yaw = 0.0;
24 context.orbit.target.setValues(0.0, 1.0, 2.0);
25 }
28 Scene build(DemoContext context) {
29 final GraphicsDevice device = context.device;
30 final Material stone = Material(
31 name: 'stone',
32 baseColor: Vector4(0.35, 0.33, 0.3, 1.0),
33 roughness: 0.9,
34 );
35 final Material floorStone = stone.copy()..doubleSided = true;
37 final DeviceMesh pillar = DeviceMesh.upload(
38 device,
39 CuboidShape(size: Vector3(1.2, 4.0, 1.2)).build(),
40 );
41 final MeshNode left = MeshNode(pillar, stone, name: 'left pillar')
42 ..setPosition(-1.6, 2.0, -2.0);
43 final MeshNode right = MeshNode(pillar, stone, name: 'right pillar')
44 ..setPosition(1.6, 2.0, -2.0);
45 final MeshNode lintel = MeshNode(
46 DeviceMesh.upload(
47 device,
48 CuboidShape(size: Vector3(4.4, 0.8, 1.2)).build(),
49 ),
50 stone,
51 name: 'lintel',
52 )..setPosition(0.0, 4.4, -2.0);
54 final LightNode sun = LightNode(
55 name: 'sun',
56 intensity: 6.0,
57 castsShadow: true,
58 )..setLocalForward(Vector3(-0.3, -0.6, 0.7));
60 return Scene()
61 ..add(
62 MeshNode(
63 DeviceMesh.upload(
64 device,
65 const PlaneShape(width: 20, depth: 20).build(),
66 ),
67 floorStone,
68 name: 'floor',
69 ),
70 )
71 ..add(left)
72 ..add(right)
73 ..add(lintel)
74 ..add(sun);
75 }
78 RenderSettings settings(DemoContext context) => RenderSettings(
79 lightShafts: LightShaftSettings(
80 enabled: true,
81 strength: strength,
82 anisotropy: anisotropy,
83 distance: distance,
84 ),
85 );
88 List<DemoControl> controls(DemoContext context) => <DemoControl>[
89 SliderControl(
90 'Density',
91 min: 0,
92 max: 0.1,
93 value: () => strength,
94 onChanged: (double v) => strength = v,
95 format: (double v) => '${v.toStringAsFixed(3)} /m',
96 ),
97 SliderControl(
98 'Forward scattering',
99 min: -0.9,
100 max: 0.9,
101 value: () => anisotropy,
102 onChanged: (double v) => anisotropy = v,
103 format: (double v) => v.toStringAsFixed(2),
104 ),
105 SliderControl(
106 'Reach',
107 min: 5,
108 max: 60,
109 value: () => distance,
110 onChanged: (double v) => distance = v,
111 format: (double v) => '${v.round()} m',
112 ),
113 ];
116 void verify(Scene scene, FrameResult frame) {
117 if (!frame.passes.any((FramePass p) => p.name == 'light shafts')) {
118 throw StateError('the light shafts pass did not run: ${frame.skipped}');
119 }
120 }
121}