Labels and billboards#
Labels are scene geometry. They move with their parents and use the same textured triangles, clipping, depth, and picking as other meshes on WebGPU and software. Wireframe uses depth-tested terminal text.
id, err := scene.SetLabel("peak", nt3d.Label{
Text: "Peak\nz = 0.60",
Pixels: true, Height: 32,
Color: color.RGBA{245, 248, 255, 255},
Background: color.RGBA{24, 32, 48, 255},
Padding: 2, Border: 1,
BorderColor: color.RGBA{90, 160, 210, 255},
})
// Handle errors from each operation.
err = scene.Reparent(id, terrainNode)
err = scene.SetTransform(id, nt3dmath.Translation(nt3dmath.Vec3{Z: .8}))Height is the whole label’s height. With Pixels, it stays the same size
while zooming and supersampling; otherwise it is in world units. Zero height
uses the texture’s native height in pixel mode, or one world unit. Labels are
centered on the node origin. Camera-facing labels inherit parent translation;
Fixed: true keeps the local XY plane and inherits parent rotation and scale.
Fixed opaque labels are single-sided, following the mesh winding convention.
Blended quads draw both sides, as other transparent meshes do. Scene bounds
include only the anchor for pixel-sized labels, so camera fitting does not
interpret pixels as world units.
Call SetLabel again to update text or appearance; the node, transform, and
children survive. Use SetTransform alone for movement, which preserves GPU
buffers and textures. For many identical labels, call Label.Geometry() once,
then NewSharedGeometry and SetShared for each instance. Hundreds of shared
labels use the existing instanced GPU draw path. Distinct label textures cost
separate bindings and draws; there is no global glyph atlas.
The default face is the small bitmap basicfont.Face7x13, suitable for short
ASCII annotations. Face accepts an existing font.Face for other sizes or
Unicode coverage; text is drawn at construction, so the face need not remain
alive afterward. Choose a raster size close to the intended display size.
Multiline text uses newline separators. Icon adds an image at native pixel
size to the left. Padding, border width, and the optional one-pixel Shadow
are measured in texture pixels. Color defaults to white.
By default the label uses alpha cutouts: glyphs and backgrounds write depth,
while transparent gaps leave geometry visible and pickable. Transparent
enables blended alpha edges and translucent backgrounds, using the existing
back-to-front transparent mesh pass; these labels test depth but do not write
it. Opacity in (0, 1) also enables blending; zero defaults to one, and Hide
sets opacity to zero. Supersampling and linear-light filtering apply normally.
Blended labels share the existing transparent mesh sorting limitations.
Labels are pickable triangles by default, with the usual node ID and name.
NoPick lets clicks pass through. Cutout picking respects texture alpha;
blended picking follows ordinary transparent meshes and tests the whole quad.
The anchor must be between the near and far planes, and quads outside the
frustum are culled. Regenerate the frame after changing camera or viewport:
Scene.Frame resolves the billboard transform for rendering and picking.
For an image without text, use NewBillboard(texture, width, height, opacity, transparent, Billboard{Pixels: true}). Its opacity is explicit (zero means
invisible). Set Billboard.Text for a terminal representation, or leave it
empty for quad edges. A hand-built frame must supply resolved instance
transforms itself. CellText, DynamicLines, Overlays, and widget frame
decorators continue to work as before.
Wireframe preserves text as terminal graphemes, centered at the projected anchor, testing occupied cells against surface depth. It simplifies orientation, size, texture, background, and opacity to readable character cells; it does not rasterize the label’s rectangular background into its depth buffer. Icons without text appear as quad edges. This is a terminal fallback rather than a pixel-exact reproduction of the textured quad.
Try the Terrain explorer gallery (task terrain): labels move with the
terrain parent, the peak text changes when flattened, and ridges occlude
annotations as the camera orbits. Keys 1, 2, and 3 compare the renderers.