nanoraster
0.7.1

Frame the model

Choose a fitted camera direction, screen-up vector, projection, and margin while nanoraster solves placement from referenced geometry.

Open Markdown

With a fitted camera, you choose an orientation. nanoraster solves the optical target, distance, and clip planes from indexed geometry that can be drawn. Use it for thumbnails, contact sheets, and any capture that must keep the whole subject in frame.

Set direction and screen up

direction points from the subject toward the camera. up establishes screen vertical and preserves roll. Both are Cartesian vectors in the declared caller world. The renderer ignores their magnitudes. Fitting may translate the camera and optical target together without changing orientation.

Omit up on a fit camera and it becomes the declared world.up. Supply up to roll the camera. A plan view's direction runs along that same axis and names no roll. Screen-up comes from world.forward instead, so a plan view needs no up.

import { directionFromOrbit, renderImage } from 'nanoraster';const world = { up: '+z', forward: '-y', unit: 'meter' } as const;const image = await renderImage(glb, {  format: 'webp',  world,  camera: {    framing: 'fit',    direction: directionFromOrbit({ azimuth: 45, elevation: 30 }, world),  },});

Rendering…

 

advanced · direction XYZ

This worked request authors a three-quarter view in Z-up metres. nanoraster converts it once to the GLB frame. The live azimuth and elevation controls move the two angles in the copyable code. Expand advanced · direction XYZ for raw vector entry.

Turn angles into directions

directionFromOrbit converts orbit angles to the Cartesian direction a fitted camera takes. orbitFromDirection inverts it. Both resolve their axes from the world you pass, so pass the same world the request declares, as the example above does. Read a direction back the same way:

import { orbitFromDirection } from 'nanoraster';

const { azimuth, elevation } = orbitFromDirection([0.61, -0.61, 0.5], world);

Azimuth is the angle in the world horizontal plane. It is zero on world.forward and positive toward the viewer's right. Elevation is the angle above it. This convention is the pair's own, not the one the removed phi/theta options carried. The wire stays Cartesian: a turntable is a sequence of azimuths.

Choose the fitted projection

Perspective is the default. Its verticalFieldOfView is the exact rectilinear angle in degrees. An orthographic fit uses the same referenced geometry and margin but removes depth scaling.

const perspective = {
  framing: 'fit',
  direction: [1, 0.4, 1],
  projection: { kind: 'perspective', verticalFieldOfView: 52 },
} as const;

const plan = {
  framing: 'fit',
  direction: [0, 1, 0],
  projection: { kind: 'orthographic' },
} as const;

Fit framing has no zoom by design. margin is its one size control. Use a fixed camera when a caller must own magnification.

Set the empty border

camera.margin is the minimum empty fraction around contained fitted geometry, from 0 to 0.5. The default is 0.1.

const image = await renderImage(glb, {
  format: 'webp',
  camera: { framing: 'fit', margin: 0.02 },
});

Fitting uses width / height, so set the output size before you tune the margin. Annotations may loosen a fitted camera to keep the model clear of their fixed screen-space slots. The non-limiting image axis may have more whitespace. Annotations never change a fixed camera.

Very wide perspective

The 1..179 degree range is rectilinear. Near 180 degrees, positive front clearance can add whitespace beyond margin. Use a narrower field for photographic framing. Fisheye is a different projection.

On this page