Note
Go to the end to download the full example code.
Graphics Background#
Plotting various transformations and concepts from computer graphics
# isort: skip_file
# isort: off
import numpy as np
import pyvista as pv
import mirage as mr
import mirage.vis as mrv
pv.set_plot_theme('document')
def orthographic_frustum_as_pyvista_mesh(
origin, direction, up, width, near, far
) -> pv.PolyData:
box = pv.Box()
box.points[:, 0] *= width
box.points[:, 1] *= width
box.points[:, 2] *= far - near
v3 = mr.hat(direction)
v1 = mr.hat(np.cross(up, v3))
v2 = mr.hat(np.cross(v3, v1))
dcm = np.vstack((v1, v2, v3)).T
box.points = mr.stack_mat_mult_vec(dcm, box.points)
box.points += origin + direction * (far - near)
return box
camera = pv.Camera()
near_range = 0.3
far_range = 0.9
camera.clipping_range = (near_range, far_range)
camera.position = (1.0, -1.0, 0.0)
camera.up = (0.0, 1.1, 1.0)
unit_vector = mr.hat(np.array(camera.direction))
perspective_frustum = camera.view_frustum(1.0)
position = camera.position
focal_point = camera.focal_point
line = pv.Line(position, focal_point)
bunny_obj = mr.SpaceObject('stanford_bunny.obj')
bunny = bunny_obj._mesh
xyz = camera.position + unit_vector * 0.6 - np.mean(bunny.points, axis=0)
bunny.points += np.array(xyz)
P = mr.perspective_projection_matrix(camera.view_angle, 1, near_range, far_range)
W = np.eye(4)
M = np.eye(4)
M[3, :3] = -xyz
V = mr.look_at_matrix(camera.position, camera.focal_point, camera.up)
MVP = P @ V @ M
ortho_frustum = orthographic_frustum_as_pyvista_mesh(
camera.position, unit_vector, camera.up, 0.1, near_range, far_range
)
Plotting the camera view frustum for perspective projection
pl = pv.Plotter(window_size=(2000, 1300), shape=(2, 1))
for i, frustum in enumerate([perspective_frustum, ortho_frustum]):
clipping_plane_opacity = (
np.abs(
mr.dot(
unit_vector.reshape(-1, 3), frustum.compute_normals()['Normals']
).flatten()
)
> 0.99
) * 0.2
pl.subplot(i, 0)
mrv.render_spaceobject(pl, bunny_obj)
pl.add_mesh(frustum, style='wireframe', line_width=5, color='k')
pl.add_mesh(frustum, color='orange', opacity=clipping_plane_opacity, line_width=5)
pl.add_mesh(line, color='k', line_width=5)
if i == 0:
pl.add_text(
'Perspective Projection',
font_size=25,
position='upper_edge',
color='k',
font='courier',
)
else:
pl.add_text(
'Orthographic Projection',
font_size=25,
position='upper_edge',
color='k',
font='courier',
)
if i == 0:
pl.add_point_labels(
[
position,
camera.position + unit_vector * near_range,
camera.position + unit_vector * far_range,
focal_point,
],
['$R_{cam}$', 'Near plane', 'Far plane', '$T_{cam}$'],
margin=0,
fill_shape=True,
font_size=30,
shape_color='white',
point_color='red',
text_color='black',
always_visible=True,
)
else:
pl.add_point_labels(
[
position,
focal_point,
],
['$R_{cam}$', '$T_{cam}$'],
margin=0,
fill_shape=True,
font_size=30,
shape_color='white',
point_color='red',
text_color='black',
always_visible=True,
)
near_range = 0.01
bunny_on_near_plane = bunny.copy()
x = mr.stack_mat_mult_vec(
P @ V @ M,
np.hstack(
(bunny_on_near_plane.points, np.ones((bunny_on_near_plane.n_points, 1)))
),
)
bunny_on_near_plane.points = x[:, :3] / x[:, [3]]
bunny_on_near_plane.points[:, -1] = 0
bunny_on_near_plane.points *= near_range * mr.tand(camera.view_angle / 2) / 2
bunny_on_near_plane.points = mr.stack_mat_mult_vec(
V[:3, :3].T, bunny_on_near_plane.points
)
bunny_on_near_plane.rotate_vector(unit_vector, 180, inplace=True)
bunny_on_near_plane.points += camera.position + unit_vector * near_range
pl.add_mesh(bunny_on_near_plane, opacity=0.5)
pl.add_mesh(bunny_on_near_plane, opacity=0.1, color='k', style='wireframe')
mrv.plot_basis(
pl, M[:3, :3].T, 'M', origin=np.mean(bunny.points, axis=0), scale=0.2
)
mrv.plot_basis(pl, W[:3, :3].T, 'W', origin=np.zeros(3), scale=0.2)
mrv.plot_basis(pl, V[:3, :3].T, 'V', origin=camera.position, scale=0.2)
mrv.plot_arrow(
pl, camera.position, camera.up, label='$U_{cam}$', scale=0.15, color='c'
)
pl.link_views()
pl.camera.position = (1.1, 1.1, -0.5)
pl.camera.focal_point = camera.position + unit_vector * far_range
pl.camera.up = (0.0, 0.0, 1.0)
pl.camera.zoom(1.3)
pl.show()

Total running time of the script: (0 minutes 0.462 seconds)