Reflection probes: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: ball (line 44), probe (line 51), live (line 98).
1/// A mirror finish that shows the room it stands in, not the sky: a cube
2/// captured from one point and convolved into a roughness chain on the
3/// device.
4///
5/// Quoted by `reflection_probes.md` and shown whole in the Source tab.
6library;
8import 'dart:math' as math;
10import 'package:flutter3d/flutter3d.dart';
11import 'package:flutter3d_showcase/src/demo/demo.dart';
12import 'package:vector_math/vector_math.dart';
14final class ReflectionProbesDemo extends ShowcaseDemo {
15 double intensity = 1.0;
16 double roughness = 0.06;
18 late final ReflectionProbeNode _probe;
19 late final Material _ball;
22 void configureView(DemoContext context) {
23 context.orbit
24 ..distance = 6.0
25 ..pitch = 0.18
26 ..yaw = 0.7;
27 context.orbit.target.setValues(0.0, 0.7, 0.0);
28 }
31 Scene build(DemoContext context) {
32 final Material floor = Material(
33 name: 'floor',
34 baseColor: Vector4(0.5, 0.5, 0.53, 1.0),
35 roughness: 0.9,
36 doubleSided: true,
37 );
38 final Material wall = Material(
39 name: 'wall',
40 baseColor: Vector4(0.82, 0.18, 0.14, 1.0),
41 roughness: 0.9,
42 doubleSided: true,
43 );
44 final Material ball = _ball = Material(
45 name: 'mirror ball',
46 baseColor: Vector4(0.9, 0.9, 0.92, 1.0),
47 metallic: 1.0,
48 roughness: roughness,
49 );
51 _probe = ReflectionProbeNode(name: 'probe', intensity: intensity)
52 ..setPosition(0.0, 0.9, 0.0);
54 final MeshNode wallNode =
55 MeshNode(
56 DeviceMesh.upload(
57 context.device,
58 const PlaneShape(width: 3, depth: 4).build(),
59 ),
60 wall,
61 name: 'wall',
62 )
63 ..setPosition(-2.0, 1.5, 0.0)
64 ..setRotation(
65 Quaternion.axisAngle(Vector3(0.0, 0.0, 1.0), -math.pi / 2),
66 );
68 return Scene()
69 ..ambientIntensity = 0.15
70 ..add(
71 MeshNode(
72 DeviceMesh.upload(
73 context.device,
74 const PlaneShape(width: 8, depth: 8).build(),
75 ),
76 floor,
77 name: 'floor',
78 ),
79 )
80 ..add(wallNode)
81 ..add(
82 MeshNode(
83 DeviceMesh.upload(
84 context.device,
85 SphereShape(radius: 0.9, segments: 32, rings: 16).build(),
86 ),
87 ball,
88 name: 'ball',
89 )..setPosition(0.0, 0.9, 0.0),
90 )
91 ..add(
92 LightNode(name: 'sun', intensity: 3.0)
93 ..setLocalForward(Vector3(-0.5, -0.7, -0.35)),
94 )
95 ..add(_probe);
96 }
99 void update(DemoContext context, double dt) {
100 _probe.intensity = intensity;
101 _ball.roughness = roughness;
102 }
105 List<DemoControl> controls(DemoContext context) => <DemoControl>[
106 SliderControl(
107 'Probe intensity',
108 min: 0,
109 max: 2,
110 value: () => intensity,
111 onChanged: (double v) => intensity = v,
112 ),
113 SliderControl(
114 'Ball roughness',
115 min: 0.02,
116 max: 1,
117 value: () => roughness,
118 onChanged: (double v) => roughness = v,
119 ),
120 ];
123 void verify(Scene scene, FrameResult frame) {
124 if (!_probe.isCaptured) {
125 throw StateError('the probe never finished capturing its cube');
126 }
127 final FramePass capture = frame.passes.firstWhere(
128 (FramePass p) => p.name == 'reflection probe 0',
129 orElse: () => throw StateError('no reflection probe pass ran this frame'),
130 );
131 if (capture.drawCalls == 0) {
132 throw StateError('the probe pass ran and drew nothing into its cube');
133 }
134 }
135}