背景需求
项目需要用python实现常见的raycast射线检测。即: 给定一个点,一个方向,一个mesh。 求:从改点沿该方向发送射线,与mesh是否有交点,如果有,给出交点坐标,距离等信息。
找到两种亲测可行的解决方案:
两个包的安装
trimesh 支持 pip install 直接下载安装
python mesh raycast 包: 需要按照github主页里的说明进行安装,即运行命令: git clone https://github.com/cprogrammer1994/python-mesh-raycast cd python-mesh-raycast python setup.py develop
两个包的用法:
没有找到全面的文档,都是看的github里面的example和源码。
trimesh
https://github.com/mikedh/trimesh/blob/master/examples/ray.py
注意他的origins和directions都是n*3的,也就是需要默认用不同点和方向,做多次raycast检测。只有一个点和方向的话,也要套一层[]
mesh = trimesh.primitives.Sphere()
ray_origins = np.array([[0, 0, -5],
[2, 2, -10]])
ray_directions = np.array([[0, 0, 1],
[0, 0, 1]])
locations, index_ray, index_tri = mesh.ray.intersects_location(
ray_origins=ray_origins,
ray_directions=ray_directions)
print(locations)
python mesh raycast
单个三角形的mesh:
import mesh_raycast
triangles = np.array([
[0.0, 0.0, 0.0],
[4.0, 0.0, 0.0],
[0.0, 4.0, 0.0],
], dtype='f4')
result = mesh_raycast.raycast(source=(0.4, 0.8, 5.0), direction=(0.0, 0.0, -1.0), mesh=triangles)
结果如下,result包含很多字段,需要自取。可以看到比trimesh多了我们想要的distance:
{
'face': 0,
'point': (0.4, 0.8, 0.0),
'normal': (0.0, 0.0, 1.0),
'coeff': (0.1, 0.2),
'distance': 5.0,
'dot': 1.0,
}
多个三角形的mesh也可以:
triangles = np.array([
[0.0, 0.0, 0.0],
[4.0, 0.0, 0.0],
[0.0, 4.0, 0.0],
[0.0, 0.0, 2.0],
[2.0, 0.0, 2.0],
[0.0, 2.0, 2.0],
], dtype='f4')
source = (1.0, 1.0, 5.0)
direction = mesh_raycast.direction(source, (0.5, 0.5, -0.5))
result = mesh_raycast.raycast(source=source, direction=direction, mesh=triangles)
二者对比
这里用trimesh读入本地的一个obj文件作为mesh。
import trimesh
import numpy as np
import mesh_raycast
import time
objPath = 'DatasetGen/untitled.obj'
mesh = trimesh.load_mesh(objPath)
if (type(mesh) == trimesh.scene.scene.Scene):
print("scene")
print(list(mesh.geometry.values())[0])
elif (type(mesh) == trimesh.base.Trimesh):
print("single object")
mesh=mesh_centerize(mesh)
print(np.min(mesh.vertices,0))
print(np.max(mesh.vertices, 0))
然后进行碰撞检测:
UAV_height = 50
ray_origins = np.array([0,UAV_height,0])
ray_directions = np.array([0,-1,0])
t1 = time.time()
locations, index_ray, index_tri = mesh.ray.intersects_location(
ray_origins=[ray_origins],
ray_directions=[ray_directions])
print(locations)
t2 = time.time()
print("Ran in {0:.3f} s".format(t2 - t1))
t1 = time.time()
triangles = mesh.vertices[mesh.faces]
triangles = np.array(triangles, dtype='f4')
result = mesh_raycast.raycast(ray_origins, ray_directions, mesh=triangles)
first_result = min(result, key=lambda x: x['distance'])
print(first_result['point'])
t2 = time.time()
print("Ran in {0:.3f} s".format(t2 - t1))
主要的注意点是triangles一定要用np.array处理一下,不然直接用会不对。
结果是:
single object
[-35.7634975 -0.066495 -44.813048 ]
[35.7634975 12.359008 44.813048 ]
[[0. 0.292007 0. ]]
Ran in 1.129 s
(0.0, 0.2920074462890625, 0.0)
Ran in 0.015 s
可见方法二,python mesh raycast快多了。 这是意料之中的,因为在trimesh的ray.py代码中,已经说了自己速度很慢: https://github.com/mikedh/trimesh/blob/master/examples/ray.py
Do simple mesh- ray queries. Base functionality only requires numpy, but if you install pyembree you get the same API with a roughly 50x speedup.
这个pyembree我还没试过。
而方法二是用C实现的,那当然快了:
SThis module is using glm and the Python’s c-api. The implementation can be found in the mesh_raycast.cpp.
而且我的应用里确实需要用到distance。那当然选python mesh raycast了~
|