当光线投射形成多个交点时,它在哪个点return? return可以控制哪个点?
When raycasting forms multiple intersections, which point does it return? Can which point is returned be controlled?
我正在 Godot 中开发国际象棋“游戏”,玩家可以为棋盘导入 3D 模型,并用 uv 坐标在棋盘周围包裹棋盘。该项目所需的世界、本地和 uv space 之间的所有转换都非常疯狂。但是,对于复杂的板形状,尤其是我刚进口的克莱因瓶,一个更意想不到的警告是,从相机投射一条线理论上会有多个解决方案,但只有 return 一个。
当玩家点击屏幕时,我认为光线投射应该找到离他们最近的交点,但 Godot 的 PhysicsDirectSpaceState.intersect_ray() method is returning whichever intersection it finds first. This intersection could either be the correct one or not, and with up to 4 intersection points with the klein bottle, the chances are slim that the correct intersection is returned. The RayCast 对象做同样的事情,只存储一个交点。
有没有办法 return Godot 中 ray/line 的所有交点? 这样我就可以对它们进行排序以找到最接近的交点指向玩家节点。我也觉得这首先不应该是一个问题。 Godot's Ray-Casting 教程将光线描述为 return 值时“击中某物”,这直观地告诉我与“光线原点”参数最近的交点将是 returned。
如果您对正在进行的意大利面条式代码感兴趣,我有一个 GitHub project,上面有所有源代码。这是我在游戏中进行光线投射时使用的代码:
#cast out a ray from the camera, given a physics state s
static func raycast(var p:Vector2, var c:Camera,
var w:World, var v:float = INF, var mask:int = 0x7FFFFFFF):
#get physics state of the current scene
var s = w.direct_space_state
#origin and normal of the camera
var o:Vector3 = c.project_ray_origin(p)
var n:Vector3 = c.project_ray_normal(p)
#get ray intersection with that scene
var r = s.intersect_ray(o, n * v, [], mask)
#if the intersection lands, return r
if !r.empty():
return r
return null
以上来自 BoardConverter.gd, line 472.
我们可以呈现隐藏的 Viewport
并查询它。
所以让我们从添加一个 Viewport
到你的场景开始。确保它有一个尺寸集。事实上,我们可以调整大小以匹配主 Viewport
和脚本。例如:
extends Viewport
func _ready() -> void:
# warning-ignore:return_value_discarded
get_tree().connect("screen_resized", self, "resize")
func resize() -> void:
size = get_viewport().size
或者:
extends Viewport
func _ready() -> void:
# warning-ignore:return_value_discarded
get_viewport().connect("size_changed", self, "resize")
func resize() -> void:
size = get_viewport().size
还要确保将 render_target_update_mode
设置为 UPDATE_ALWAYS
。将 render_target_v_flip
设置为 true,将 transparent_bg
设置为 true
。
然后你可以添加一个 Camera
子节点,脚本复制主 Camera
的 global_transform
,并使其成为 current
(在 Viewport
节点)。例如:
extends Camera
func _ready() -> void:
current = true
func _process(_delta: float) -> void:
global_transform = get_tree().root.get_camera().global_transform
您可能还想复制相机的所有相关属性以确保视图匹配:
var tracked_cam:Camera = get_tree().root.get_camera()
global_transform = tracked_cam.global_transform
fov = tracked_cam.fov
keep_aspect = tracked_cam.keep_aspect
cull_mask = tracked_cam.cull_mask
environment = tracked_cam.environment
h_offset = tracked_cam.h_offset
v_offset = tracked_cam.v_offset
doppler_tracking = tracked_cam.doppler_tracking
projection = tracked_cam.projection
fov = tracked_cam.fov
size = tracked_cam.size
frustum_offset = tracked_cam.frustum_offset
near = tracked_cam.near
far = tracked_cam.far
您要在 Viewport
中渲染的是您的网格,但具有不同的 material。因此,您正在复制 MeshInstance
,您可以:
- 将其添加为原始子项,并使用
MeshInstance
的 layers
(在检查器面板中的“VisualInstance”下)和 Camera
的 cull_masks
] 以确保只有 Viewport
中的 Camera
可以看到它。
- 或者,将其添加为
Viewport
的子项,做一些类似于我们为 Camera
所做的事情,因此它复制原始位置,并设置 own_world
到 Viewport
上的 true
,因此它只呈现其中的内容。
关于material,它会是一个ShaderMaterial
输出UV作为颜色(写ALBEDO
,并将着色器设置为unshaded
):
shader_type spatial;
render_mode unshaded;
void fragment()
{
ALBEDO = vec3(UV, 0.0);
}
然后在附加到其他一些脚本中 Node
。它将具有 Texture
类型的 export var
并将其设置为您定义的 Viewport
中的新 ViewportTexture
:
export var texture:Texture
我们要确保 Node
进入时 Viewport
已经在场景树中。它可以找到它。因此,这个 Node
不应该是 Viewport
,也不应该是 Viewport
的父级。还要避免使用 Viewport
的子项。因此,我们想要一个兄弟姐妹。以及场景树中 Viewport
之前的同级。
现在,要从 Texture
中读取一个像素,您可以使用 get_data
从中获取 Image
,然后对其调用 lock
,然后使用get_pixel
或 get_pixelv
。别忘了unlock
它。
此代码适用于鼠标或触摸输入:
func _input(event: InputEvent) -> void:
if not (
event is InputEventScreenDrag
or event is InputEventScreenTouch
or event is InputEventMouse
):
return
var image:Image = texture.get_data()
image.lock()
var color := image.get_pixelv(event.position)
if color.a != 0.0:
print(Vector2(color.r, color.g))
image.unlock()
我正在 Godot 中开发国际象棋“游戏”,玩家可以为棋盘导入 3D 模型,并用 uv 坐标在棋盘周围包裹棋盘。该项目所需的世界、本地和 uv space 之间的所有转换都非常疯狂。但是,对于复杂的板形状,尤其是我刚进口的克莱因瓶,一个更意想不到的警告是,从相机投射一条线理论上会有多个解决方案,但只有 return 一个。
当玩家点击屏幕时,我认为光线投射应该找到离他们最近的交点,但 Godot 的 PhysicsDirectSpaceState.intersect_ray() method is returning whichever intersection it finds first. This intersection could either be the correct one or not, and with up to 4 intersection points with the klein bottle, the chances are slim that the correct intersection is returned. The RayCast 对象做同样的事情,只存储一个交点。
有没有办法 return Godot 中 ray/line 的所有交点? 这样我就可以对它们进行排序以找到最接近的交点指向玩家节点。我也觉得这首先不应该是一个问题。 Godot's Ray-Casting 教程将光线描述为 return 值时“击中某物”,这直观地告诉我与“光线原点”参数最近的交点将是 returned。
如果您对正在进行的意大利面条式代码感兴趣,我有一个 GitHub project,上面有所有源代码。这是我在游戏中进行光线投射时使用的代码:
#cast out a ray from the camera, given a physics state s
static func raycast(var p:Vector2, var c:Camera,
var w:World, var v:float = INF, var mask:int = 0x7FFFFFFF):
#get physics state of the current scene
var s = w.direct_space_state
#origin and normal of the camera
var o:Vector3 = c.project_ray_origin(p)
var n:Vector3 = c.project_ray_normal(p)
#get ray intersection with that scene
var r = s.intersect_ray(o, n * v, [], mask)
#if the intersection lands, return r
if !r.empty():
return r
return null
以上来自 BoardConverter.gd, line 472.
我们可以呈现隐藏的 Viewport
并查询它。
所以让我们从添加一个 Viewport
到你的场景开始。确保它有一个尺寸集。事实上,我们可以调整大小以匹配主 Viewport
和脚本。例如:
extends Viewport
func _ready() -> void:
# warning-ignore:return_value_discarded
get_tree().connect("screen_resized", self, "resize")
func resize() -> void:
size = get_viewport().size
或者:
extends Viewport
func _ready() -> void:
# warning-ignore:return_value_discarded
get_viewport().connect("size_changed", self, "resize")
func resize() -> void:
size = get_viewport().size
还要确保将 render_target_update_mode
设置为 UPDATE_ALWAYS
。将 render_target_v_flip
设置为 true,将 transparent_bg
设置为 true
。
然后你可以添加一个 Camera
子节点,脚本复制主 Camera
的 global_transform
,并使其成为 current
(在 Viewport
节点)。例如:
extends Camera
func _ready() -> void:
current = true
func _process(_delta: float) -> void:
global_transform = get_tree().root.get_camera().global_transform
您可能还想复制相机的所有相关属性以确保视图匹配:
var tracked_cam:Camera = get_tree().root.get_camera()
global_transform = tracked_cam.global_transform
fov = tracked_cam.fov
keep_aspect = tracked_cam.keep_aspect
cull_mask = tracked_cam.cull_mask
environment = tracked_cam.environment
h_offset = tracked_cam.h_offset
v_offset = tracked_cam.v_offset
doppler_tracking = tracked_cam.doppler_tracking
projection = tracked_cam.projection
fov = tracked_cam.fov
size = tracked_cam.size
frustum_offset = tracked_cam.frustum_offset
near = tracked_cam.near
far = tracked_cam.far
您要在 Viewport
中渲染的是您的网格,但具有不同的 material。因此,您正在复制 MeshInstance
,您可以:
- 将其添加为原始子项,并使用
MeshInstance
的layers
(在检查器面板中的“VisualInstance”下)和Camera
的cull_masks
] 以确保只有Viewport
中的Camera
可以看到它。 - 或者,将其添加为
Viewport
的子项,做一些类似于我们为Camera
所做的事情,因此它复制原始位置,并设置own_world
到Viewport
上的true
,因此它只呈现其中的内容。
关于material,它会是一个ShaderMaterial
输出UV作为颜色(写ALBEDO
,并将着色器设置为unshaded
):
shader_type spatial;
render_mode unshaded;
void fragment()
{
ALBEDO = vec3(UV, 0.0);
}
然后在附加到其他一些脚本中 Node
。它将具有 Texture
类型的 export var
并将其设置为您定义的 Viewport
中的新 ViewportTexture
:
export var texture:Texture
我们要确保 Node
进入时 Viewport
已经在场景树中。它可以找到它。因此,这个 Node
不应该是 Viewport
,也不应该是 Viewport
的父级。还要避免使用 Viewport
的子项。因此,我们想要一个兄弟姐妹。以及场景树中 Viewport
之前的同级。
现在,要从 Texture
中读取一个像素,您可以使用 get_data
从中获取 Image
,然后对其调用 lock
,然后使用get_pixel
或 get_pixelv
。别忘了unlock
它。
此代码适用于鼠标或触摸输入:
func _input(event: InputEvent) -> void:
if not (
event is InputEventScreenDrag
or event is InputEventScreenTouch
or event is InputEventMouse
):
return
var image:Image = texture.get_data()
image.lock()
var color := image.get_pixelv(event.position)
if color.a != 0.0:
print(Vector2(color.r, color.g))
image.unlock()