BVH and frustum culling: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: visible (line 34), offscreen (line 46), light (line 58), check (line 67).
1/// A large scene where most mesh boxes sit outside the current frustum.
2library;
4import 'package:flutter3d/flutter3d.dart';
5import 'package:flutter3d_showcase/src/demo/demo.dart';
6import 'package:vector_math/vector_math.dart';
8final class CullingDemo extends ShowcaseDemo {
9 static const int visibleCount = 16;
10 static const int hiddenCount = 304;
12 late final SceneNode _offscreenBranch;
15 void configureView(DemoContext context) {
16 context.orbit
17 ..distance = 12.0
18 ..pitch = 0.42
19 ..yaw = 0.55;
20 }
23 Scene build(DemoContext context) {
24 final DeviceMesh cube = DeviceMesh.upload(
25 context.device,
26 CuboidShape(size: Vector3.all(0.72)).build(),
27 );
28 final Material material = Material(
29 name: 'culling markers',
30 baseColor: Vector4(0.25, 0.62, 0.86, 1.0),
31 roughness: 0.58,
32 );
34 final Scene scene = Scene()
35 ..ambientColor = Vector3(0.44, 0.53, 0.7)
36 ..ambientIntensity = 0.14;
37 for (var i = 0; i < visibleCount; i++) {
38 final int x = i % 4;
39 final int z = i ~/ 4;
40 scene.add(
41 MeshNode(cube, material, name: 'visible cube $i')
42 ..setPosition((x - 1.5) * 1.35, 0.0, (z - 1.5) * 1.35),
43 );
44 }
46 _offscreenBranch = SceneNode(name: 'offscreen warehouse')
47 ..setPosition(120.0, 0.0, 80.0);
48 for (var i = 0; i < hiddenCount; i++) {
49 final int x = i % 19;
50 final int z = i ~/ 19;
51 _offscreenBranch.add(
52 MeshNode(cube, material, name: 'offscreen cube $i')
53 ..setPosition(x * 1.1, 0.0, z * 1.1),
54 );
55 }
56 scene.add(_offscreenBranch);
58 scene.add(
59 LightNode(name: 'sun', intensity: 3.1)
60 ..setLocalForward(Vector3(-0.5, -0.8, -0.3)),
61 );
62 return scene;
63 }
66 void verify(Scene scene, FrameResult frame) {
67 final int total = visibleCount + hiddenCount;
68 if (scene.meshes.length != total ||
69 _offscreenBranch.subtreeBounds == null ||
70 frame.culled < hiddenCount ||
71 frame.drawCalls < visibleCount) {
72 throw StateError('the offscreen mesh field was not culled');
73 }
74 }
75}