Skip to main content

Scene objects

A 3D scene is a tree of nodes under a view's root. Every node is a Container3D, the 3D counterpart of a 2D Container. So you move, turn, scale and group every kind of node the same way, as Scene graph and transforms shows.

The node types​

Light from the surroundings, such as a sky, isn't a node. It comes from the view's environment; see Environment.

Putting a scene together​

A 3D node isn't a 2D Container, so it can't go on app.stage. The View3D goes on the stage, and every 3D node goes under view3d.root, at any depth. A node that isn't under a view's root draws nothing and lights nothing.

2D content, such as a Text, can't go under the root either. Draw it onto a 3D surface with a ContainerTexture instead, as Rendering 2D into 3D shows. Or keep it on the stage and pin it over a 3D point with worldToScreen.

The view finds every mesh and light wherever it sits in the tree, so a light can have a mesh as a child, and a mesh can have a light. Here a point light has a glowing bulb as its child, so the bulb moves with it:

import { Material3D, Mesh3D, PointLight, SphereGeometry } from '@pixi/3d';

const color = 0xff2e7e;
const light = new PointLight({ color, intensity: 8, range: 6 });
// An emissive material glows in its own colour, whatever light reaches it
const bulb = new Mesh3D({
geometry: new SphereGeometry({ radius: 0.09 }),
material: new Material3D({ baseColor: 0x000000, emissive: { color, strength: 4 } }),
castShadow: false, // a glowing bulb shouldn't cast a shadow
});

light.addChild(bulb);
view3d.root.addChild(light);

app.ticker.add(() => {
const time = performance.now() / 1000;

light.position.set(Math.cos(time) * 2.6, 1.2, Math.sin(time) * 2.6); // the bulb moves with the light
});

Hiding the light with visible turns it off and hides the bulb too.

Scene graph and transforms covers the rest, including why, unlike in 2D, the order of children doesn't decide what covers what.

Choosing a node for many objects​

You haveUse
Objects you place, click and animate one by oneMesh3D
Copies of a loaded model that each animate on their ownSeveral Model3Ds built from one loaded file
Up to a few thousand cards that face the camera and mostly stay put, such as treesSprite3D
Thousands of copies of a mesh, such as trees, rocks, stars or bulletsBatchedMesh3D
Fire, smoke, sparks or snow that emit, move and fade on their ownParticleSystem3D
Thousands of cards that your own code moves every frameParticleContainer3D

For a glTF file, Assets.load returns a Scene3DSource. Models built from one source share its geometry, materials and textures, as Loading models explains. Each model has its own nodes and, when the file has animations, its own AnimationPlayer.

Each mesh and each sprite, including the meshes inside a model, is a scene node. A mesh or sprite that moves, or sits under a node that moves, costs CPU time on every frame it moves, because the view works out its place in the world again. The view also sorts the meshes and sprites that blend with what's behind them back to front every frame, whether they move or not. A sprite that makes its own material blends by default; see alphaMode.

Meshes that don't move and share their draw calls cost the CPU little each frame. Performance says what lets them share. Their number still counts, because each node takes memory and time to build. Where the view culls on the CPU, as it does by default under WebGL2, it works out each frame which meshes the camera can't see and skips them. That work grows with the number of meshes.

So put scenery, such as a forest's trees and rocks, in BatchedMesh3D slots. A few thousand meshes that never move are fine, but for tens of thousands, use slots. A batched mesh and a particle container have no node per copy, so moving a copy means writing into an array.

Unlike pixi's 2D ParticleContainer, a ParticleContainer3D has no object per particle. You write each card by its index with writePacked, then call upload once a frame, as Driving particles yourself shows. You set its capacity when you create it and can't change it later. Writing past it throws, so build a bigger container when your particles outgrow it.

Next steps​

  • Meshes: building a mesh from a geometry and a material, and sharing and swapping them.
  • Cameras and Lights: seeing and lighting a scene.
  • Performance: draw calls, culling and a checklist for phones.