Skip to content

ray_mesh_intersect

ray_mesh_intersect(cam_pos, cam_dir, V, F, use_embree=True, C=None, W=None, CH=None, tri_ind=None, intersector=None)

Shoot a ray from a position and see where it crashes into a given mesh

Uses a bounding volume hierarchy to efficiently compute intersections of many different rays with a given mesh.

Parameters:

Name Type Description Default
cam_pos numpy double array

Matrix of camera positions

required
cam_dir numpy double array

Matrix of camera directions

required
V (n,d) numpy array

vertex list of a triangle mesh

required
F (m,3) numpy int array

face index list of a triangle mesh

required
use_embree bool, optional (default True)

Whether to use the optimized C++ Embree implementation of ray-mesh intersections. If False, uses GPyToolbox's native AABB tree and intersection queries. If True but GPyToolbox was built without Embree, warns and falls back to the native implementation.

True
C numpy double array, optional (default None)

Matrix of AABB box centers (if None and use_embree=False, will be computed)

None
W numpy double array, optional (default None)

Matrix of AABB box widths (if None and use_embree=False, will be computed)

None
CH numpy int array, optional (default None)

Matrix of child indeces (-1 if leaf node). If None and use_embree=False, will be computed

None
tri_ind numpy int array, optional (default None)

Vector of AABB element indices (-1 if not leaf node). If None and use_embree=False, will be computed

None
intersector gpytoolbox.ray_mesh_intersect_precompute, optional (default None)

Precomputed intersector built via gpytoolbox.ray_mesh_intersect_precompute(V, F). When provided (and use_embree=True), reuses the available Embree scene or portable AABB tree across calls instead of rebuilding it. V and F are still consulted only for shape; the actual queries go through the cached intersector.

None

Returns:

Name Type Description
ts list of doubles

The i-th entry of this list is the time it takes a ray starting at the i-th camera position with the i-th camera direction to hit the surface (inf if no hit is detected)

ids list of ints

The i-th entry is the index into F of the mesh element that the i-th ray hits (-1 if no hit is detected)

lambdas numpy double array

The i-th row contains the barycentric coordinates of where in the triangle the ray hit (all zeros is no hit is detected)

Examples:

Standard one-shot call (rebuilds the available acceleration structure internally each time):

from gpytoolbox import ray_mesh_intersect
v,f = gpytoolbox.read_mesh("test/unit_tests_data/cube.obj")
cam_pos = np.array([[1,0.1,0.1],[1,0.2,0.0]])
cam_dir = np.array([[-1,0,0],[-1,0,0]])
t, ids, l = ray_mesh_intersect(cam_pos,cam_dir,v,f)

When making many calls against the same mesh, build the intersector once and reuse it via the intersector= kwarg to avoid the O(n) construction cost on every call:

v,f = gpytoolbox.read_mesh("bunny.obj")
rmi = gpytoolbox.ray_mesh_intersect_precompute(v, f) # build once
for _ in range(num_iters):
    origins = 2*np.random.rand(num_rays,3)-1
    dirs = np.random.randn(num_rays,3)
    t, ids, l = gpytoolbox.ray_mesh_intersect(origins,dirs,v,f,intersector=rmi)
Source code in src/gpytoolbox/ray_mesh_intersect.py
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def ray_mesh_intersect(cam_pos,cam_dir,V,F,use_embree=True,C=None,W=None,CH=None,tri_ind=None,intersector=None):
    """Shoot a ray from a position and see where it crashes into a given mesh

    Uses a bounding volume hierarchy to efficiently compute intersections of many different rays with a given mesh.

    Parameters
    ----------
    cam_pos : numpy double array
        Matrix of camera positions
    cam_dir : numpy double array
        Matrix of camera directions
    V : (n,d) numpy array
        vertex list of a triangle mesh
    F : (m,3) numpy int array
        face index list of a triangle mesh
    use_embree : bool, optional (default True)
        Whether to use the optimized C++ Embree implementation of ray-mesh
        intersections. If False, uses GPyToolbox's native AABB tree and
        intersection queries. If True but GPyToolbox was built without
        Embree, warns and falls back to the native implementation.
    C : numpy double array, optional (default None)
        Matrix of AABB box centers (if None and use_embree=False, will be computed)
    W : numpy double array, optional (default None)
        Matrix of AABB box widths (if None and use_embree=False, will be computed)
    CH : numpy int array, optional (default None)
        Matrix of child indeces (-1 if leaf node). If None and use_embree=False, will be computed
    tri_ind : numpy int array, optional (default None)
        Vector of AABB element indices (-1 if *not* leaf node). If None and use_embree=False, will be computed
    intersector : gpytoolbox.ray_mesh_intersect_precompute, optional (default None)
        Precomputed intersector built via
        `gpytoolbox.ray_mesh_intersect_precompute(V, F)`. When provided (and
        `use_embree=True`), reuses the available Embree scene or portable AABB
        tree across calls instead of rebuilding it. V and F are still consulted
        only for shape; the actual queries go through the cached intersector.

    Returns
    -------
    ts : list of doubles
        The i-th entry of this list is the time it takes a ray starting at the i-th camera position with the i-th camera direction to hit the surface (inf if no hit is detected)
    ids : list of ints
        The i-th entry is the index into F of the mesh element that the i-th ray hits (-1 if no hit is detected)
    lambdas : numpy double array
        The i-th row contains the barycentric coordinates of where in the triangle the ray hit (all zeros is no hit is detected)

    Examples
    --------
    Standard one-shot call (rebuilds the available acceleration structure
    internally each time):
    ```python
    from gpytoolbox import ray_mesh_intersect
    v,f = gpytoolbox.read_mesh("test/unit_tests_data/cube.obj")
    cam_pos = np.array([[1,0.1,0.1],[1,0.2,0.0]])
    cam_dir = np.array([[-1,0,0],[-1,0,0]])
    t, ids, l = ray_mesh_intersect(cam_pos,cam_dir,v,f)
    ```

    When making many calls against the same mesh, build the intersector once
    and reuse it via the `intersector=` kwarg to avoid the O(n) construction
    cost on every call:
    ```python
    v,f = gpytoolbox.read_mesh("bunny.obj")
    rmi = gpytoolbox.ray_mesh_intersect_precompute(v, f) # build once
    for _ in range(num_iters):
        origins = 2*np.random.rand(num_rays,3)-1
        dirs = np.random.randn(num_rays,3)
        t, ids, l = gpytoolbox.ray_mesh_intersect(origins,dirs,v,f,intersector=rmi)
    ```
    """
    if use_embree:
        if intersector is not None:
            ts, ids, lambdas = intersector.intersect(cam_pos, cam_dir)
        else:
            import gpytoolbox_bindings
            if getattr(gpytoolbox_bindings, "_has_embree", True):
                ts, ids, lambdas = (
                    gpytoolbox_bindings._ray_mesh_intersect_cpp_impl(
                        cam_pos.astype(np.float64),
                        cam_dir.astype(np.float64),
                        V.astype(np.float64),
                        F.astype(np.int32),
                    )
                )
            else:
                warnings.warn(
                    "GPyToolbox was built without Embree, so ray_mesh_intersect "
                    "is falling back to its slower native AABB implementation. "
                    "Pass use_embree=False to silence this warning.",
                    stacklevel=2)
                use_embree = False
    if not use_embree:
        ts = np.inf*np.ones(cam_pos.shape[0])
        ids = -np.ones(cam_pos.shape[0],dtype=int)
        lambdas = np.zeros((cam_pos.shape[0],3))
        # print("building tree")
        if ((C is None) or (W is None) or (tri_ind is None) or (CH is None)):
            C,W,CH,_,_,tri_ind,_ = initialize_aabbtree(V,F=F)
        # print("built tree")
        # print("computing distances")
        for i in range(cam_pos.shape[0]):
            trav = ray_mesh_intersect_traversal(cam_pos[i,:],cam_dir[i,:],V,F)
            traverse_fun = trav.traversal_function
            add_to_queue_fun = trav.add_to_queue
            _ = traverse_aabbtree(C,W,CH,tri_ind,None,traverse_fun,add_to_queue=add_to_queue_fun)
            ts[i] = trav.t
            ids[i] = np.squeeze(trav.id)
            lambdas[i,:] = trav.lmbd
        # print("computed distances")
    return ts, ids, lambdas