如何检测相机前方对象是否可见?VisibleOnScreenNotifier3D节点问题
检测相机前方对象是否可见
要判断相机前方对象是否可见(无遮挡且处于相机视锥体内),需结合视锥体范围校验和遮挡检测:
- 视锥体校验:使用
Camera3D.is_position_in_frustum(object.global_position)判断对象是否在相机可视范围内;也可通过VisibleOnScreenNotifier3D的screen_entered/screen_exited信号监听对象进出视锥体,但该节点不处理遮挡逻辑。 - 遮挡检测:用
RayCast3D或ShapeCast3D从相机位置发射射线至对象关键位置,检查是否被带碰撞的物体(如开启碰撞的MeshInstance3D墙体)阻挡。若射线未命中障碍物,则对象无遮挡。
示例GDScript代码:
func is_object_visible_to_camera(camera: Camera3D, object: Node3D) -> bool: # 先校验是否在视锥体内 if not camera.is_position_in_frustum(object.global_position): return false # 再检测是否有遮挡 var ray = RayCast3D.new() ray.global_position = camera.global_position ray.target_position = object.global_position ray.collision_mask = 1 # 匹配墙体所在的碰撞层 add_child(ray) ray.force_raycast_update() var obstructed = ray.is_colliding() remove_child(ray) return not obstructed
检测对象是否正在被渲染
判断对象是否被实际渲染到视口,需综合节点可见性、视口剔除掩码、视锥体范围和遮挡情况:
- 基础校验:先检查
object.visible和object.is_inside_tree()确保节点本身启用可见且在场景树中。 - 视口掩码校验:调用
get_viewport().get_cull_mask_bit(object.layers)确认对象所在层未被视口剔除。 - 结合前面的可见性检测:只有对象在视锥体内且无遮挡时,才会被实际渲染。
示例代码:
func is_object_rendered(object: MeshInstance3D) -> bool: if not object.visible or not object.is_inside_tree(): return false var viewport = get_viewport() if not viewport.get_cull_mask_bit(object.layers): return false var camera = viewport.get_camera_3d() return is_object_visible_to_camera(camera, object)
解决VisibleOnScreenNotifier3D墙体遮挡不触发screen_exited的问题
VisibleOnScreenNotifier3D仅检测对象包围盒是否在相机视锥体内,不考虑场景中的遮挡物,因此对象被墙体遮挡但仍在视锥内时,不会触发screen_exited信号。
解决方案:
- 保留
VisibleOnScreenNotifier3D监听对象进出视锥体的状态。 - 当对象处于视锥体内时,额外执行遮挡检测,手动判断是否被遮挡,以此模拟“不可见”状态。
示例实现:
extends Node3D @onready var notifier = $VisibleOnScreenNotifier3D @onready var target = $MeshInstance3D var in_frustum = false var is_obscured = false func _ready(): notifier.screen_entered.connect(_on_enter_frustum) notifier.screen_exited.connect(_on_exit_frustum) func _process(_delta): if in_frustum: is_obscured = check_obstruction() if is_obscured: _on_object_obscured() else: _on_object_visible() func _on_enter_frustum(): in_frustum = true func _on_exit_frustum(): in_frustum = false is_obscured = false _on_object_left_frustum() func check_obstruction() -> bool: var camera = get_viewport().get_camera_3d() var ray = RayCast3D.new() ray.global_position = camera.global_position ray.target_position = target.global_position ray.collision_mask = 1 # 匹配墙体碰撞层 add_child(ray) ray.force_raycast_update() var result = ray.is_colliding() remove_child(ray) return result # 自定义回调逻辑 func _on_object_obscured(): print("对象被遮挡,未实际渲染") func _on_object_visible(): print("对象在相机前方可见") func _on_object_left_frustum(): print("对象离开相机视锥体")
内容的提问来源于stack exchange,提问作者cyner boy
相关产品推荐
相关产品推荐

