3 Transforms and Cameras
dxtr edited this page 2026-10-14 19:30:49 +02:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Transforms and Cameras

Every function here returns a new matrix or vector, or writes into an optional last argument, result; see Results and Allocation. Read Conventions for the directions of the axes and of rotations.

Translation, rotation and scale

Function Returns the mat4 that
(mat4-translation offset) moves by the vec3 offset.
(mat4-scaling scale) scales by the vec3 scale, along each axis.
(mat4-rotation axis angle) rotates by angle about axis, which need not be normalised.
(mat4-rotation-x angle), -y, -z rotates about an axis.
(mat4-from-quat quaternion) rotates as a unit quaternion does.
(mat4-compose translation rotation scale) scales, then rotates by a unit quaternion, then translates: a node's transform.

mat4-compose makes in one step what (gm:mat4* T (gm:mat4* R S)) would:

(gm:mat4-compose (gm:make-vec3 :x 1 :y 2 :z 3) (gm:make-quat) (gm:make-vec3 :x 2 :y 2 :z 2))
;; => #(2.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 0.0 2.0 0.0 1.0 2.0 3.0 1.0)

Points and directions

Function Returns
(mat4-transform-point matrix point) The vec3 point rotated, scaled and translated by an affine matrix. The matrix's bottom row is ignored.
(mat4-transform-direction matrix direction) The vec3 direction rotated and scaled, without the translation.
(mat4-project-point matrix point) The point transformed and divided by the resulting W: through a view-projection matrix, the point in normalised device coordinates. Signals gmath-error if W is 0, as for a point in the camera's plane.
(gm:mat4-transform-point (gm:mat4-translation (gm:make-vec3 :x 1 :y 2 :z 3)) (gm:make-vec3))
;; => #(1.0 2.0 3.0)

Normals need the normal matrix rather than the model matrix where the scale differs between axes; see Matrices.

Cameras

mat4-look-at makes a view matrix, which maps the world into the camera's view space, where it looks down −Z with +Y up:

(gm:mat4-look-at eye target up) => mat4
;; A camera at Z = 5, looking at the origin: the origin is 5 in front of it.
(gm:mat4-transform-point (gm:mat4-look-at (gm:make-vec3 :z 5) (gm:make-vec3) (gm:make-vec3 :y 1))
                         (gm:make-vec3))
;; => #(0.0 0.0 -5.0)

up says which way is up on the screen; it must not point along the line from eye to target, and the two must differ, or gmath-error says so. A camera that looks straight down needs another up, such as −Z.

Projections

All three map into Vulkan's clip space, where Y points down, so the top of the view is at clip Y −1; and all three put the near plane at depth 1 and the far plane at 0. See Conventions.

Function Projects
(mat4-perspective fov-y aspect near far) in perspective, with the vertical field of view fov-y, in radians, the aspect ratio of width to height, and the distances to the near and far planes. Needs 0 < near < far.
(mat4-perspective-infinite fov-y aspect near) in perspective, with the far plane at infinity: depth falls from 1 at the near plane toward 0 with distance, and nothing is clipped for being far away.
(mat4-orthographic left right bottom top near far) the box from left to right, bottom to top, and from near to far in front of the camera, without perspective.
(let ((projection (gm:mat4-orthographic -2 2 -1 1 0 10)))
  (list (gm:mat4-project-point projection (gm:make-vec3 :x 2 :y 1))              ; near, top right
        (gm:mat4-project-point projection (gm:make-vec3 :x -2 :y -1 :z -10))))   ; far, bottom left
;; => (#(1.0 -1.0 1.0) #(-1.0 1.0 0.0))

(let ((projection (gm:mat4-perspective-infinite (gm:radians 60) 1 0.1)))
  (gm:vec3-z (gm:mat4-project-point projection (gm:make-vec3 :z -1000))))
;; => 1.00000005e-4

Model, view, projection

A shader takes the product of the three, applied right to left: the model's matrix first, then the camera's view, then the projection:

(let* ((model (gm:mat4-compose (gm:make-vec3 :z -2)
                               (gm:quat-from-axis-angle (gm:make-vec3 :y 1) (gm:radians 90))
                               (gm:make-vec3 :x 1 :y 1 :z 1)))
       (view (gm:mat4-look-at (gm:make-vec3 :y 1 :z 3)   ; the eye
                              (gm:make-vec3 :z -2)       ; looks at the model
                              (gm:make-vec3 :y 1)))      ; with +Y up
       (projection (gm:mat4-perspective (gm:radians 60) 16/9 0.1 100)))
  (gm:mat4-project-point (gm:mat4* projection (gm:mat4* view model)) (gm:make-vec3 :z 1)))
;; => #(0.19107175 1.4094049e-8 0.018630244)

2D

2D transforms have a page of their own, 2D Transforms: mat3x2, a 2D node's transform in six numbers, and the mat3s of homogeneous 2D points.