[flame_3d] Object3D.isVisible only checks the center point, so partially visible objects are not drawn
Maintainer thường phản hồi trong vòng 1 ngày
@ufrshubham đang làm issue này rồi.
Từ ngày 29/10/2026.
Đánh giá
Issue này chưa được đánh giá.
Mô tả
What happened?
The base Object3D.isVisible only checks whether the component's local position is inside the camera's frustum:
bool isVisible(CameraComponent3D camera) {
return camera.frustum.containsVector3(position);
}
Object3D.renderTree calls it to decide whether to draw the component itself whenever its AABB intersects the edge of the frustum. So any Object3D that is partially visible, but whose center is outside of the view, isn't drawn. This happens even when the component isn't nested and reports correct bounds through computeLocalAabb. Being nested makes it worse, since position is then in the parent's space.
MeshComponent and ModelComponent aren't affected because they override isVisible with camera.frustum.intersectsWithAabb3(aabb). Custom Object3D subclasses that don't do the same are affected, for example long walls, floors or anything else that can be partially on screen.
What do you expect?
An Object3D whose AABB intersects the frustum should be drawn, like MeshComponent does.
How can we reproduce this?
- Run the
main.dartbelow withflutter run --enable-flutter-gpu(on Windows also add--enable-impeller). - There are two identical walls going from
z = -30toz = 0, both directly in the world. Their centers (z = -15) are behind the camera, but their front parts are in view. The red one is aMeshComponent, the green one is a customObject3Dwith the samecomputeLocalAabb, but without overridingisVisible. - Only the red wall is rendered.
Nothing is nested, moved or added after the first frame, so this is independent of the other culling issues.
What steps should take to fix this?
Use the world-space AABB in the base implementation, and remove the now identical override from MeshComponent:
bool isVisible(CameraComponent3D camera) {
return camera.frustum.intersectsWithAabb3(aabb);
}
ModelComponent has the same override plus a TODO about animations, so it can keep it for now.
Do have an example of where the bug occurs?
import 'package:flame/game.dart';
import 'package:flame_3d/camera.dart';
import 'package:flame_3d/components.dart';
import 'package:flame_3d/game.dart';
import 'package:flame_3d/graphics.dart';
import 'package:flame_3d/resources.dart';
import 'package:flutter/widgets.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await GpuBackend.initialize();
runApp(GameWidget(game: ReproGame()));
}
class ReproGame extends FlameGame3D {
ReproGame()
: super(
camera: CameraComponent3D(
position: Vector3(0, 2, -6),
target: Vector3(0, 1, 0),
),
);
@override
Future<void> onLoad() async {
// Two identical walls going from z = -30 to z = 0. Their centers
// (z = -15) are behind the camera, but their front parts are in view.
world.addAll([
// Red: MeshComponent. Rendered.
MeshComponent(
mesh: _wallMesh(const Color(0xFFFF0000)),
position: Vector3(1.5, 0.5, -15),
),
// Green: custom Object3D with the same bounds. Not rendered.
CustomMesh(
mesh: _wallMesh(const Color(0xFF00FF00)),
position: Vector3(-1.5, 0.5, -15),
),
]);
}
Mesh _wallMesh(Color color) => CuboidMesh(
size: Vector3(1, 1, 30),
material: UnlitMaterial(albedoColor: color),
);
}
/// An [Object3D] that draws a mesh and reports its bounds, but does not
/// override `isVisible`.
class CustomMesh extends Object3D {
CustomMesh({required this.mesh, super.position});
final Mesh mesh;
@override
Aabb3? computeLocalAabb() => mesh.aabb;
@override
void draw(covariant RenderContext3D context) {
context
..model.setFrom(worldTransformMatrix)
..drawMesh(mesh);
}
}
Relevant log output
No errors or warnings are logged.
Execute in a terminal and put output into the code block below
Flutter 3.47.8 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 737ba8f885 (6 days ago) • 2026-10-22 21:37:31 -0700
Engine • hash dfd2e6cb7d319db05e3bf1503f3e2ac60f6288a2 (revision cf636b1dfb) (6 days ago) • 2026-10-23 02:13:38.000Z
Tools • Dart 3.13.7 • DevTools 2.60.0
Affected platforms
All
Other information
Reproduced on Windows (Impeller with the OpenGL ES backend), but the culling logic is platform independent.
Closely related to #4165: for a nested Object3D without computeLocalAabb, both are needed for it to be culled correctly, since this fix relies on the AABB being at the world position. Other culling issues found at the same time: #4163 and #4164.
Are you interested in working on a PR for this?
- I want to work on this
- Ngôn ngữ chính
- Dart
- Star
- 10.8k
- Fork
- 1.1k
- Merge trung bình
- 1 ngày 21 giờ
- Pull request đã merge (30 ngày)
- 50
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của flame-engine/flame
-
`PostProcessComponent` throws `StateError` when it has no `PositionComponent` childrenCó thể đã có người làm @ufrshubham đã nhận hôm nay. Đang mởbug
flame-engine/flame#4182 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
flame-engine/flame#4176 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
discussion enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
flame-engine/flame#4175 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
flame-engine/flame#4174 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
enhancement
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
flame-engine/flame#4173 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của flame-engine/flame
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 82/100
bannzai/Pilll#2080 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
good first issue help wanted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
mollykitten/waveform#112 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
auto-implement-claude
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Suwayomi/Suwayomi-Tsumiru#698 ·
Maintainer thường phản hồi trong vòng 1 ngày