Skip to main content

Engine cookbook

Each recipe assumes an existing Device. The snippets emphasize the object or update that changes; shader source and ordinary render-pass setup are omitted when they are not the point.

GoalEngine objectResult
Render geometryModelOne reusable draw contract
Update visible stateShaderInputs or model settersA redraw without reconstruction
Avoid idle workneedsRedraw()Frames only when state changes
Pick and highlightPickingManagerStable object and batch indices
Retain hierarchyGroupNode and ModelNodeTraversable scene structure
Apply an image effectShaderPassRendererA texture or presented frame

Render a model

const model = new Model(device, {source, vs, fs, geometry});
const renderPass = device.beginRenderPass({clearColor: [0, 0, 0, 1]});
model.draw(renderPass);
renderPass.end();
device.submit();

Create the model once and call model.destroy() when its owner unmounts.

Update data without rebuilding

model.shaderInputs.setProps({lighting: {intensity: 0.8}});
model.setInstanceCount(visibleInstanceCount);
animationLoop.setNeedsRedraw('lighting or visibility changed');

Use setters for dynamic state. Reconstruct only when the object’s fundamental contract changes.

Animate on demand

function onCameraChange(): void {
model.setNeedsRedraw('camera changed');
animationLoop.setNeedsRedraw('camera changed');
}

Treat redraw as invalidation, not permission to render forever. See Redraw detection.

Pick and highlight

if (pickingManager.shouldPick(mousePosition)) {
const pickingPass = pickingManager.beginRenderPass();
model.draw(pickingPass);
pickingPass.end();
await pickingManager.updatePickInfo(mousePosition);
}

The manager publishes the selected index to shader inputs; skip the pass when the cursor is unchanged.

Compose a scene

const root = new GroupNode([
new ModelNode({model: terrainModel}),
new ModelNode({model: vehicleModel})
]);
root.traverse(node => node.draw(renderPass));

Use direct model lists when transforms and parent-child traversal add no value. Destroying the root destroys its children.

Apply a pass

const renderer = new ShaderPassRenderer(device, {shaderPasses: [bloom, toneMapping]});
renderer.renderToScreen({sourceTexture: sceneColorTexture});

Each subpass is another draw and may allocate intermediate textures. Call renderer.destroy() with its owner.