ianmackenzie/elm-3d-camera

Camera type for 3D rendering and projection


Keywords
elm, matrix, opensolid, point, vector, webgl
License
MPL-2.0
Install
elm-package install ianmackenzie/elm-3d-camera 4.0.1

Documentation

NOTE

elm-3d-scene has not yet been updated to use elm-3d-camera 4.x, so in the meantime to avoid version conflicts you'll likely want to install elm-3d-scene before elm-3d-camera, e.g.

elm install ianmackenzie/elm-3d-scene
elm install ianmackenzie/elm-3d-camera

If you do run into a problem, try manually adjusting the version of elm-3d-camera in elm.json to 3.1.0 instead of 4.0.0.

elm-3d-camera

This package provides convenient ways to define and use perspective and orthographic cameras in 3D. It is based on elm-geometry and is used heavily by elm-3d-scene. It can also be used standalone to:

  • Construct WebGL model/view/projection matrices in a way that is more intuitive than using elm-explorations/linear-algebra directly
  • Perform 3D-to-2D projection of various elm-geometry values (points, line segments, triangles). This in turn allows you to do things like render simple 3D shapes by projecting them into 2D so that they can be drawn with SVG instead of WebGL.

Defining cameras

The functions in this package let you construct perspective or orthographic cameras in various different ways, for example:

import Angle
import Camera3d
import Length
import Point3d

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

Note that there are no functions for transforming (translating, rotating etc.) cameras - cameras are intended to be 'throwaway' values that you would construct on the fly when doing some rendering. For example, if in the above code you wanted to have an animated camera that tracked some moving object, you might store the camera and object positions in your model as Point3d values but then recreate the actual Camera3d value every frame.

WebGL rendering

Once you have a camera, you can use it to get WebGL view and projection matrices:

import WebGL.Matrices as WebGL

viewMatrix =
    WebGL.viewMatrix camera

projectionMatrix =
    WebGL.projectionMatrix camera
        { nearClipDepth = Length.meters 0.1
        , farClipDepth = Length.meters 100
        , aspectRatio = 16 / 9
        }

Projection to screen space

You can also use a Camera3d to project points, lines, and triangles from 3D to 2D. This allows you to, for example, do a perspective projection of 3D points and lines into 2D so that those points and lines can be rendered with SVG (taking advantage of SVG features like perfect circles and dashed lines which are difficult to do with WebGL):

Perspective projection

First, you must define the dimensions of the screen you want to project to; this should generally be of the form

screen =
    Rectangle2d.from bottomLeftCorner topRightCorner

Note that if you want browser DOM coordinates directly, you'll probably want to use something like

scree =
    Rectangle2d.from
        (Point2d.pixels 0 clientHeight)
        (Point2d.pixels clientWidth 0)

since in HTML 0 is the top and positive Y is down. I personally generally prefer working in 2D coordinate systems where positive Y is up (converting to DOM coordinates at the last possible moment), so my code that projects from 3D to 2D looks like this:

import Point3d.Projection as Point3d
import LineSegment3d.Projection as LineSegment3d

screen =
    Rectangle2d.from Point2d.origin
        (Point2d.pixels 800 600)

point2d =
    point3d |> Point3d.toScreenSpace camera screen

lineSegment2d =
    lineSegment3d
        |> LineSegment3d.toScreenSpace camera screen

(The Overlay.elm example uses an under-development Drawing2d module which works in a coordinate system where positive Y is up, converting to Y-down coordinates only when actually rendering to SVG internally.)

Roadmap

A few more features are planned:

  • More 3D-to-2D projections (directions, axes)
  • Construction of 3D cut planes from 2D on-screen lines

Questions? Comments?

Please open a new issue if you run into a bug, if any documentation is missing/incorrect/confusing, or if there's a new feature that you would find useful. For general questions about using elm-3d-camera, try:

  • Joining the #geometry or #webgl channels on the Elm Slack, or sending me (@ianmackenzie) a message - even if you don't have any particular questions right now, it would be great to know what you're hoping to do with the package! - Posting to the Elm Discourse forums

You can also find me on Twitter (@ianemackenzie), where I occasionally post elm-geometry-related stuff like demos or new releases. Have fun, and don't be afraid to ask for help!