differt.geometry.viewing_frustum#
- viewing_frustum(viewing_vertex, world_vertices, *, active_vertices=None, reduce=False)[source]#
Compute the viewing frustum as seen by one viewer.
The frustum is a region, expressed in spherical coordinates, see Spherical coordinates, that fully contains the world vertices.
Warning
The frustum may present wrong results along the polar axis.
We are still looking at a better way to compute the frustum, so feel free to reach out if your have any suggestion.
- Parameters:
viewing_vertex (
Float[ArrayLike, '*#batch 3']) – The coordinates of the viewer (i.e., camera).world_vertices (
Float[ArrayLike, '*#batch num_vertices 3']) – The array of world coordinates.active_vertices (
Bool[ArrayLike, '*#batch num_vertices']|None) – An optional mask to select which vertices to consider when computing the frustum.reduce (
bool) – Whether to reduce batch dimensions.
- Return type:
- Returns:
The extents (min. and max. values) of the viewing frustum.
Examples
The following example shows how to launch rays in a limited region of space, to avoid launching rays where no triangles would be hit.
>>> from differt.geometry import ( ... fibonacci_lattice, ... viewing_frustum, ... TriangleMesh, ... ) >>> from differt.plotting import draw_rays, reuse, draw_markers >>> >>> with reuse("plotly") as fig: ... tx = jnp.array([0.0, 0.0, 0.0]) ... key = jax.random.key(1234) ... draw_markers(tx.reshape(-1, 3), labels=["tx"], showlegend=False) ... for mesh in ( ... TriangleMesh.box(with_top=True).translate(tx).iter_objects() ... ): ... key, key_color = jax.random.split(key, 2) ... color = r, g, b = jax.random.randint(key_color, (3,), 0, 256) ... center = mesh.bounding_box.mean(axis=0) ... mesh = mesh.translate(5 * (center - tx)).set_face_colors(color) ... mesh.plot(opacity=0.5) ... ... frustum = viewing_frustum( ... tx, mesh.triangle_vertices.reshape(-1, 3) ... ) ... ray_origins, ray_directions = jnp.broadcast_arrays( ... tx, fibonacci_lattice(20, frustum=frustum) ... ) ... ray_origins += 0.5 * ray_directions ... ray_directions *= 2.5 # Scale rays length before plotting ... draw_rays( ... ray_origins, ... ray_directions, ... color=f"rgb({r:f},{g:f},{b:f})", ... showlegend=False, ... ) >>> fig
This second example shows what happens if you compute the frustum on all the objects at the same time, instead of computing one frustum per object (i.e., face).
>>> with reuse("plotly") as fig: ... tx = jnp.array([0.0, 0.0, 0.0]) ... world_vertices = jnp.empty((0, 3)) ... draw_markers(tx.reshape(-1, 3), labels=["tx"], showlegend=False) ... for mesh in ( ... TriangleMesh ... .box(with_top=True) ... .translate(tx) ... .set_face_colors(jnp.array([1.0, 0.0, 0.0])) ... .iter_objects() ... ): ... center = mesh.bounding_box.mean(axis=0) ... mesh = mesh.translate(5 * (center - tx)) ... mesh.plot(opacity=0.5) ... ... world_vertices = jnp.concatenate( ... (world_vertices, mesh.triangle_vertices.reshape(-1, 3)), ... axis=0, ... ) ... ... frustum = viewing_frustum(tx, world_vertices) ... ray_origins, ray_directions = jnp.broadcast_arrays( ... tx, fibonacci_lattice(20 * 6, frustum=frustum) ... ) ... ray_origins += 0.5 * ray_directions ... ray_directions *= 2.5 # Scale rays length before plotting ... draw_rays( ... ray_origins, ... ray_directions, ... color="red", ... showlegend=False, ... ) >>> fig
While the rays cover all the objects, many of them are launching in spatial regions where there is not object to hit.
This third example shows a scenario where TX is far from the mesh, where computing the frustum becomes very suitable.
>>> with reuse("plotly") as fig: ... tx = jnp.array([30.0, 0.0, 20.0]) ... draw_markers(tx.reshape(-1, 3), labels=["tx"], showlegend=False) ... mesh = TriangleMesh.box( ... width=10.0, length=20.0, height=3.0, with_top=True ... ).set_face_colors(jnp.array([1.0, 0.0, 0.0])) ... mesh.plot(opacity=0.5) ... ... frustum = viewing_frustum(tx, mesh.triangle_vertices.reshape(-1, 3)) ... ray_origins, ray_directions = jnp.broadcast_arrays( ... tx, fibonacci_lattice(20 * 6, frustum=frustum) ... ) ... ray_origins += 0.5 * ray_directions ... ray_directions *= 40.0 # Scale rays length before plotting ... draw_rays( ... ray_origins, ... ray_directions, ... color="red", ... showlegend=False, ... ) >>> fig