Skip to content

InstancedMesh

InstancedMesh renders many copies of a shared mesh in an efficient single draw call via a Vulkan SSBO (Shader Storage Buffer Object). Up to 65,536 instances are supported. Instances can be static, updated each frame, or driven by a physics simulation.

Beyond the per-instance transform, each instance can carry its own base colour and a small block of shader params (a height/scale multiplier and a wind-phase offset), and the mesh can opt in to per-instance frustum culling so a large scatter field only pays for the instances actually in view. See Per-instance colour, params & culling.

Returns: InstancedMesh — A new instanced mesh registered with the scene.

Parameters:

  • name (String) — Identifier for later retrieval via findInstancedMesh.

Returns: InstancedMesh or null — Find an existing instanced mesh by name.

_instanced = _scene.createInstancedMesh("trees")

Set the mesh to instance from a Node (e.g. loaded from a .blend).

Parameters:

  • node (Node) — Node whose mesh is used as the prototype.

Set the mesh to instance from a Mesh object created in code.

_instanced.setMesh(_treeMesh)
// or from a blend node:
_instanced.setMeshFromNode(_treeNode)

setMaterial(r, g, b, a, metallic, roughness)

Section titled “setMaterial(r, g, b, a, metallic, roughness)”

Set a shared PBR material for all instances.

Parameters: All Num in 0–1 range.

_instanced.setMaterial(0.2, 0.8, 0.3, 1.0, 0.0, 0.7) // Green matte

Returns: Num — Index of the added instance.

Add a new instance at position (x, y, z) with identity rotation and scale 1.

Returns: Num — Index of the added instance.

Add a new instance with uniform scale.

Update the position of an existing instance (keeps existing rotation and scale).

Update an instance with a full 4×4 column-major transform matrix (a flat List of 16 Nums).

Remove instance at index. Swaps with the last instance for O(1) removal (indices may change).

Remove all instances.

Resize the instance pool. Growing adds identity transforms; shrinking removes trailing instances.

Set all instance transforms from a flat List of count * 16 floats. Used for high-performance bulk updates (e.g. from a physics simulation).

// Static grid of 1000 instances
for (i in 0...1000) {
var x = (i % 32) * 2.0 - 32
var z = (i / 32).floor * 2.0 - 16
_instanced.addInstance(x, 0, z)
}
// Dynamic update per frame (wave animation)
for (i in 0..._instanced.instanceCount) {
var x = (i % 32) * 2.0 - 32
var z = (i / 32).floor * 2.0
var y = Math.sin(_time + x * 0.3) * 0.5
_instanced.setInstancePosition(i, x, y, z)
}

Per-instance colour, params & culling (enhanced instancing)

Section titled “Per-instance colour, params & culling (enhanced instancing)”

Enhanced instancing (engine ADR 0081, PLM-248) lets each instance vary from its neighbours and lets the renderer skip off-screen instances — the difference between a field of grass that looks stamped from one blade and one that reads as a living field. It builds on the plain transform instancing above: the same SSBO now carries a widened 96-byte per-instance record — [transform (64 B), baseColor (16 B), params (16 B)] — so no extra buffer or descriptor is needed. The shader-side layout is the Plume3D_Instance struct declared once in the engine’s shader ABI include (plume3d.slang); the transform stays at offset 0, so a transform-only shader is unaffected.

addInstanceColored(x, y, z, scale, r, g, b)

Section titled “addInstanceColored(x, y, z, scale, r, g, b)”

Returns: Num — Index of the added instance.

Add an instance at (x, y, z) with uniform scale and a per-instance base colour (r, g, b) (each 0–1). Like addInstanceWithScale, but the colour is uploaded in the instance’s baseColor channel for the shader to tint with. Instances added via addInstance / addInstanceWithScale default to white.

setInstanceParams(index, scaleMul, windPhase, lodBias)

Section titled “setInstanceParams(index, scaleMul, windPhase, lodBias)”

Set the per-instance shader params for an existing instance:

  • scaleMul (Num) — a height/scale multiplier the shader applies (e.g. vary blade height per instance). Default 1.
  • windPhase (Num) — a phase offset (radians) for wind sway, so instances don’t sway in lockstep. Default 0.
  • lodBias (Num) — reserved for a future distance-LOD system; ships now so the GPU contract is stable, currently inert.

These are read by the mesh shader from the instance params channel. How scaleMul / windPhase are used is up to the shader (the grass_field demo multiplies blade height by scaleMul and offsets its wind sway by windPhase).

// Per-instance colour + height + wind phase (a grass blade)
var idx = _grass.addInstanceColored(x, 0, z, 1.0, 0.2, 0.6, 0.15)
_grass.setInstanceParams(idx, 0.9, (x + z) * 0.6, 0.0) // shorter blade, phase-offset sway

Opt in to per-instance frustum culling. When enabled, the renderer builds the camera frustum from proj * view, transforms the mesh’s local AABB by each instance’s transform, and draws only the instances whose world AABB is in view — the single instanced draw call submits just the survivors. Off by default (every instance is drawn, unchanged behaviour). Recommended for large scatter fields where much of the field is off-screen.

_grass.setCullingEnabled(true) // draw only the blades in the frustum
PropertyReturnsDescription
instanceCountNumCurrent number of instances
name / name=(value)StringName identifier
isVisible / isVisible=(value)BoolShow or hide all instances
useGpuInstancing / useGpuInstancing=(value)BoolEnable true GPU instancing via SSBO (recommended for high counts); false falls back to loop-based push-constant instancing
cullingEnabledBoolWhether per-instance frustum culling is on (set via setCullingEnabled)
visibleCountNumInstances actually drawn last frame — a diagnostic. Equals instanceCount with culling off; drops below it as culling removes off-screen instances
_instanced.useGpuInstancing = true // Single draw call via SSBO
_instanced.isVisible = false // Hide the whole group
Logger.info("Instances: %(_instanced.instanceCount)")

See Scene for createInstancedMesh and findInstancedMesh. See ParticleEmitter for physics-driven particle rendering (which also uses instanced meshes internally). See the Instanced Mesh GPU and Grass Field examples for working demos, the latter exercising per-instance colour + params + culling.