ianmackenzie / elm-3d-camera / Camera3d

A Camera3d is a perspective or orthographic camera in 3D space. This module contains functions for:


type Camera3d units coordinates

A camera in 3D space, using particular units (usually meters) within a particular coordinate system (generally a user-defined type like WorldCoordinates).


type Projection
    = Perspective
    | Orthographic

Defines whether a camera uses perspective or orthographic projection.


type FieldOfView units

Defines the vertical field of view of a camera. Strictly speaking, for a perspective camera the field of view should be defined as an angle and for an orthographic camera it should be defined as a height. Practically speaking, however, it is usually possible (and often useful) to convert between the two.

Given a focal distance it is possible (with a bit of trigonometry) to compute the field of view angle from the field of view height, or vice versa:

Field of view

All the camera construction functions in this module either compute the focal distance automatically (e.g. as the distance from an eye point to a focal point), or (in the case of the with constructor) require you to provide one explicitly.

As a result, this module allows you to specify field of view as either an angle or a height for either perspective or orthographic cameras, and any necessary conversions will be done automatically.

angle : Angle -> FieldOfView units

Specify vertical field of view as an angle. For an orthographic camera, this will be converted to a height at the camera's focal distance.

height : Quantity Basics.Float units -> FieldOfView units

Specify vertical field of view as a height. For a perspective camera, this will be converted to an angle at the camera's focal distance.

Constructors

lookAt : { eyePoint : Point3d units coordinates, focalPoint : Point3d units coordinates, upDirection : Direction3d coordinates, projection : Projection, fov : FieldOfView units } -> Camera3d units coordinates

Construct a Camera3d at the given eye point looking towards the given focal point, with the given global up direction (which will typically be Direction3d.positiveZ or Direction3d.positiveY). The focal distance will be computed as the distance from the eye point to the focal point. For example, to construct a camera at the point (10, 0, 5) looking towards the origin:

camera =
    Camera3d.lookAt
        { eyePoint = Point3d.meters 10 0 5
        , focalPoint = Point3d.origin
        , upDirection = Direction3d.positiveZ
        , projection = Camera3d.Perspective
        , fov = Camera3d.angle (Angle.degrees 30)
        }

Camera3d.eyePoint camera
--> Point3d.meters 10 0 5

Camera3d.viewDirection camera
--> Direction3d.xz (Angle.degrees -153.43)

That is likely all you need to know but if you are interested in the details and corner cases, read on!

The view direction of the returned camera will point from the eye point to the focal point. The Y direction will be chosen to be as close to the global up direction as possible (the camera will not have any 'roll') and the X direction will point to the right.

If the direction from the eye point to the focal point is parallel to the global up direction (that is, the camera represents looking straight up or straight down) then the X and Y directions will be chosen arbitrarily.

If the given eye point and focal point are coincident (so that there is no well- defined view direction), then the returned camera will have its Y direction set to the global up direction and its X and view directions will be chosen arbitrarily.

orbit : { focalPoint : Point3d units coordinates, groundPlane : SketchPlane3d units coordinates defines, azimuth : Angle, elevation : Angle, distance : Quantity Basics.Float units, projection : Projection, fov : FieldOfView units } -> Camera3d units coordinates

Construct a Camera3d looking at the given focal point, the given distance away. The direction from the focal point to the eye point is defined by the given azimuth and elevation angles, which are with respect to the given ground plane (the position of the ground plane does not matter, only its orientation). For example,

Camera3d.orbit
    { focalPoint = Point3d.meters 0 0 1
    , groundPlane = SketchPlane3d.xy
    , azimuth = Angle.degrees 0
    , elevation = Angle.degrees 45
    , distance = Length.meters 10
    , projection = Camera3d.Perspective
    , fov = Camera3d.angle (Angle.degrees 30)
    }

is equivalent to

Camera3d.lookAt
    { focalPoint = Point3d.meters 0 0 1
    , eyePoint = Point3d.meters 7.071 0 7.071
    , upDirection = Direction3d.z
    , projection = Camera3d.Perspective
    , fov = Camera3d.angle (Angle.degrees 30)
    }

As the name suggests, Camera3d.orbit is useful for making orbiting cameras; you can orbit around the focal point by changing just the azimuth, and rotate up and down by changing just the elevation.

orbitZ : { focalPoint : Point3d units coordinates, azimuth : Angle, elevation : Angle, distance : Quantity Basics.Float units, projection : Projection, fov : FieldOfView units } -> Camera3d units coordinates

A special case of orbit for orbiting around a Z axis through the given focal point. This corresponds to setting groundPlane to SketchPlane3d.xy, so azimuth is measured from the X axis towards the Y axis and elevation is measured up from the XY plane. Not related to the classic soft drink.

isometric : { focalPoint : Point3d units coordinates, distance : Quantity Basics.Float units, projection : Projection, fov : FieldOfView units } -> Camera3d units coordinates

Construct a camera looking at the given focal point, the given distance away, such that a set of XYZ axes at that point will appear to have:

isometricElevation : Angle

Not actually a constructor, but a useful value (approximately 35.26 degrees) to use when constructing cameras using orbit or orbitZ: using this as the elevation value will result in an isometric camera if azimuth is set to 45 degrees. Said another way, this is the elevation angle of a vector with components (1, 1, 1).

with : { viewPlane : SketchPlane3d units coordinates defines, focalDistance : Quantity Basics.Float units, projection : Projection, fov : FieldOfView units } -> Camera3d units coordinates

Construct a camera directly from:

This corresponds most directly to the internal representation of a Camera3d and is the most flexible, but usually also the most akward to use.

Properties

eyePoint : Camera3d units coordinates -> Point3d units coordinates

Get the location of a camera.

viewDirection : Camera3d units coordinates -> Direction3d coordinates

Get the direction that a camera is looking in (the direction 'into the screen').

viewPlane : Camera3d units coordinates -> SketchPlane3d units coordinates defines

The view plane of a camera is a SketchPlane3d where:

frame : Camera3d units coordinates -> Frame3d units coordinates defines

The frame of a camera is a Frame3d where:

focalDistance : Camera3d units coordinates -> Quantity Basics.Float units

The focal distance of the camera is used to convert between angle-based and height-based field of view. This generally corresponds to the distance from the camera to the object currently being looked at/focused on (which is often also the center of rotation/orbit point for the camera).

projection : Camera3d units coordinates -> Projection

Get the projection type of a camera.

fovAngle : Camera3d units coordinates -> Angle

Get a camera's vertical field of view as an angle. If necessary, this will be computed from a height-based field of view using the camera's focal distance.

fovHeight : Camera3d units coordinates -> Quantity Basics.Float units

Get a camera's vertical field of view as a height. If necessary, this will be computed from an angle-based field of view using the camera's focal distance.

Ray casting

ray : Camera3d units coordinates -> Rectangle2d screenUnits screenCoordinates -> Point2d screenUnits screenCoordinates -> Axis3d units coordinates

Given a camera, a rectangle defining the shape and size of a screen, and a 2D point within that screen, calculate the corresponding 3D ray as an Axis3d. Conceptually, the ray will pass through the given point on the screen and will have direction equal to the viewing direction at that point.

For a perspective camera, the origin of the ray will be constant (always equal to the camera's eye point) and the direction will vary depending on the 2D screen point. For an orthographic camera, the direction of the ray will be constant (the view direction of the camera) but the origin will vary depending on the 2D screen point.

Advanced

frustumSlope : Camera3d units coordinates -> Basics.Float

Half the fovAngle, expressed as a slope instead of an angle. This is useful for various low-level camera operations such as computing projection matrices.