Skip to content

Cameras

Generally speaking, the camera controls the range of coordinates that are shown and from what perspective they are shown. For example, a 2D camera will give us a 2D perspective, meaning one where x extends right, y extends up and z extend towards the viewer. The range of x and y values shown on the screen is also controlled by the camera.

Overview

The term "camera" in Makie can be quite confusing as there are multiple things controlling, processing and storing camera related objects.

Before Blocks like Axis were introduced, scenes were the main building block users interacted with. Every Block still relies on them internally, and LScene specifically is just a light wrapper around Scene. Up until Makie 0.24 every scene contained two fields related to cameras - camera(scene) and cameracontrols(scene). The camera(scene)::Camera stores camera matrices like projectionview. Over time it has grown to also include eyeposition, view_direction and upvector, which describe the orientation and placement of the camera. The cameracontrols(scene) <: AbstractCamera are, as the name implies, an object that controls the camera. They generate the matrices stored in camera(scene) which means they are responsible for the range of visible coordinates and the perspective from which they are shown. They also process user input like mouse drags if that has an effect on the camera, and they may include settings.

Blocks have their own interaction system, which is also used to control the camera. This effectively replaces cameracontrols(scene). For example, if you check cameracontrols(ax.scene) for Axis and PolarAxis you will find an EmptyCamera. LScene still uses cameracontrols(scene) as it is just a wrapper around Scene.

Makie 0.24 brought in another layer with the scenes ComputeGraph in scene.compute. Currently the compute graph is fed by camera(scene) to (lazily) calculate all the projection matrices needed to resolve the space and markerspace attributes. It is effectively now the final output of the camera pipeline.

Camera Controls

The cameracontrols(scene) control how plot data is shown in Scene and LScene. They determine 2D vs 3D projections, the range of shown coordinates, the perspective and how these things react to user interaction.

Currently, we offer the following camera controllers/constructors

  • campixel!: A 2D camera using pixel coordinates

  • cam_relative!: A 2D camera using relative (0..1) coordinates

  • cam2d!: A 2D camera using dynamic coordinate ranges

  • Camera3D: A general, highly adjustable 3D camera

  • cam3d!: A Camera3D with default settings

  • cam3d_cad!: A Camera3D with CAD-like settings

To specify the camera controller you can set the camera attribute in a Scene.

julia
Scene(..., camera = cam3d!)
LScene(..., scenekw = (camera = cam3d!, ))

You can replace and existing camera in a scene:

julia
scene = Scene(...)
cam3d!(scene)

ax = LScene(...)
cam3d!(ax.scene)

Pixel Camera

The pixel camera (campixel!) projects the scene in pixel space, i.e. each integer step in the displayed data will correspond to one pixel. There are no controls for this camera. The z clipping limits are set to (-10_000, 10_000).

Relative Camera

The relative camera (cam_relative!) projects the scene into a 0..1 by 0..1 space. There are no controls for this camera. The z clipping limits are set to (-10_000, 10_000).

2D Camera

The 2D camera (cam2d!) uses an orthographic projection with a fixed rotation and aspect ratio. You can set the following attributes via keyword arguments in cam2d! or by accessing the camera struct cam = cameracontrols(scene):

  • zoomspeed = 0.10f0 sets the speed of mouse wheel zooms.

  • zoombutton = nothing sets an additional key that needs to be pressed in order to zoom. Defaults to no key.

  • panbutton = Mouse.right sets the mouse button that needs to be pressed to translate the view.

  • selectionbutton = (Keyboard.space, Mouse.left) sets a set of buttons that need to be pressed to perform rectangle zooms.

The z clipping limits are set to (-10_000, 10_000).

Warning

This camera is not used by Axis. It is used, by default, for 2D LScenes and Scenes.

3D Camera

Camera3D is a generalized 3D camera with a large number of options. cam3d! and cam3d_cad! are specialized versions. The former is the default camera for 3D scenes. The latter is a camera that tries to mimic CAD-style cameras.

Makie.Camera3D Type
julia
Camera3D(scene[; kwargs...])

Sets up a 3D camera with mouse and keyboard controls.

The behavior of the camera can be adjusted via keyword arguments or the fields settings and controls.

Settings

Settings include anything that isn't a mouse or keyboard button.

  • projectiontype = Perspective sets the type of the projection. Can be Orthographic or Perspective.

  • rotation_center = :lookat sets the default center for camera rotations. Currently allows :lookat or :eyeposition.

  • fixed_axis = true: If true panning uses the (world/plot) z-axis instead of the camera up direction.

  • zoom_shift_lookat = true: If true keeps the data under the cursor when zooming.

  • cad = false: If true rotates the view around lookat when zooming off-center.

  • clipping_mode = :adaptive: Controls how near and far get processed. Options:

    • :static passes near and far as is

    • :adaptive scales near by norm(eyeposition - lookat) and passes far as is

    • :view_relative scales near and far by norm(eyeposition - lookat)

    • :bbox_relative scales near and far to the scene bounding box as passed to the camera with update_cam!(..., bbox). (More specifically far = 1 is scaled to the furthest point of a bounding sphere and near is generally overwritten to be the closest point.)

  • center = true: Controls whether the camera placement gets reset when calling center!(scene), which is called when a new plot is added.

  • keyboard_rotationspeed = 1.0 sets the speed of keyboard based rotations.

  • keyboard_translationspeed = 0.5 sets the speed of keyboard based translations.

  • keyboard_zoomspeed = 1.0 sets the speed of keyboard based zooms.

  • mouse_rotationspeed = 1.0 sets the speed of mouse rotations.

  • mouse_translationspeed = 0.5 sets the speed of mouse translations.

  • mouse_zoomspeed = 1.0 sets the speed of mouse zooming (mousewheel).

  • circular_rotation = (false, false, false) enables circular rotations for (fixed x, fixed y, fixed z) rotation axis. (This means drawing a circle with your mouse around the center of the scene will result in a continuous rotation.)

Controls

Controls include any kind of hotkey setting.

  • up_key = Keyboard.r sets the key for translations towards the top of the screen.

  • down_key = Keyboard.f sets the key for translations towards the bottom of the screen.

  • left_key = Keyboard.a sets the key for translations towards the left of the screen.

  • right_key = Keyboard.d sets the key for translations towards the right of the screen.

  • forward_key = Keyboard.w sets the key for translations into the screen.

  • backward_key = Keyboard.s sets the key for translations out of the screen.

  • zoom_in_key = Keyboard.u sets the key for zooming into the scene (translate eyeposition towards lookat).

  • zoom_out_key = Keyboard.o sets the key for zooming out of the scene (translate eyeposition away from lookat).

  • increase_fov_key = Keyboard.b sets the key for increasing the fov.

  • decrease_fov_key = Keyboard.n sets the key for decreasing the fov.

  • pan_left_key = Keyboard.j sets the key for rotations around the screens vertical axis.

  • pan_right_key = Keyboard.l sets the key for rotations around the screens vertical axis.

  • tilt_up_key = Keyboard.i sets the key for rotations around the screens horizontal axis.

  • tilt_down_key = Keyboard.k sets the key for rotations around the screens horizontal axis.

  • roll_clockwise_key = Keyboard.e sets the key for rotations of the screen.

  • roll_counterclockwise_key = Keyboard.q sets the key for rotations of the screen.

  • fix_x_key = Keyboard.x sets the key for fixing translations and rotations to the (world/plot) x-axis.

  • fix_y_key = Keyboard.y sets the key for fixing translations and rotations to the (world/plot) y-axis.

  • fix_z_key = Keyboard.z sets the key for fixing translations and rotations to the (world/plot) z-axis.

  • reset = Keyboard.left_control & Mouse.left sets the key for resetting the camera. This equivalent to calling center!(scene).

  • reposition_button = Keyboard.left_alt & Mouse.left sets the key for focusing the camera on a plot object.

  • translation_button = Mouse.right sets the mouse button for drag-translations. (up/down/left/right)

  • scroll_mod = true sets an additional modifier button for scroll-based zoom. (true being neutral)

  • rotation_button = Mouse.left sets the mouse button for drag-rotations. (pan, tilt)

Other kwargs

Some keyword arguments are used to initialize fields. These include

  • eyeposition = Vec3d(3): The position of the camera.

  • lookat = Vec3d(0): The point the camera is focused on.

  • upvector = Vec3d(0, 0, 1): The world direction corresponding to the up direction of the screen.

  • fov = 45.0 is the field of view. This is irrelevant if the camera uses an orthographic projection.

  • near = automatic sets the position of the near clip plane. Anything between the camera and the near clip plane is hidden. Must be greater 0. Usage depends on clipping_mode.

  • far = automatic sets the position of the far clip plane. Anything further away than the far clip plane is hidden. Usage depends on clipping_mode. Defaults to 1 for clipping_mode = :bbox_relative, 2 for :view_relative or a value derived from limits for :static.

Note that updating these observables in an active camera requires a call to update_cam(scene) for them to be applied. For updating eyeposition, lookat and/or upvector update_cam!(scene, eyeposition, lookat, upvector = Vec3d(0,0,1)) is preferred.

The camera position and orientation can also be adjusted via the functions

  • translate_cam!(scene, v) will translate the camera by the given vector v.

  • rotate_cam!(scene, angles) will rotate the camera around its axes with the corresponding angles. The first angle will rotate around the cameras "right" that is the screens horizontal axis, the second around the up vector/vertical axis or Vec3d(0, 0, +-1) if fixed_axis = true, and the third will rotate around the view direction i.e. the axis out of the screen. The rotation respects the current rotation_center of the camera.

  • zoom!(scene, zoom_step) will change the zoom level of the scene without translating or rotating the scene. zoom_step applies multiplicatively to cam.zoom_mult which is used as a multiplier to the fov (perspective projection) or width and height (orthographic projection).

source

Warning

This camera is not used by Axis3. It is used, by default, for 3D LScenes and Scenes.

Camera and Projections

Sometimes you may need to interact with camera matrices to project data into a different space. As of Makie 0.24 you can get all the relevant matrices for this from scene.compute using the helper functions:

  • Makie.get_projectionview(scene, space)

  • Makie.get_projection(scene, space)

  • Makie.get_view(scene, space)

  • Makie.get_preprojection(scene, space, markerspace)

  • Makie.get_space_to_space_matrix(scene, input_space, output_space)

Example - Visualizing the camera's view box

julia
using GLMakie
using GeometryBasics, LinearAlgebra

function frustum_snapshot(cam)
    r = Rect3f(-1, -1, -1, 2, 2, 2)
    rect_ps = Makie.convert_arguments(Lines, r)[1]
    inv_pv = inv(cam.projectionview[])
    return map(rect_ps) do p
        p = inv_pv * to_ndim(Point4f, p, 1)
        return p[Vec(1,2,3)] / p[4]
    end
end


ex = Point3f(1,0,0)
ey = Point3f(0,1,0)
ez = Point3f(0,0,1)

fig = Figure()

# Set up Scene shown by a camera
scene = LScene(fig[1, 1])
cc = Makie.Camera3D(scene.scene, projectiontype = Makie.Perspective, center = false)

linesegments!(scene, Rect3f(Point3f(-1), Vec3f(2)), color = :black)
linesegments!(scene,
    [-ex, ex, -ey, ey, -ez, ez],
    color = [:red, :red, :green, :green, :blue, :blue]
)
center!(scene.scene)

cam = scene.scene.camera
eyeposition = cc.eyeposition
lookat = cc.lookat
frustum = map(pv -> frustum_snapshot(cam), cam.projectionview)

# Set up scene visualizing the cameras view
scene = LScene(fig[1, 2])
_cc = Makie.Camera3D(scene.scene, projectiontype = Makie.Orthographic, center = false)
lines!(scene, frustum, color = :blue, linestyle = :dot)
scatter!(scene, eyeposition, color = :black)
scatter!(scene, lookat, color = :black)

linesegments!(scene,
    [-ex, ex, -ey, ey, -ez, ez],
    color = [:red, :red, :green, :green, :blue, :blue]
)
linesegments!(scene, Rect3f(Point3f(-1), Vec3f(2)), color = :black)

# Tweak initial camera position
update_cam!(scene.scene, Vec3f(4.5, 2.5, 3.5), Vec3f(0))
update_cam!(scene.scene, Vec3f(6, 8, 5), Vec3f(0))

fig

General Remarks

Buttons passed to the 2D and 3D camera are forwarded to ispressed. As such you can pass false to disable an interaction, true to ignore a modifier, any button, collection of buttons or even logical expressions of buttons. See the events documentation for more details.