项目需要用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和源码。
https://github.com/mikedh/trimesh/blob/master/examples/ray.py
注意他的origins和directions都是n*3的,也就是需要默认用不同点和方向,做多次raycast检测。只有一个点和方向的话,也要套一层[]
# test on a sphere mesh mesh = trimesh.primitives.Sphere() # create some rays ray_origins = np.array([[0, 0, -5], [2, 2, -10]]) ray_directions = np.array([[0, 0, 1], [0, 0, 1]]) # run the mesh- ray test locations, index_ray, index_tri = mesh.ray.intersects_location( ray_origins=ray_origins, ray_directions=ray_directions) print(locations) # 打印相交的点的坐标
单个三角形的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) #load or load_mesh 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 # 计算raycast ray_origins = np.array([0,UAV_height,0]) # 3*1 ray_directions = np.array([0,-1,0]) # 3*1 # raycast方法一:trimesh t1 = time.time() locations, index_ray, index_tri = mesh.ray.intersects_location( ray_origins=[ray_origins], # n*3*1 ray_directions=[ray_directions]) # n*3*1 print(locations) t2 = time.time() print("Ran in {0:.3f} s".format(t2 - t1)) # raycast方法二: 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 installpyembree
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了~