nanoraster
0.7.1

Render a view sheet

Build a four-view contact sheet from one GLB, learning batch rendering, fitted cameras, and labels along the way.

Open Markdown

Build a labelled four-view contact sheet (front, right, top, and iso) from one GLB in a single render call.

Step 1: Render a single fitted view

Start with one view, so the camera controls are visible on their own. Frame the model explains direction and up.

import { directionFromOrbit, renderImage } from 'nanoraster';import { readFile, writeFile } from 'node:fs/promises';const glb = Uint8Array.from(await readFile('model.glb'));const front = await renderImage(glb, {  format: 'webp',  width: 512,  height: 512,  camera: {    framing: 'fit',    direction: directionFromOrbit({ azimuth: 90, elevation: 0 }),  },});await writeFile('front.webp', front.bytes);

Rendering…

 

advanced · direction XYZ

Verify: front.webp shows the gear face-on, teeth all around the rim.

Step 2: Switch to the batch call

A separate call for each view parses and uploads the GLB every time. renderImages does that once, then renders every view in the list. Start with two views. Add the rest in step 3. A sheet needs a tighter fit than a single hero image, so each preset uses margin: 0.05.

import { directionFromOrbit, renderImages } from 'nanoraster';import { readFile, writeFile } from 'node:fs/promises';const glb = Uint8Array.from(await readFile('model.glb'));const sheet = await renderImages(glb, {  format: 'webp',  width: 512,  height: 512,  views: [    {      id: 'front',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 90, elevation: 0 }),        margin: 0.05,      },    },    {      id: 'right',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 180, elevation: 0 }),        margin: 0.05,      },    },  ],});for (const { id, file } of sheet) {  await writeFile(file.name, file.bytes);  console.log(id, file.name);}

Rendering…

advanced · direction XYZ

Verify: two files appear. The console prints the IDs in the order you declared them:

front render-front.webp
right render-right.webp

The result filename carries the view id, not a counter.

Step 3: Add the top and iso views

The top view looks down the up axis. The iso view is the default three-quarter angle. axes draws a camera-aware marker in each image. This marker orients the top view once the image is on its own.

const sheet = await renderImages(glb, {  format: 'webp',  width: 512,  height: 512,  axes: true,  views: [    {      id: 'front',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 90, elevation: 0 }),        margin: 0.05,      },    },    {      id: 'right',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 180, elevation: 0 }),        margin: 0.05,      },    },    {      id: 'top',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 0, elevation: 90 }),        up: [0, 0, 1],        margin: 0.05,      },    },    {      id: 'iso',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 45, elevation: 30 }),        margin: 0.05,      },    },  ],});

Rendering…

advanced · direction XYZ

Verify: four files appear. render-top.webp looks down at the model. render-iso.webp uses the quick start's direction.

Step 4: Label each view

The renderer draws each label into the image, screen-upright. A sheet then stays readable once the images are separated from their filenames. A view carries a label when its entry sets label. There is no flag to set. Clear a view's label entry to remove its label. scaleBar adds the bottom-left bar, so the finished sheet also carries its own units.

const sheet = await renderImages(glb, {  format: 'webp',  width: 512,  height: 512,  axes: true,  scaleBar: true,  views: [    {      id: 'front',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 90, elevation: 0 }),        margin: 0.05,      },      label: 'Front',    },    {      id: 'right',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 180, elevation: 0 }),        margin: 0.05,      },      label: 'Right',    },    {      id: 'top',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 0, elevation: 90 }),        up: [0, 0, 1],        margin: 0.05,      },      label: 'Top',    },    {      id: 'iso',      camera: {        framing: 'fit',        direction: directionFromOrbit({ azimuth: 45, elevation: 30 }),        margin: 0.05,      },      label: 'Iso',    },  ],});

Rendering…

advanced · direction XYZ

Verify: each image carries its name in the top-left corner.

Step 5: Keep it reproducible

Run the script twice. Compare the files. The sheet regenerates byte-for-byte on the same host. How it works states what is and is not guaranteed across hosts.

On this page