Learn how to configure lighting, shadows, and environment. This example uses a point light with shadows, a receiving floor, and a cube.
You are viewing: Browser (WASM) · Switch
vulfram-canvas in your page.1. Create the camera with a 3/4 angle to see the shadow volume.
2. Add the light with castShadow: true.
3. Configure configureShadows and configureEnvironment.
4. Mark the floor with receiveShadow: true.
Copy the full example below. In browser mode, use the vulfram-canvas canvas.
import {
initEngine,
createWorld,
createWindow,
createEntity,
createCamera,
createLight,
createGeometry,
createMaterial,
createModel,
createTexture,
updateTransform,
configureEnvironment,
configureShadows,
tick,
} from '@vulfram/engine';
import { initWasmTransport, transportWasm } from '@vulfram/transport-wasm';
const WINDOW_ID = 1;
async function boot() {
await initWasmTransport();
initEngine({ transport: transportWasm });
createWorld(WINDOW_ID);
createWindow(WINDOW_ID, {
title: 'Lights & Shadows',
size: [1100, 700],
position: [0, 0],
canvasId: 'vulfram-canvas',
});
const camera = createEntity(WINDOW_ID);
updateTransform(WINDOW_ID, camera, {
position: [0, 3, 12],
rotation: [0, 0, 0, 1],
scale: [1, 1, 1],
});
createCamera(WINDOW_ID, camera, { kind: 'perspective', near: 0.1, far: 100.0 });
const light = createEntity(WINDOW_ID);
updateTransform(WINDOW_ID, light, {
position: [4, 6, 6],
rotation: [0, 0, 0, 1],
scale: [1, 1, 1],
});
createLight(WINDOW_ID, light, {
kind: 'point',
intensity: 18,
range: 40,
castShadow: true,
});
configureEnvironment(WINDOW_ID, {
msaa: { enabled: true, sampleCount: 4 },
skybox: { mode: 'procedural', intensity: 1, rotation: 0, tint: [1, 1, 1] },
});
configureShadows(WINDOW_ID, {
tileResolution: 1024,
atlasTilesW: 8,
atlasTilesH: 8,
atlasLayers: 2,
virtualGridSize: 1,
smoothing: 2,
normalBias: 0.03,
});
const floorTex = createTexture(WINDOW_ID, {
source: { type: 'color', color: [0.2, 0.2, 0.2, 1] },
srgb: true,
});
const floorMat = createMaterial(WINDOW_ID, {
kind: 'standard',
options: {
type: 'standard',
content: {
baseColor: [1, 1, 1, 1],
surfaceType: 'opaque',
baseTexId: floorTex,
baseSampler: 'linear-repeat',
flags: 0,
},
},
});
const cubeTex = createTexture(WINDOW_ID, {
source: { type: 'color', color: [0.8, 0.85, 0.9, 1] },
srgb: true,
});
const cubeMat = createMaterial(WINDOW_ID, {
kind: 'standard',
options: {
type: 'standard',
content: {
baseColor: [1, 1, 1, 1],
surfaceType: 'opaque',
baseTexId: cubeTex,
baseSampler: 'linear-clamp',
flags: 0,
},
},
});
const floorGeom = createGeometry(WINDOW_ID, {
type: 'primitive',
shape: 'plane',
options: { size: [18, 18, 1], subdivisions: 1 },
});
const cubeGeom = createGeometry(WINDOW_ID, { type: 'primitive', shape: 'cube' });
const floor = createEntity(WINDOW_ID);
updateTransform(WINDOW_ID, floor, {
position: [0, -2, 0],
rotation: [0, 0, 0, 1],
scale: [1, 1, 1],
});
createModel(WINDOW_ID, floor, { geometryId: floorGeom, materialId: floorMat, receiveShadow: true });
const cube = createEntity(WINDOW_ID);
updateTransform(WINDOW_ID, cube, {
position: [0, 0, 0],
rotation: [0, 0, 0, 1],
scale: [1.2, 1.2, 1.2],
});
createModel(WINDOW_ID, cube, { geometryId: cubeGeom, materialId: cubeMat, castShadow: true, receiveShadow: true });
let last = performance.now();
function frame(now: number) {
const delta = now - last;
last = now;
tick(now, delta);
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
boot().catch(console.error);