Babylon.js Samples
Babylon.js 3D engine examples including scenes, meshes, materials, lighting, physics, and interactive 3D web applications
Key Facts
- Category
- 3D Graphics
- Items
- 3
- Format Families
- text
Sample Overview
Babylon.js 3D engine examples including scenes, meshes, materials, lighting, physics, and interactive 3D web applications This sample set belongs to 3D Graphics and can be used to test related workflows inside Elysia Tools.
💻 Babylon.js Hello World typescript
🟢 simple
⭐
Basic Babylon.js scene setup with engine, scene, camera, lights, and simple 3D objects
⏱️ 15 min
🏷️ babylonjs, 3d, webgl, engine
Prerequisites:
TypeScript basics, WebGL concepts
// Babylon.js Hello World Examples
import * as BABYLON from '@babylonjs/core';
// 1. Basic Babylon.js Scene Setup
class BasicBabylonScene {
private engine: BABYLON.Engine;
private scene: BABYLON.Scene;
private camera: BABYLON.UniversalCamera;
constructor(canvas: HTMLCanvasElement) {
// Create engine
this.engine = new BABYLON.Engine(canvas, true);
// Create scene
this.scene = new BABYLON.Scene(this.engine);
this.scene.clearColor = new BABYLON.Color4(0.2, 0.2, 0.2, 1);
// Setup camera
this.setupCamera();
// Add lights
this.addLights();
// Create 3D objects
this.createObjects();
// Handle window resize
window.addEventListener('resize', this.handleResize.bind(this));
// Start render loop
this.startRenderLoop();
}
private setupCamera(): void {
// Create universal camera
this.camera = new BABYLON.UniversalCamera(
'camera',
new BABYLON.Vector3(0, 5, -10),
this.scene
);
this.camera.setTarget(BABYLON.Vector3.Zero());
this.camera.attachControl(this.engine.getRenderingCanvas(), true);
// Set camera controls
this.camera.keysUp = [87, 38]; // W, Up arrow
this.camera.keysDown = [83, 40]; // S, Down arrow
this.camera.keysLeft = [65, 37]; // A, Left arrow
this.camera.keysRight = [68, 39]; // D, Right arrow
}
private addLights(): void {
// Ambient light for overall illumination
const ambientLight = new BABYLON.HemisphericLight(
'ambientLight',
new BABYLON.Vector3(0, 1, 0),
this.scene
);
ambientLight.intensity = 0.7;
// Directional light for shadows
const directionalLight = new BABYLON.DirectionalLight(
'directionalLight',
new BABYLON.Vector3(1, -1, 1),
this.scene
);
directionalLight.intensity = 0.8;
directionalLight.position = new BABYLON.Vector3(10, 10, 10);
}
private createObjects(): void {
// Create ground
const ground = BABYLON.MeshBuilder.CreateGround(
'ground',
{ width: 20, height: 20 },
this.scene
);
const groundMaterial = new BABYLON.StandardMaterial('groundMat', this.scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.5, 0.5);
ground.material = groundMaterial;
// Create rotating cube
const cube = BABYLON.MeshBuilder.CreateBox('cube', { size: 2 }, this.scene);
cube.position.y = 1;
const cubeMaterial = new BABYLON.StandardMaterial('cubeMat', this.scene);
cubeMaterial.diffuseColor = new BABYLON.Color3(1, 0, 0); // Red
cube.material = cubeMaterial;
// Create sphere
const sphere = BABYLON.MeshBuilder.CreateSphere(
'sphere',
{ diameter: 2, segments: 32 },
this.scene
);
sphere.position.set(-3, 1, 0);
const sphereMaterial = new BABYLON.StandardMaterial('sphereMat', this.scene);
sphereMaterial.diffuseColor = new BABYLON.Color3(0, 1, 0); // Green
sphere.material = sphereMaterial;
// Create cylinder
const cylinder = BABYLON.MeshBuilder.CreateCylinder(
'cylinder',
{ height: 3, diameter: 1.5 },
this.scene
);
cylinder.position.set(3, 1.5, 0);
const cylinderMaterial = new BABYLON.StandardMaterial('cylinderMat', this.scene);
cylinderMaterial.diffuseColor = new BABYLON.Color3(0, 0, 1); // Blue
cylinder.material = cylinderMaterial;
// Create torus
const torus = BABYLON.MeshBuilder.CreateTorus(
'torus',
{ diameter: 2, thickness: 0.5, tessellation: 32 },
this.scene
);
torus.position.set(0, 1, 3);
const torusMaterial = new BABYLON.StandardMaterial('torusMat', this.scene);
torusMaterial.diffuseColor = new BABYLON.Color3(1, 1, 0); // Yellow
torus.material = torusMaterial;
// Add rotation animations
this.scene.registerBeforeRender(() => {
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
sphere.rotation.y += 0.02;
cylinder.rotation.y += 0.015;
torus.rotation.x += 0.01;
torus.rotation.z += 0.01;
});
}
private handleResize(): void {
this.engine.resize();
}
private startRenderLoop(): void {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
}
// 2. Interactive Scene with GUI
class InteractiveScene {
private engine: BABYLON.Engine;
private scene: BABYLON.Scene;
private camera: BABYLON.ArcRotateCamera;
private meshes: BABYLON.Mesh[] = [];
constructor(canvas: HTMLCanvasElement) {
this.engine = new BABYLON.Engine(canvas, true);
this.scene = new BABYLON.Scene(this.engine);
this.scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.2, 1);
this.setupCamera();
this.addLights();
this.createInteractiveObjects();
this.setupGUI();
this.setupInteraction();
this.startRenderLoop();
}
private setupCamera(): void {
this.camera = new BABYLON.ArcRotateCamera(
'camera',
0,
Math.PI / 3,
15,
BABYLON.Vector3.Zero(),
this.scene
);
this.camera.attachControl(this.engine.getRenderingCanvas(), true);
this.camera.lowerRadiusLimit = 5;
this.camera.upperRadiusLimit = 25;
}
private addLights(): void {
// Multiple light sources for better illumination
const light1 = new BABYLON.HemisphericLight(
'light1',
new BABYLON.Vector3(0, 1, 0),
this.scene
);
light1.intensity = 0.6;
const light2 = new BABYLON.PointLight(
'light2',
new BABYLON.Vector3(5, 5, 5),
this.scene
);
light2.intensity = 0.8;
const light3 = new BABYLON.PointLight(
'light3',
new BABYLON.Vector3(-5, 5, -5),
this.scene
);
light3.intensity = 0.8;
light3.diffuse = new BABYLON.Color3(0.5, 0.5, 1); // Blue-ish light
}
private createInteractiveObjects(): void {
// Create various meshes for interaction
const meshData = [
{ type: 'box', name: 'Box', position: new BABYLON.Vector3(0, 1, 0), color: new BABYLON.Color3(1, 0, 0) },
{ type: 'sphere', name: 'Sphere', position: new BABYLON.Vector3(-4, 1, 0), color: new BABYLON.Color3(0, 1, 0) },
{ type: 'cylinder', name: 'Cylinder', position: new BABYLON.Vector3(4, 1, 0), color: new BABYLON.Color3(0, 0, 1) },
{ type: 'torus', name: 'Torus', position: new BABYLON.Vector3(0, 1, 4), color: new BABYLON.Color3(1, 1, 0) }
];
meshData.forEach(data => {
let mesh: BABYLON.Mesh;
switch (data.type) {
case 'box':
mesh = BABYLON.MeshBuilder.CreateBox(data.name, { size: 2 }, this.scene);
break;
case 'sphere':
mesh = BABYLON.MeshBuilder.CreateSphere(data.name, { diameter: 2 }, this.scene);
break;
case 'cylinder':
mesh = BABYLON.MeshBuilder.CreateCylinder(data.name, { height: 2, diameter: 1.5 }, this.scene);
break;
case 'torus':
mesh = BABYLON.MeshBuilder.CreateTorus(data.name, { diameter: 2, thickness: 0.5 }, this.scene);
break;
}
mesh.position = data.position;
const material = new BABYLON.StandardMaterial(`${data.name}Mat`, this.scene);
material.diffuseColor = data.color;
mesh.material = material;
// Store original properties for reset
mesh.metadata = {
originalColor: data.color,
originalPosition: data.position.clone(),
originalScaling: mesh.scaling.clone()
};
this.meshes.push(mesh);
});
// Create ground
const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 20, height: 20 }, this.scene);
const groundMaterial = new BABYLON.StandardMaterial('groundMat', this.scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.3, 0.3, 0.3);
groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
ground.material = groundMaterial;
}
private setupGUI(): void {
// Add advanced texture for 3D text
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
// Title text
const title = new BABYLON.GUI.TextBlock();
title.text = 'Babylon.js Interactive Scene';
title.color = 'white';
title.fontSize = 24;
title.fontWeight = 'bold';
title.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
title.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
title.top = '20px';
advancedTexture.addControl(title);
// Instructions
const instructions = new BABYLON.GUI.TextBlock();
instructions.text = 'Click on objects to interact with them';
instructions.color = 'white';
instructions.fontSize = 16;
instructions.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
instructions.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
instructions.top = '60px';
advancedTexture.addControl(instructions);
// Object info panel
const infoPanel = new BABYLON.GUI.StackPanel();
infoPanel.width = '200px';
infoPanel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
infoPanel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
infoPanel.top = '20px';
advancedTexture.addControl(infoPanel);
const infoText = new BABYLON.GUI.TextBlock();
infoText.text = 'Click an object';
infoText.color = 'white';
infoText.fontSize = 14;
infoPanel.addControl(infoText);
// Store reference to update info text
this.scene.metadata = { infoText };
}
private setupInteraction(): void {
this.scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERDOWN:
this.handlePointerDown(pointerInfo);
break;
}
});
}
private handlePointerDown(pointerInfo: BABYLON.PointerInfo): void {
if (pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh) {
const mesh = pointerInfo.pickInfo.pickedMesh;
this.interactWithMesh(mesh);
}
}
private interactWithMesh(mesh: BABYLON.Mesh): void {
// Update info text
const infoText = this.scene.metadata.infoText;
if (infoText) {
infoText.text = `Selected: ${mesh.name}`;
}
// Create interaction effect
this.createInteractionEffect(mesh);
// Animate the mesh
this.animateMesh(mesh);
}
private createInteractionEffect(mesh: BABYLON.Mesh): void {
// Create particle effect at mesh position
const particleSystem = new BABYLON.ParticleSystem('particles', 1000, this.scene);
particleSystem.particleTexture = new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChAI9jU77yQAAAABJRU5ErkJggg==', this.scene);
particleSystem.emitter = mesh.position;
particleSystem.minEmitBox = new BABYLON.Vector3(-0.5, -0.5, -0.5);
particleSystem.maxEmitBox = new BABYLON.Vector3(0.5, 0.5, 0.5);
particleSystem.color1 = new BABYLON.Color4(1, 1, 1, 1);
particleSystem.color2 = new BABYLON.Color4(1, 1, 0, 1);
particleSystem.colorDead = new BABYLON.Color4(0, 0, 0, 0);
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.5;
particleSystem.minLifeTime = 0.5;
particleSystem.maxLifeTime = 2;
particleSystem.emitRate = 500;
particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
particleSystem.direction1 = new BABYLON.Vector3(-1, 1, -1);
particleSystem.direction2 = new BABYLON.Vector3(1, 1, 1);
particleSystem.minAngularSpeed = 0;
particleSystem.maxAngularSpeed = Math.PI;
particleSystem.minEmitPower = 0.5;
particleSystem.maxEmitPower = 2;
particleSystem.updateSpeed = 0.01;
particleSystem.start();
// Stop particles after 1 second
setTimeout(() => {
particleSystem.stop();
}, 1000);
}
private animateMesh(mesh: BABYLON.Mesh): void {
// Bounce animation
BABYLON.Animation.CreateAndStartAnimation(
'bounce',
mesh,
'position.y',
30,
20,
mesh.position.y,
mesh.position.y + 3,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
// Rotation animation
BABYLON.Animation.CreateAndStartAnimation(
'rotate',
mesh,
'rotation.y',
30,
30,
mesh.rotation.y,
mesh.rotation.y + Math.PI * 2,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
// Scale animation
BABYLON.Animation.CreateAndStartAnimation(
'scale',
mesh,
'scaling',
30,
15,
mesh.scaling.clone(),
mesh.metadata.originalScaling.scale(1.5),
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
}
private startRenderLoop(): void {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
}
// 3. Physics-Based Scene
class PhysicsScene {
private engine: BABYLON.Engine;
private scene: BABYLON.Scene;
private camera: BABYLON.UniversalCamera;
private boxes: BABYLON.Mesh[] = [];
constructor(canvas: HTMLCanvasElement) {
this.engine = new BABYLON.Engine(canvas, true);
this.scene = new BABYLON.Scene(this.scene);
this.scene.clearColor = new BABYLON.Color4(0.5, 0.8, 0.9, 1); // Sky blue
this.setupCamera();
this.addLights();
this.enablePhysics();
this.createPhysicsObjects();
this.setupControls();
this.startRenderLoop();
}
private setupCamera(): void {
this.camera = new BABYLON.UniversalCamera(
'camera',
new BABYLON.Vector3(0, 5, -15),
this.scene
);
this.camera.setTarget(BABYLON.Vector3.Zero());
this.camera.attachControl(this.engine.getRenderingCanvas(), true);
}
private addLights(): void {
const light = new BABYLON.HemisphericLight(
'light',
new BABYLON.Vector3(0, 1, 0),
this.scene
);
light.intensity = 1;
const dirLight = new BABYLON.DirectionalLight(
'dirLight',
new BABYLON.Vector3(-1, -2, -1),
this.scene
);
dirLight.position = new BABYLON.Vector3(20, 40, 20);
dirLight.intensity = 0.5;
}
private enablePhysics(): void {
// Enable physics with gravity
this.scene.enablePhysics(
new BABYLON.Vector3(0, -9.81, 0),
new BABYLON.CannonJSPlugin()
);
}
private createPhysicsObjects(): void {
// Create ground
const ground = BABYLON.MeshBuilder.CreateGround(
'ground',
{ width: 20, height: 20 },
this.scene
);
const groundMaterial = new BABYLON.StandardMaterial('groundMat', this.scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.3, 0.7, 0.3);
ground.material = groundMaterial;
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
ground,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0, restitution: 0.7 },
this.scene
);
// Create walls
this.createWalls();
// Create initial boxes
for (let i = 0; i < 5; i++) {
this.createBox(
new BABYLON.Vector3(
(Math.random() - 0.5) * 4,
2 + i * 1.1,
(Math.random() - 0.5) * 4
)
);
}
// Create bouncing spheres
for (let i = 0; i < 3; i++) {
this.createSphere(
new BABYLON.Vector3(
(Math.random() - 0.5) * 6,
5 + i * 2,
(Math.random() - 0.5) * 6
)
);
}
}
private createWalls(): void {
const wallMaterial = new BABYLON.StandardMaterial('wallMat', this.scene);
wallMaterial.diffuseColor = new BABYLON.Color3(0.7, 0.7, 0.7);
wallMaterial.alpha = 0.3;
// Back wall
const backWall = BABYLON.MeshBuilder.CreateBox(
'backWall',
{ width: 20, height: 10, depth: 0.5 },
this.scene
);
backWall.position.z = -10;
backWall.material = wallMaterial;
backWall.physicsImpostor = new BABYLON.PhysicsImpostor(
backWall,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0, restitution: 0.5 },
this.scene
);
// Side walls
const leftWall = BABYLON.MeshBuilder.CreateBox(
'leftWall',
{ width: 0.5, height: 10, depth: 20 },
this.scene
);
leftWall.position.x = -10;
leftWall.material = wallMaterial;
leftWall.physicsImpostor = new BABYLON.PhysicsImpostor(
leftWall,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0, restitution: 0.5 },
this.scene
);
const rightWall = leftWall.clone('rightWall');
rightWall.position.x = 10;
this.scene.addMesh(rightWall);
}
private createBox(position: BABYLON.Vector3): void {
const box = BABYLON.MeshBuilder.CreateBox(
`box${this.boxes.length}`,
{ size: 1 },
this.scene
);
box.position = position;
const boxMaterial = new BABYLON.StandardMaterial(`boxMat${this.boxes.length}`, this.scene);
const hue = Math.random();
boxMaterial.diffuseColor = new BABYLON.Color3.FromHSV(hue * 360, 1, 1);
box.material = boxMaterial;
// Add physics impostor
box.physicsImpostor = new BABYLON.PhysicsImpostor(
box,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 1, restitution: 0.8, friction: 0.5 },
this.scene
);
this.boxes.push(box);
}
private createSphere(position: BABYLON.Vector3): void {
const sphere = BABYLON.MeshBuilder.CreateSphere(
`sphere${Date.now()}`,
{ diameter: 0.8, segments: 16 },
this.scene
);
sphere.position = position;
const sphereMaterial = new BABYLON.StandardMaterial(`sphereMat${Date.now()}`, this.scene);
sphereMaterial.diffuseColor = new BABYLON.Color3.FromHSV(Math.random() * 360, 1, 1);
sphere.material = sphereMaterial;
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(
sphere,
BABYLON.PhysicsImpostor.SphereImpostor,
{ mass: 0.5, restitution: 0.9, friction: 0.3 },
this.scene
);
}
private setupControls(): void {
this.scene.onPointerDown = (evt, pickResult) => {
if (pickResult.hit) {
// Apply impulse to clicked object
const direction = this.camera.position.subtract(pickResult.pickedPoint!).normalize();
const force = direction.scale(10);
if (pickResult.pickedMesh!.physicsImpostor) {
pickResult.pickedMesh!.physicsImpostor!.applyImpulse(
force,
pickResult.pickedPoint!
);
}
}
};
// Add keyboard controls
window.addEventListener('keydown', (event) => {
switch (event.key) {
case ' ':
// Space: Add new box at camera position
this.createBox(
this.camera.position.add(
this.camera.getDirection(new BABYLON.Vector3(0, 0, 1)).scale(2)
)
);
break;
case 's':
// S: Add new sphere
this.createSphere(
this.camera.position.add(
this.camera.getDirection(new BABYLON.Vector3(0, 0, 1)).scale(2)
)
);
break;
case 'r':
// R: Reset scene
this.resetScene();
break;
}
});
}
private resetScene(): void {
// Remove all physics objects except ground and walls
this.scene.meshes.forEach(mesh => {
if (mesh.name.startsWith('box') || mesh.name.startsWith('sphere')) {
mesh.dispose();
}
});
this.boxes = [];
// Create new initial objects
for (let i = 0; i < 5; i++) {
this.createBox(
new BABYLON.Vector3(
(Math.random() - 0.5) * 4,
2 + i * 1.1,
(Math.random() - 0.5) * 4
)
);
}
}
private startRenderLoop(): void {
// Add instructions text
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
const text = new BABYLON.GUI.TextBlock();
text.text = 'Click objects to apply force | Space: Add box | S: Add sphere | R: Reset';
text.color = 'white';
text.fontSize = 14;
text.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
text.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
text.top = '20px';
advancedTexture.addControl(text);
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
}
// Usage examples:
// const basicScene = new BasicBabylonScene(document.getElementById('canvas1') as HTMLCanvasElement);
// const interactiveScene = new InteractiveScene(document.getElementById('canvas2') as HTMLCanvasElement);
// const physicsScene = new PhysicsScene(document.getElementById('canvas3') as HTMLCanvasElement);
export {
BasicBabylonScene,
InteractiveScene,
PhysicsScene
};
💻 Materials and Shaders typescript
🟡 intermediate
⭐⭐⭐
Advanced Babylon.js materials, PBR shaders, textures, custom materials, and realistic surface rendering
⏱️ 30 min
🏷️ babylonjs, materials, pbr, shaders
Prerequisites:
Babylon.js basics, 3D graphics concepts, Material properties
// Babylon.js Materials and Shaders Examples
import * as BABYLON from '@babylonjs/core';
import { GridMaterial } from '@babylonjs/materials/grid';
import '@babylonjs/loaders';
// 1. Comprehensive Material Showcase
class MaterialShowcase {
private engine: BABYLON.Engine;
private scene: BABYLON.Scene;
private camera: BABYLON.ArcRotateCamera;
constructor(canvas: HTMLCanvasElement) {
this.engine = new BABYLON.Engine(canvas, true);
this.scene = new BABYLON.Scene(this.engine);
this.scene.clearColor = new BABYLON.Color4(0.05, 0.05, 0.1, 1);
this.setupCamera();
this.setupLighting();
this.createMaterialSamples();
this.setupEnvironment();
this.startRenderLoop();
}
private setupCamera(): void {
this.camera = new BABYLON.ArcRotateCamera(
'camera',
0,
Math.PI / 3,
20,
BABYLON.Vector3.Zero(),
this.scene
);
this.camera.attachControl(this.engine.getRenderingCanvas(), true);
this.camera.lowerRadiusLimit = 8;
this.camera.upperRadiusLimit = 30;
}
private setupLighting(): void {
// Multiple light setup for PBR materials
const hemisphericLight = new BABYLON.HemisphericLight(
'hemiLight',
new BABYLON.Vector3(0, 1, 0),
this.scene
);
hemisphericLight.intensity = 0.3;
hemisphericLight.groundColor = new BABYLON.Color3(0.2, 0.2, 0.3);
const mainLight = new BABYLON.DirectionalLight(
'mainLight',
new BABYLON.Vector3(-1, -2, 1),
this.scene
);
mainLight.intensity = 3;
mainLight.position = new BABYLON.Vector3(20, 40, 20);
// Colored fill lights
const warmLight = new BABYLON.PointLight(
'warmLight',
new BABYLON.Vector3(10, 5, -5),
this.scene
);
warmLight.intensity = 1;
warmLight.diffuse = new BABYLON.Color3(1, 0.8, 0.4);
const coolLight = new BABYLON.PointLight(
'coolLight',
new BABYLON.Vector3(-10, 5, 5),
this.scene
);
coolLight.intensity = 1;
coolLight.diffuse = new BABYLON.Color3(0.4, 0.6, 1);
}
private createMaterialSamples(): void {
const materials = [
this.createStandardMaterial(),
this.createPBRMaterial(),
this.createGridMaterial(),
this.createCustomShaderMaterial(),
this.createAnimatedMaterial(),
this.createToonMaterial(),
this.createNormalMaterial(),
this.createWireframeMaterial()
];
materials.forEach((material, index) => {
const mesh = BABYLON.MeshBuilder.CreateSphere(
`sphere${index}`,
{ diameter: 2, segments: 32 },
this.scene
);
const angle = (index / materials.length) * Math.PI * 2;
mesh.position.set(
Math.cos(angle) * 6,
0,
Math.sin(angle) * 6
);
mesh.material = material;
// Add subtle rotation
this.scene.registerBeforeRender(() => {
mesh.rotation.y += 0.005 * (index % 2 === 0 ? 1 : -1);
});
});
}
private createStandardMaterial(): BABYLON.StandardMaterial {
const material = new BABYLON.StandardMaterial('standardMat', this.scene);
material.diffuseColor = new BABYLON.Color3(0.8, 0.2, 0.2);
material.specularColor = new BABYLON.Color3(1, 1, 1);
material.specularPower = 128;
material.emissiveColor = new BABYLON.Color3(0.1, 0, 0);
material.backFaceCulling = false;
// Add texture
material.diffuseTexture = this.createProceduralTexture(256, (ctx, w, h) => {
const gradient = ctx.createLinearGradient(0, 0, w, h);
gradient.addColorStop(0, '#ff6666');
gradient.addColorStop(0.5, '#cc0000');
gradient.addColorStop(1, '#990000');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
// Add pattern
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2;
for (let i = 0; i < w; i += 16) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, h);
ctx.stroke();
}
});
return material;
}
private createPBRMaterial(): BABYLON.PBRMaterial {
const material = new BABYLON.PBRMaterial('pbrMat', this.scene);
material.baseColor = new BABYLON.Color3(0.2, 0.8, 0.2);
material.metallic = 0.7;
material.roughness = 0.3;
material.alpha = 1;
material.subSurface.isRefractionEnabled = true;
material.subSurface.refractionIntensity = 0.5;
// Add PBR textures
material.albedoTexture = this.createProceduralTexture(512, (ctx, w, h) => {
// Metallic green texture
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const x = (i / 4) % w;
const y = Math.floor((i / 4) / w);
const noise = Math.sin(x * 0.1) * Math.cos(y * 0.1);
const value = 100 + noise * 50;
imageData.data[i] = value * 0.2; // R
imageData.data[i + 1] = value; // G
imageData.data[i + 2] = value * 0.2; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
material.metallicTexture = this.createProceduralTexture(256, (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const value = 200 + Math.random() * 55;
imageData.data[i] = value; // R
imageData.data[i + 1] = value; // G
imageData.data[i + 2] = value; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
return material;
}
private createGridMaterial(): GridMaterial {
const material = new GridMaterial('gridMat', this.scene);
material.majorUnitFrequency = 5;
material.minorUnitVisibility = 0.5;
material.gridRatio = 1;
material.backFaceCulling = false;
material.mainColor = new BABYLON.Color3(0, 0.5, 1);
material.lineColor = new BABYLON.Color3(1, 1, 1);
material.opacity = 0.95;
return material;
}
private createCustomShaderMaterial(): BABYLON.ShaderMaterial {
// Create custom shader
BABYLON.Effect.ShadersStore['customVertexShader'] = `
precision highp float;
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
uniform mat4 world;
uniform mat4 worldViewProjection;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec2 vUV;
varying float vTime;
uniform float time;
void main(void) {
vec3 newPosition = position;
newPosition.y += sin(position.x * 5.0 + time) * 0.1;
newPosition.y += cos(position.z * 5.0 + time) * 0.1;
vec4 worldPosition = world * vec4(newPosition, 1.0);
vPosition = worldPosition.xyz;
vNormal = normalize(world * vec4(normal, 0.0)).xyz;
vUV = uv;
vTime = time;
gl_Position = worldViewProjection * worldPosition;
}
`;
BABYLON.Effect.ShadersStore['customFragmentShader'] = `
precision highp float;
varying vec3 vNormal;
varying vec3 vPosition;
varying vec2 vUV;
varying float vTime;
uniform vec3 lightPosition;
uniform vec3 cameraPosition;
void main(void) {
vec3 lightDirection = normalize(lightPosition - vPosition);
vec3 viewDirection = normalize(cameraPosition - vPosition);
vec3 normal = normalize(vNormal);
float diff = max(dot(normal, lightDirection), 0.0);
vec3 diffuse = diff * vec3(1.0, 1.0, 1.0);
// Animated color based on time and UV
vec3 color = vec3(
sin(vTime + vUV.x * 10.0) * 0.5 + 0.5,
cos(vTime + vUV.y * 10.0) * 0.5 + 0.5,
sin(vTime * 2.0) * 0.5 + 0.5
);
vec3 finalColor = color * (0.3 + diffuse * 0.7);
gl_FragColor = vec4(finalColor, 1.0);
}
`;
const shaderMaterial = new BABYLON.ShaderMaterial(
'customShader',
this.scene,
{
vertex: 'custom',
fragment: 'custom',
},
{
attributes: ['position', 'normal', 'uv'],
uniforms: ['world', 'worldViewProjection', 'time', 'lightPosition', 'cameraPosition']
}
);
shaderMaterial.setFloat('time', 0);
shaderMaterial.setVector3('lightPosition', new BABYLON.Vector3(10, 10, 10));
shaderMaterial.setVector3('cameraPosition', this.camera.position);
// Animate the shader
this.scene.registerBeforeRender(() => {
shaderMaterial.setFloat('time', Date.now() * 0.001);
shaderMaterial.setVector3('cameraPosition', this.camera.position);
});
return shaderMaterial;
}
private createAnimatedMaterial(): BABYLON.StandardMaterial {
const material = new BABYLON.StandardMaterial('animatedMat', this.scene);
// Create animated texture
const dynamicTexture = new BABYLON.DynamicTexture('animated', 512, this.scene);
const context = dynamicTexture.getContext();
let frame = 0;
this.scene.registerBeforeRender(() => {
context.fillStyle = '#000080';
context.fillRect(0, 0, 512, 512);
// Draw animated circles
for (let i = 0; i < 5; i++) {
const x = 256 + Math.cos((frame + i * 60) * 0.05) * 150;
const y = 256 + Math.sin((frame + i * 60) * 0.05) * 150;
const radius = 20 + Math.sin(frame * 0.1 + i) * 10;
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2);
context.fillStyle = `hsl(${(frame + i * 60) % 360}, 100%, 50%)`;
context.fill();
}
dynamicTexture.update();
frame++;
});
material.diffuseTexture = dynamicTexture;
material.emissiveTexture = dynamicTexture;
material.emissiveColor = new BABYLON.Color3(0.5, 0.5, 0.5);
return material;
}
private createToonMaterial(): BABYLON.StandardMaterial {
const material = new BABYLON.StandardMaterial('toonMat', this.scene);
// Create toon shading effect with texture
material.diffuseTexture = this.createProceduralTexture(256, (ctx, w, h) => {
// Create stepped gradient for toon effect
const steps = 4;
const stepHeight = h / steps;
for (let i = 0; i < steps; i++) {
const brightness = 255 - (i * 60);
ctx.fillStyle = `rgb(${brightness}, ${brightness * 0.7}, ${brightness * 0.3})`;
ctx.fillRect(0, i * stepHeight, w, stepHeight);
}
// Add cell border effect
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
for (let i = 1; i < steps; i++) {
ctx.beginPath();
ctx.moveTo(0, i * stepHeight);
ctx.lineTo(w, i * stepHeight);
ctx.stroke();
}
});
material.specularColor = new BABYLON.Color3(0, 0, 0); // No specular highlights
material.roughness = 1; // Rough surface
return material;
}
private createNormalMaterial(): BABYLON.StandardMaterial {
const material = new BABYLON.StandardMaterial('normalMat', this.scene);
// Create normal map texture
material.bumpTexture = this.createProceduralTexture(256, (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const x = (i / 4) % w;
const y = Math.floor((i / 4) / w);
// Create flowing normal pattern
const nx = Math.sin(x * 0.05) * 127 + 128;
const ny = Math.cos(y * 0.05) * 127 + 128;
const nz = 255;
imageData.data[i] = nx; // R (X normal)
imageData.data[i + 1] = ny; // G (Y normal)
imageData.data[i + 2] = nz; // B (Z normal)
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
material.diffuseColor = new BABYLON.Color3(0.7, 0.7, 0.7);
material.specularColor = new BABYLON.Color3(1, 1, 1);
material.specularPower = 64;
return material;
}
private createWireframeMaterial(): BABYLON.StandardMaterial {
const material = new BABYLON.StandardMaterial('wireframeMat', this.scene);
material.wireframe = true;
material.emissiveColor = new BABYLON.Color3(0, 1, 1);
material.diffuseColor = new BABYLON.Color3(0, 0, 0);
material.specularColor = new BABYLON.Color3(0, 0, 0);
return material;
}
private createProceduralTexture(size: number, generator: (ctx: CanvasRenderingContext2D, w: number, h: number) => void): BABYLON.Texture {
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const context = canvas.getContext('2d')!;
generator(context, size, size);
return new BABYLON.Texture(null, this.scene, false, false, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, null, canvas);
}
private setupEnvironment(): void {
// Create ground with reflective material
const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 30, height: 30 }, this.scene);
ground.position.y = -3;
const groundMaterial = new BABYLON.StandardMaterial('groundMat', this.scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.1, 0.1, 0.1);
groundMaterial.specularColor = new BABYLON.Color3(1, 1, 1);
groundMaterial.specularPower = 128;
groundMaterial.alpha = 0.9;
ground.material = groundMaterial;
// Create skybox
this.createSkybox();
// Add particle system for atmosphere
this.createAtmosphere();
}
private createSkybox(): void {
const skybox = BABYLON.MeshBuilder.CreateBox('skyBox', { size: 100 }, this.scene);
const skyboxMaterial = new BABYLON.StandardMaterial('skyBox', this.scene);
skyboxMaterial.backFaceCulling = false;
skyboxMaterial.disableLighting = true;
// Create gradient texture for skybox
const skyTexture = this.createProceduralTexture(512, (ctx, w, h) => {
const gradient = ctx.createLinearGradient(0, 0, 0, h);
gradient.addColorStop(0, '#001133');
gradient.addColorStop(0.5, '#004488');
gradient.addColorStop(1, '#0088ff');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
// Add stars
ctx.fillStyle = '#ffffff';
for (let i = 0; i < 100; i++) {
const x = Math.random() * w;
const y = Math.random() * h * 0.7;
const size = Math.random() * 2;
ctx.fillRect(x, y, size, size);
}
});
skyboxMaterial.diffuseTexture = skyTexture;
skyboxMaterial.emissiveTexture = skyTexture;
skybox.material = skyboxMaterial;
}
private createAtmosphere(): void {
const particleSystem = new BABYLON.ParticleSystem('atmosphere', 2000, this.scene);
particleSystem.particleTexture = new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', this.scene);
particleSystem.emitter = BABYLON.Vector3.Zero();
particleSystem.minEmitBox = new BABYLON.Vector3(-25, -25, -25);
particleSystem.maxEmitBox = new BABYLON.Vector3(25, 25, 25);
particleSystem.color1 = new BABYLON.Color4(1, 1, 1, 0.5);
particleSystem.color2 = new BABYLON.Color4(0.5, 0.5, 1, 0.3);
particleSystem.colorDead = new BABYLON.Color4(0, 0, 0, 0);
particleSystem.minSize = 0.05;
particleSystem.maxSize = 0.15;
particleSystem.minLifeTime = 10;
particleSystem.maxLifeTime = 30;
particleSystem.emitRate = 50;
particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
particleSystem.gravity = new BABYLON.Vector3(0, 0, 0);
particleSystem.direction1 = new BABYLON.Vector3(-1, -1, -1);
particleSystem.direction2 = new BABYLON.Vector3(1, 1, 1);
particleSystem.minEmitPower = 0.01;
particleSystem.maxEmitPower = 0.05;
particleSystem.start();
}
private startRenderLoop(): void {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
}
// 2. Advanced PBR Materials
class PBRMaterialDemo {
private engine: BABYLON.Engine;
private scene: BABYLON.Scene;
private camera: BABYLON.ArcRotateCamera;
constructor(canvas: HTMLCanvasElement) {
this.engine = new BABYLON.Engine(canvas, true);
this.scene = new BABYLON.Scene(this.engine);
this.scene.clearColor = new BABYLON.Color4(0.02, 0.02, 0.05, 1);
this.setupCamera();
this.setupHDREnvironment();
this.createPBRMaterials();
this.createMaterialControls();
this.startRenderLoop();
}
private setupCamera(): void {
this.camera = new BABYLON.ArcRotateCamera(
'camera',
Math.PI / 2,
Math.PI / 3,
15,
BABYLON.Vector3.Zero(),
this.scene
);
this.camera.attachControl(this.engine.getRenderingCanvas(), true);
this.camera.wheelPrecision = 50;
}
private setupHDREnvironment(): void {
// Create HDR environment for realistic lighting
const envTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
'https://playground.babylonjs.com/textures/environment.dds',
this.scene
);
envTexture.gammaSpace = false;
envTexture.rotationY = Math.PI;
// Set as environment texture
this.scene.environmentTexture = envTexture;
this.scene.createDefaultSkybox(envTexture, true, 1000);
// Create IBL (Image Based Lighting)
this.scene.environmentIntensity = 1.0;
}
private createPBRMaterials(): void {
const materials = [
this.createMetalMaterial(),
this.createGlassMaterial(),
this.createFabricMaterial(),
this.createWoodMaterial(),
this.createMarbleMaterial(),
this.createHolographicMaterial()
];
// Create display spheres
materials.forEach((material, index) => {
const mesh = BABYLON.MeshBuilder.CreateSphere(
`pbrSphere${index}`,
{ diameter: 2, segments: 64 },
this.scene
);
const angle = (index / materials.length) * Math.PI * 2;
mesh.position.set(
Math.cos(angle) * 5,
0,
Math.sin(angle) * 5
);
mesh.material = material;
// Add animation
this.scene.registerBeforeRender(() => {
mesh.rotation.y += 0.01;
mesh.position.y = Math.sin(Date.now() * 0.001 + index) * 0.5;
});
});
// Create ground with PBR material
this.createPBRGround();
}
private createMetalMaterial(): BABYLON.PBRMetallicRoughnessMaterial {
const material = new BABYLON.PBRMetallicRoughnessMaterial('metal', this.scene);
material.baseColor = new BABYLON.Color3(0.7, 0.7, 0.8);
material.metallic = 1.0;
material.roughness = 0.1;
material.environmentIntensity = 1.0;
// Add normal map for surface detail
material.normalTexture = this.createNormalMap();
material.useParallax = true;
material.useParallaxOcclusion = true;
return material;
}
private createGlassMaterial(): BABYLON.PBRMaterial {
const material = new BABYLON.PBRMaterial('glass', this.scene);
material.alpha = 0.2;
material.subSurface.isRefractionEnabled = true;
material.subSurface.refractionIntensity = 0.8;
material.subSurface.tintColor = new BABYLON.Color3(0.8, 0.9, 1.0);
material.indexOfRefraction = 1.5;
material.metallic = 0;
material.roughness = 0;
material.clearCoat.isEnabled = true;
material.clearCoat.intensity = 1.0;
material.clearCoat.roughness = 0;
return material;
}
private createFabricMaterial(): BABYLON.PBRMaterial {
const material = new BABYLON.PBRMaterial('fabric', this.scene);
material.albedoColor = new BABYLON.Color3(0.3, 0.2, 0.1);
material.metallic = 0;
material.roughness = 0.9;
material.useSubSurfaceScattering = true;
material.subSurface.tintColor = new BABYLON.Color3(0.8, 0.4, 0.1);
material.subSurface.translucencyIntensity = 0.8;
// Add fabric texture
material.albedoTexture = this.createFabricTexture();
material.bumpTexture = this.createFabricBumpTexture();
return material;
}
private createWoodMaterial(): BABYLON.PBRMetallicRoughnessMaterial {
const material = new BABYLON.PBRMetallicRoughnessMaterial('wood', this.scene);
material.baseColor = new BABYLON.Color3(0.4, 0.2, 0.1);
material.metallic = 0;
material.roughness = 0.8;
// Create wood grain texture
material.baseTexture = this.createWoodGrainTexture();
material.normalTexture = this.createWoodNormalTexture();
return material;
}
private createMarbleMaterial(): BABYLON.PBRMaterial {
const material = new BABYLON.PBRMaterial('marble', this.scene);
material.albedoColor = new BABYLON.Color3(0.95, 0.95, 0.9);
material.metallic = 0;
material.roughness = 0.1;
material.subSurface.isTranslucencyEnabled = true;
material.subSurface.translucencyIntensity = 0.2;
material.subSurface.tintColor = new BABYLON.Color3(0.9, 0.9, 0.8);
// Create marble texture
material.albedoTexture = this.createMarbleTexture();
material.bumpTexture = this.createMarbleBumpTexture();
return material;
}
private createHolographicMaterial(): BABYLON.PBRMaterial {
const material = new BABYLON.PBRMaterial('holographic', this.scene);
material.albedoColor = new BABYLON.Color3(1, 1, 1);
material.metallic = 1.0;
material.roughness = 0.1;
// Create iridescent effect
material.emissiveTexture = this.createIridescentTexture();
material.emissiveIntensity = 0.5;
// Animate the holographic effect
this.scene.registerBeforeRender(() => {
const time = Date.now() * 0.001;
material.emissiveColor = new BABYLON.Color3(
Math.sin(time) * 0.5 + 0.5,
Math.sin(time + 2) * 0.5 + 0.5,
Math.sin(time + 4) * 0.5 + 0.5
);
});
return material;
}
private createPBRGround(): void {
const ground = BABYLON.MeshBuilder.CreateGround('ground', { width: 30, height: 30 }, this.scene);
ground.position.y = -3;
const groundMaterial = new BABYLON.PBRMetallicRoughnessMaterial('groundPBR', this.scene);
groundMaterial.baseColor = new BABYLON.Color3(0.1, 0.1, 0.1);
groundMaterial.metallic = 0.1;
groundMaterial.roughness = 0.8;
groundMaterial.baseTexture = this.createGroundTexture();
groundMaterial.normalTexture = this.createGroundNormalTexture();
ground.material = groundMaterial;
}
// Texture creation helper methods
private createNormalMap(): BABYLON.Texture {
return this.createProceduralTexture('normalMap', (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const x = (i / 4) % w;
const y = Math.floor((i / 4) / w);
const wave = Math.sin(x * 0.1) * Math.cos(y * 0.1);
imageData.data[i] = 128 + wave * 50; // R
imageData.data[i + 1] = 128 - wave * 30; // G
imageData.data[i + 2] = 255; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
}
private createFabricTexture(): BABYLON.Texture {
return this.createProceduralTexture('fabricTexture', (ctx, w, h) => {
ctx.fillStyle = '#8B4513';
ctx.fillRect(0, 0, w, h);
// Create fabric weave pattern
ctx.strokeStyle = '#654321';
ctx.lineWidth = 1;
for (let x = 0; x < w; x += 4) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, h);
ctx.stroke();
}
for (let y = 0; y < h; y += 4) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(w, y);
ctx.stroke();
}
});
}
private createFabricBumpTexture(): BABYLON.Texture {
return this.createProceduralTexture('fabricBump', (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const x = (i / 4) % w;
const y = Math.floor((i / 4) / h);
const weave = (Math.floor(x / 4) + Math.floor(y / 4)) % 2;
const value = weave ? 180 : 80;
imageData.data[i] = 128; // R
imageData.data[i + 1] = 128; // G
imageData.data[i + 2] = value; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
}
private createWoodGrainTexture(): BABYLON.Texture {
return this.createProceduralTexture('woodGrain', (ctx, w, h) => {
const gradient = ctx.createLinearGradient(0, 0, w, 0);
gradient.addColorStop(0, '#4A2C17');
gradient.addColorStop(0.3, '#6B4423');
gradient.addColorStop(0.6, '#8B5A2B');
gradient.addColorStop(1, '#4A2C17');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, w, h);
// Add grain lines
ctx.strokeStyle = '#2A1A0B';
ctx.lineWidth = 1;
for (let i = 0; i < 20; i++) {
ctx.beginPath();
ctx.moveTo(0, Math.random() * h);
ctx.quadraticCurveTo(
w/2, Math.random() * h,
w, Math.random() * h
);
ctx.stroke();
}
});
}
private createWoodNormalTexture(): BABYLON.Texture {
return this.createProceduralTexture('woodNormal', (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const x = (i / 4) % w;
const y = Math.floor((i / 4) / h);
const grain = Math.sin(x * 0.05) * 20;
imageData.data[i] = 128 + grain; // R
imageData.data[i + 1] = 128; // G
imageData.data[i + 2] = 255; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
}
private createMarbleTexture(): BABYLON.Texture {
return this.createProceduralTexture('marble', (ctx, w, h) => {
ctx.fillStyle = '#F5F5DC';
ctx.fillRect(0, 0, w, h);
// Create marble veins
ctx.strokeStyle = '#8B8680';
ctx.lineWidth = 2;
for (let i = 0; i < 15; i++) {
ctx.beginPath();
ctx.moveTo(Math.random() * w, 0);
for (let y = 0; y < h; y += 10) {
const x = Math.random() * w;
ctx.lineTo(x, y);
}
ctx.globalAlpha = 0.3;
ctx.stroke();
}
ctx.globalAlpha = 1;
});
}
private createMarbleBumpTexture(): BABYLON.Texture {
return this.createProceduralTexture('marbleBump', (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const x = (i / 4) % w;
const y = Math.floor((i / 4) / h);
const noise = Math.sin(x * 0.02) * Math.cos(y * 0.02);
imageData.data[i] = 128 + noise * 30; // R
imageData.data[i + 1] = 128 + noise * 30; // G
imageData.data[i + 2] = 128 + noise * 20; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
}
private createIridescentTexture(): BABYLON.Texture {
return this.createProceduralTexture('iridescent', (ctx, w, h) => {
for (let x = 0; x < w; x++) {
for (let y = 0; y < h; y++) {
const hue = (x / w) * 360;
const lightness = 50 + Math.sin((x / w) * Math.PI * 4) * 20;
ctx.fillStyle = `hsl(${hue}, 100%, ${lightness}%)`;
ctx.fillRect(x, y, 1, 1);
}
}
});
}
private createGroundTexture(): BABYLON.Texture {
return this.createProceduralTexture('ground', (ctx, w, h) => {
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(0, 0, w, h);
// Add concrete texture
ctx.fillStyle = '#2a2a2a';
for (let i = 0; i < 100; i++) {
const x = Math.random() * w;
const y = Math.random() * h;
const size = Math.random() * 3;
ctx.fillRect(x, y, size, size);
}
});
}
private createGroundNormalTexture(): BABYLON.Texture {
return this.createProceduralTexture('groundNormal', (ctx, w, h) => {
const imageData = ctx.createImageData(w, h);
for (let i = 0; i < imageData.data.length; i += 4) {
const noise = Math.random() * 20 - 10;
imageData.data[i] = 128 + noise; // R
imageData.data[i + 1] = 128 + noise; // G
imageData.data[i + 2] = 255; // B
imageData.data[i + 3] = 255; // A
}
ctx.putImageData(imageData, 0, 0);
});
}
private createProceduralTexture(name: string, generator: (ctx: CanvasRenderingContext2D, w: number, h: number) => void): BABYLON.Texture {
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const context = canvas.getContext('2d')!;
generator(context, 512, 512);
const texture = new BABYLON.Texture(`${name}Texture`, this.scene, false, false, BABYLON.Texture.TRILINEAR_SAMPLINGMODE, null, canvas);
texture.wrapU = BABYLON.Texture.WRAP_ADDRESSMODE;
texture.wrapV = BABYLON.Texture.WRAP_ADDRESSMODE;
return texture;
}
private createMaterialControls(): void {
// Add UI for material controls
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
const panel = new BABYLON.GUI.StackPanel();
panel.width = '250px';
panel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
panel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
panel.top = '20px';
advancedTexture.addControl(panel);
const title = new BABYLON.GUI.TextBlock();
title.text = 'PBR Material Showcase';
title.color = 'white';
title.fontSize = 16;
title.fontWeight = 'bold';
title.height = '30px';
panel.addControl(title);
const info = new BABYLON.GUI.TextBlock();
info.text = 'Rotate camera to view different PBR materials\nEach material demonstrates different properties';
info.color = 'white';
info.fontSize = 12;
info.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
info.height = '60px';
panel.addControl(info);
}
private startRenderLoop(): void {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
}
export {
MaterialShowcase,
PBRMaterialDemo
};
💻 Animations and Interactions typescript
🔴 complex
⭐⭐⭐⭐
Advanced animations, user interactions, physics, particle systems, and interactive 3D experiences in Babylon.js
⏱️ 45 min
🏷️ babylonjs, animation, physics, interaction
Prerequisites:
Babylon.js basics, TypeScript ES6+, Animation principles
// Babylon.js Animations and Interactions Examples
import * as BABYLON from '@babylonjs/core';
import { GridMaterial } from '@babylonjs/materials/grid';
import '@babylonjs/loaders';
// 1. Advanced Animation System
class AdvancedAnimationSystem {
private engine: BABYLON.Engine;
private scene: BABYLON.Scene;
private camera: BABYLON.ArcRotateCamera;
private animations: Map<string, BABYLON.AnimationGroup> = new Map();
constructor(canvas: HTMLCanvasElement) {
this.engine = new BABYLON.Engine(canvas, true);
this.scene = new BABYLON.Scene(this.engine);
this.scene.clearColor = new BABYLON.Color4(0.05, 0.05, 0.15, 1);
this.setupCamera();
this.setupLighting();
this.createAnimatedObjects();
this.setupInteractionSystem();
this.createAnimationUI();
this.startRenderLoop();
}
private setupCamera(): void {
this.camera = new BABYLON.ArcRotateCamera(
'camera',
0,
Math.PI / 3,
25,
BABYLON.Vector3.Zero(),
this.scene
);
this.camera.attachControl(this.engine.getRenderingCanvas(), true);
this.camera.lowerRadiusLimit = 10;
this.camera.upperRadiusLimit = 50;
}
private setupLighting(): void {
const ambientLight = new BABYLON.HemisphericLight(
'ambientLight',
new BABYLON.Vector3(0, 1, 0),
this.scene
);
ambientLight.intensity = 0.4;
const mainLight = new BABYLON.DirectionalLight(
'mainLight',
new BABYLON.Vector3(-1, -2, 1),
this.scene
);
mainLight.intensity = 2;
mainLight.position = new BABYLON.Vector3(20, 40, 20);
// Add animated point light
const animatedLight = new BABYLON.PointLight(
'animatedLight',
new BABYLON.Vector3(0, 5, 0),
this.scene
);
animatedLight.intensity = 1;
animatedLight.diffuse = new BABYLON.Color3(1, 0.5, 0.2);
// Animate light position
const lightAnimation = BABYLON.Animation.CreateAndStartAnimation(
'lightMove',
animatedLight,
'position',
30,
120,
animatedLight.position.clone(),
new BABYLON.Vector3(10, 5, 10),
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
}
private createAnimatedObjects(): void {
this.createRobotCharacter();
this.createParticleEffects();
this.createMorphingGeometry();
this.createProceduralAnimations();
this.createPhysicsAnimations();
this.createSoundReactiveObjects();
}
private createRobotCharacter(): void {
const robot = new BABYLON.TransformNode('robot');
// Body
const body = BABYLON.MeshBuilder.CreateBox('body', { width: 2, height: 3, depth: 1.5 }, this.scene);
const bodyMaterial = new BABYLON.StandardMaterial('bodyMat', this.scene);
bodyMaterial.diffuseColor = new BABYLON.Color3(0.7, 0.7, 0.8);
bodyMaterial.specularColor = new BABYLON.Color3(0.2, 0.2, 0.3);
body.material = bodyMaterial;
body.parent = robot;
// Head
const head = BABYLON.MeshBuilder.CreateSphere('head', { diameter: 1.2 }, this.scene);
head.position.y = 2.5;
const headMaterial = new BABYLON.StandardMaterial('headMat', this.scene);
headMaterial.diffuseColor = new BABYLON.Color3(0.9, 0.9, 0.9);
head.material = headMaterial;
head.parent = robot;
// Eyes
const leftEye = BABYLON.MeshBuilder.CreateSphere('leftEye', { diameter: 0.2 }, this.scene);
leftEye.position.set(-0.3, 2.6, 0.5);
const eyeMaterial = new BABYLON.StandardMaterial('eyeMat', this.scene);
eyeMaterial.emissiveColor = new BABYLON.Color3(0, 1, 0);
leftEye.material = eyeMaterial;
leftEye.parent = robot;
const rightEye = BABYLON.MeshBuilder.CreateSphere('rightEye', { diameter: 0.2 }, this.scene);
rightEye.position.set(0.3, 2.6, 0.5);
rightEye.material = eyeMaterial;
rightEye.parent = robot;
// Arms
const leftArm = BABYLON.MeshBuilder.CreateBox('leftArm', { width: 0.4, height: 2.5, depth: 0.4 }, this.scene);
leftArm.position.set(-1.5, 0.5, 0);
const armMaterial = new BABYLON.StandardMaterial('armMat', this.scene);
armMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.5, 0.6);
leftArm.material = armMaterial;
leftArm.parent = robot;
const rightArm = BABYLON.MeshBuilder.CreateBox('rightArm', { width: 0.4, height: 2.5, depth: 0.4 }, this.scene);
rightArm.position.set(1.5, 0.5, 0);
rightArm.material = armMaterial;
rightArm.parent = robot;
// Legs
const leftLeg = BABYLON.MeshBuilder.CreateBox('leftLeg', { width: 0.5, height: 2, depth: 0.5 }, this.scene);
leftLeg.position.set(-0.5, -2.5, 0);
leftLeg.material = armMaterial;
leftLeg.parent = robot;
const rightLeg = BABYLON.MeshBuilder.CreateBox('rightLeg', { width: 0.5, height: 2, depth: 0.5 }, this.scene);
rightLeg.position.set(0.5, -2.5, 0);
rightLeg.material = armMaterial;
rightLeg.parent = robot;
robot.position.set(0, 2, 0);
// Create walk animation group
const walkAnimationGroup = new BABYLON.AnimationGroup('walk', this.scene);
// Body bob animation
const bodyBobAnimation = BABYLON.Animation.CreateAndStartAnimation(
'bodyBob',
robot,
'position.y',
30,
30,
2,
2.5,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(bodyBobAnimation, robot);
// Arm swing animations
const leftArmSwing = BABYLON.Animation.CreateAndStartAnimation(
'leftArmSwing',
leftArm,
'rotation.z',
30,
30,
0,
Math.PI / 4,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(leftArmSwing, leftArm);
const rightArmSwing = BABYLON.Animation.CreateAndStartAnimation(
'rightArmSwing',
rightArm,
'rotation.z',
30,
30,
0,
-Math.PI / 4,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(rightArmSwing, rightArm);
// Leg swing animations
const leftLegSwing = BABYLON.Animation.CreateAndStartAnimation(
'leftLegSwing',
leftLeg,
'rotation.x',
30,
30,
0,
Math.PI / 6,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(leftLegSwing, leftLeg);
const rightLegSwing = BABYLON.Animation.CreateAndStartAnimation(
'rightLegSwing',
rightLeg,
'rotation.x',
30,
30,
0,
-Math.PI / 6,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(rightLegSwing, rightLeg);
// Head rotation
const headLook = BABYLON.Animation.CreateAndStartAnimation(
'headLook',
head,
'rotation.y',
30,
60,
0,
Math.PI * 2,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(headLook, head);
// Eye blinking
const eyeBlink = BABYLON.Animation.CreateAndStartAnimation(
'eyeBlink',
leftEye,
'scaling.y',
30,
90,
1,
0.1,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
walkAnimationGroup.addTargetedAnimation(eyeBlink, leftEye);
walkAnimationGroup.addTargetedAnimation(eyeBlink.clone(), rightEye);
this.animations.set('walk', walkAnimationGroup);
// Create jump animation
const jumpAnimationGroup = new BABYLON.AnimationGroup('jump', this.scene);
const jumpUp = BABYLON.Animation.CreateAndStartAnimation(
'jumpUp',
robot,
'position.y',
30,
20,
2,
6,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
jumpAnimationGroup.addTargetedAnimation(jumpUp, robot);
const jumpDown = BABYLON.Animation.CreateAndStartAnimation(
'jumpDown',
robot,
'position.y',
30,
20,
6,
2,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
jumpDown.setKeys([
{ frame: 0, value: 2 },
{ frame: 20, value: 6 },
{ frame: 40, value: 2 }
]);
jumpAnimationGroup.addTargetedAnimation(jumpDown, robot);
this.animations.set('jump', jumpAnimationGroup);
// Store robot reference for interaction
robot.metadata = { type: 'robot', animations: ['walk', 'jump'] };
this.scene.metadata = { robot, currentAnimation: 'walk' };
}
private createParticleEffects(): void {
// Create multiple particle systems
// Fire effect
const fireSystem = new BABYLON.ParticleSystem('fire', 2000, this.scene);
fireSystem.particleTexture = this.createFireTexture();
fireSystem.emitter = new BABYLON.Vector3(-8, 0, 0);
fireSystem.minEmitBox = new BABYLON.Vector3(-1, 0, -1);
fireSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 1);
fireSystem.color1 = new BABYLON.Color4(1, 0.5, 0, 1);
fireSystem.color2 = new BABYLON.Color4(1, 0, 0, 1);
fireSystem.colorDead = new BABYLON.Color4(0, 0, 0, 0);
fireSystem.minSize = 0.5;
fireSystem.maxSize = 2;
fireSystem.minLifeTime = 0.5;
fireSystem.maxLifeTime = 2;
fireSystem.emitRate = 500;
fireSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
fireSystem.gravity = new BABYLON.Vector3(0, -2, 0);
fireSystem.direction1 = new BABYLON.Vector3(-1, 2, -1);
fireSystem.direction2 = new BABYLON.Vector3(1, 4, 1);
fireSystem.minAngularSpeed = -1;
fireSystem.maxAngularSpeed = 1;
fireSystem.minEmitPower = 1;
fireSystem.maxEmitPower = 3;
fireSystem.updateSpeed = 0.01;
fireSystem.start();
// Snow effect
const snowSystem = new BABYLON.ParticleSystem('snow', 1000, this.scene);
snowSystem.particleTexture = new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', this.scene);
snowSystem.emitter = new BABYLON.Vector3(0, 20, 0);
snowSystem.minEmitBox = new BABYLON.Vector3(-15, 0, -15);
snowSystem.maxEmitBox = new BABYLON.Vector3(15, 0, 15);
snowSystem.color1 = new BABYLON.Color4(1, 1, 1, 1);
snowSystem.color2 = new BABYLON.Color4(0.8, 0.8, 1, 1);
snowSystem.colorDead = new BABYLON.Color4(0, 0, 0, 0);
snowSystem.minSize = 0.2;
snowSystem.maxSize = 0.8;
snowSystem.minLifeTime = 5;
snowSystem.maxLifeTime = 15;
snowSystem.emitRate = 50;
snowSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
snowSystem.gravity = new BABYLON.Vector3(0, -0.5, 0);
snowSystem.direction1 = new BABYLON.Vector3(-0.5, -1, -0.5);
snowSystem.direction2 = new BABYLON.Vector3(0.5, -1, 0.5);
snowSystem.minAngularSpeed = 0;
snowSystem.maxAngularSpeed = Math.PI;
snowSystem.minEmitPower = 0.5;
snowSystem.maxEmitPower = 1.5;
snowSystem.start();
// Magic portal effect
const portalSystem = new BABYLON.ParticleSystem('portal', 1500, this.scene);
portalSystem.particleTexture = this.createPortalTexture();
portalSystem.emitter = new BABYLON.Vector3(8, 2, 0);
portalSystem.minEmitBox = new BABYLON.Vector3(-1, 0, -1);
portalSystem.maxEmitBox = new BABYLON.Vector3(1, 4, 1);
portalSystem.color1 = new BABYLON.Color4(0.5, 0, 1, 1);
portalSystem.color2 = new BABYLON.Color4(1, 0, 1, 1);
portalSystem.colorDead = new BABYLON.Color4(0, 0, 0.5, 0);
portalSystem.minSize = 0.3;
portalSystem.maxSize = 1;
portalSystem.minLifeTime = 1;
portalSystem.maxLifeTime = 3;
portalSystem.emitRate = 300;
portalSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
portalSystem.gravity = new BABYLON.Vector3(0, 0, 0);
portalSystem.direction1 = new BABYLON.Vector3(-2, 2, -2);
portalSystem.direction2 = new BABYLON.Vector3(2, 2, 2);
portalSystem.minAngularSpeed = -Math.PI;
portalSystem.maxAngularSpeed = Math.PI;
portalSystem.minEmitPower = 0.5;
portalSystem.maxEmitPower = 2;
portalSystem.start();
// Create platform for effects
const platform1 = BABYLON.MeshBuilder.CreateCylinder(
'firePlatform',
{ diameter: 3, height: 0.5 },
this.scene
);
platform1.position.set(-8, -0.25, 0);
const platformMaterial1 = new BABYLON.StandardMaterial('firePlatformMat', this.scene);
platformMaterial1.diffuseColor = new BABYLON.Color3(0.3, 0.1, 0);
platformMaterial1.emissiveColor = new BABYLON.Color3(0.2, 0.05, 0);
platform1.material = platformMaterial1;
const portalFrame = BABYLON.MeshBuilder.CreateTorus(
'portalFrame',
{ diameter: 4, thickness: 0.3, tessellation: 32 },
this.scene
);
portalFrame.position.set(8, 2, 0);
const portalMaterial = new BABYLON.StandardMaterial('portalMat', this.scene);
portalMaterial.diffuseColor = new BABYLON.Color3(0.5, 0, 1);
portalMaterial.emissiveColor = new BABYLON.Color3(0.3, 0, 0.6);
portalMaterial.specularColor = new BABYLON.Color3(1, 0, 1);
portalFrame.material = portalMaterial;
// Animate portal frame
this.scene.registerBeforeRender(() => {
portalFrame.rotation.y += 0.02;
portalFrame.rotation.x = Math.sin(Date.now() * 0.001) * 0.1;
});
}
private createMorphingGeometry(): void {
// Create morphing sphere
const sphere = BABYLON.MeshBuilder.CreateSphere(
'morphingSphere',
{ diameter: 3, segments: 32 },
this.scene
);
sphere.position.set(0, 5, 0);
const sphereMaterial = new BABYLON.StandardMaterial('morphingSphereMat', this.scene);
sphereMaterial.diffuseColor = new BABYLON.Color3(0.5, 0.8, 1);
sphereMaterial.specularColor = new BABYLON.Color3(1, 1, 1);
sphereMaterial.specularPower = 128;
sphereMaterial.emissiveColor = new BABYLON.Color3(0.1, 0.2, 0.3);
sphere.material = sphereMaterial;
// Store original positions
const positions = sphere.getVerticesData(BABYLON.VertexBuffer.PositionKind);
const originalPositions = new Float32Array(positions!);
// Animate morphing
this.scene.registerBeforeRender(() => {
const time = Date.now() * 0.001;
const newPositions = new Float32Array(positions!.length);
for (let i = 0; i < positions!.length; i += 3) {
const x = originalPositions[i];
const y = originalPositions[i + 1];
const z = originalPositions[i + 2];
// Apply wave deformation
const wave = Math.sin(x * 2 + time * 2) * Math.cos(z * 2 + time * 3);
const pulse = Math.sin(time * 2) * 0.1;
newPositions[i] = x * (1 + pulse + wave * 0.1);
newPositions[i + 1] = y * (1 + pulse) + Math.sin(time * 3 + x * 3) * 0.2;
newPositions[i + 2] = z * (1 + pulse + wave * 0.1);
}
sphere.updateVerticesData(BABYLON.VertexBuffer.PositionKind, newPositions);
});
}
private createProceduralAnimations(): void {
// Create animated grid of cubes
const gridSize = 5;
const spacing = 2;
const cubes: BABYLON.Mesh[] = [];
for (let x = 0; x < gridSize; x++) {
for (let z = 0; z < gridSize; z++) {
const cube = BABYLON.MeshBuilder.CreateBox(
`cube${x}_${z}`,
{ size: 1 },
this.scene
);
cube.position.set(
(x - gridSize / 2) * spacing,
0,
(z - gridSize / 2) * spacing
);
const material = new BABYLON.StandardMaterial(`cubeMat${x}_${z}`, this.scene);
const hue = (x + z) / (gridSize * 2);
material.diffuseColor = BABYLON.Color3.FromHSV(hue * 360, 1, 1);
material.emissiveColor = material.diffuseColor.scale(0.3);
cube.material = material;
cubes.push(cube);
}
}
// Animate the grid
this.scene.registerBeforeRender(() => {
const time = Date.now() * 0.001;
cubes.forEach((cube, index) => {
const x = index % gridSize;
const z = Math.floor(index / gridSize);
// Create wave effect
const distance = Math.sqrt(
Math.pow(x - gridSize / 2, 2) +
Math.pow(z - gridSize / 2, 2)
);
const wave = Math.sin(time * 2 - distance * 0.5) * 0.5 + 0.5;
cube.position.y = wave * 3;
cube.rotation.y = time + index * 0.1;
cube.scaling = cube.scaling.scale(1 + wave * 0.3);
// Change material properties based on animation
const mat = cube.material as BABYLON.StandardMaterial;
mat.emissiveColor = mat.diffuseColor!.scale(wave * 0.5);
});
});
}
private createPhysicsAnimations(): void {
// Enable physics
this.scene.enablePhysics(
new BABYLON.Vector3(0, -9.81, 0),
new BABYLON.CannonJSPlugin()
);
// Create physics playground
const ground = BABYLON.MeshBuilder.CreateGround('physicsGround', { width: 20, height: 20 }, this.scene);
ground.position.set(-15, 0, 0);
const groundMaterial = new BABYLON.StandardMaterial('physicsGroundMat', this.scene);
groundMaterial.diffuseColor = new BABYLON.Color3(0.3, 0.3, 0.3);
ground.material = groundMaterial;
ground.physicsImpostor = new BABYLON.PhysicsImpostor(
ground,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0, restitution: 0.8 },
this.scene
);
// Create walls
this.createPhysicsWalls();
// Create physics objects periodically
this.physicsSpawnTimer = 0;
this.scene.registerBeforeRender(() => {
this.physicsSpawnTimer += this.engine.getDeltaTime() / 1000;
if (this.physicsSpawnTimer > 1) {
this.spawnPhysicsObject();
this.physicsSpawnTimer = 0;
}
});
}
private createPhysicsWalls(): void {
const wallMaterial = new BABYLON.StandardMaterial('wallMat', this.scene);
wallMaterial.diffuseColor = new BABYLON.Color3(0.6, 0.6, 0.6);
wallMaterial.alpha = 0.5;
// Create invisible walls
const walls = [
{ position: new BABYLON.Vector3(-25, 5, 0), size: new BABYLON.Vector3(1, 10, 20) },
{ position: new BABYLON.Vector3(-5, 5, -10), size: new BABYLON.Vector3(20, 10, 1) },
{ position: new BABYLON.Vector3(-5, 5, 10), size: new BABYLON.Vector3(20, 10, 1) }
];
walls.forEach(wallData => {
const wall = BABYLON.MeshBuilder.CreateBox(
`wall${walls.indexOf(wallData)}`,
{ width: wallData.size.x, height: wallData.size.y, depth: wallData.size.z },
this.scene
);
wall.position = wallData.position;
wall.material = wallMaterial;
wall.physicsImpostor = new BABYLON.PhysicsImpostor(
wall,
BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 0, restitution: 0.5 },
this.scene
);
});
}
private spawnPhysicsObject(): void {
const shapes = ['box', 'sphere', 'cylinder'];
const shape = shapes[Math.floor(Math.random() * shapes.length)];
let mesh: BABYLON.Mesh;
const position = new BABYLON.Vector3(
-15 + (Math.random() - 0.5) * 10,
5 + Math.random() * 5,
(Math.random() - 0.5) * 5
);
switch (shape) {
case 'box':
mesh = BABYLON.MeshBuilder.CreateBox(`physicsBox${Date.now()}`, { size: 1 }, this.scene);
break;
case 'sphere':
mesh = BABYLON.MeshBuilder.CreateSphere(`physicsSphere${Date.now()}`, { diameter: 1 }, this.scene);
break;
case 'cylinder':
mesh = BABYLON.MeshBuilder.CreateCylinder(`physicsCylinder${Date.now()}`, { height: 1, diameter: 1 }, this.scene);
break;
}
mesh.position = position;
const material = new BABYLON.StandardMaterial(`physicsMat${Date.now()}`, this.scene);
const hue = Math.random();
material.diffuseColor = BABYLON.Color3.FromHSV(hue * 360, 1, 1);
material.emissiveColor = material.diffuseColor.scale(0.3);
mesh.material = material;
// Add physics
mesh.physicsImpostor = new BABYLON.PhysicsImpostor(
mesh,
shape === 'sphere' ? BABYLON.PhysicsImpostor.SphereImpostor : BABYLON.PhysicsImpostor.BoxImpostor,
{ mass: 1, restitution: 0.7, friction: 0.5 },
this.scene
);
// Apply random impulse
if (mesh.physicsImpostor) {
const impulse = new BABYLON.Vector3(
(Math.random() - 0.5) * 10,
Math.random() * 5,
(Math.random() - 0.5) * 10
);
mesh.physicsImpostor.applyImpulse(impulse, mesh.position);
}
}
private createSoundReactiveObjects(): void {
// Create animated spheres that react to "audio" (simulated)
const audioReactiveSpheres: BABYLON.Mesh[] = [];
const sphereCount = 8;
for (let i = 0; i < sphereCount; i++) {
const sphere = BABYLON.MeshBuilder.CreateSphere(
`audioSphere${i}`,
{ diameter: 1.5, segments: 16 },
this.scene
);
const angle = (i / sphereCount) * Math.PI * 2;
sphere.position.set(
Math.cos(angle) * 6,
2,
Math.sin(angle) * 6 + 10
);
const material = new BABYLON.StandardMaterial(`audioMat${i}`, this.scene);
material.diffuseColor = BABYLON.Color3.FromHSV((i / sphereCount) * 360, 1, 1);
material.emissiveColor = new BABYLON.Color3(0, 0, 0);
sphere.material = material;
audioReactiveSpheres.push(sphere);
}
// Simulate audio reaction
this.scene.registerBeforeRender(() => {
const time = Date.now() * 0.001;
audioReactiveSpheres.forEach((sphere, index) => {
// Simulate frequency bands
const frequency = Math.sin(time * (index + 1) * 0.5) * 0.5 + 0.5;
const bass = Math.sin(time * 0.8) * 0.5 + 0.5;
// Scale based on simulated audio
const scale = 1 + frequency * 2;
sphere.scaling = new BABYLON.Vector3(scale, scale, scale);
// Change color intensity
const mat = sphere.material as BABYLON.StandardMaterial;
const intensity = bass * 0.8;
mat.emissiveColor = mat.diffuseColor!.scale(intensity);
// Move up and down
sphere.position.y = 2 + Math.sin(time * (index + 1) * 0.3) * 1.5;
});
});
}
private setupInteractionSystem(): void {
// Ray casting for object selection
this.scene.onPointerObservable.add((pointerInfo) => {
if (pointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
const pickResult = this.scene.pick(pointerInfo.event.offsetX, pointerInfo.event.offsetY);
if (pickResult && pickResult.hit && pickResult.pickedMesh) {
this.handleObjectInteraction(pickResult.pickedMesh);
}
}
});
// Keyboard controls
window.addEventListener('keydown', (event) => {
switch (event.key.toLowerCase()) {
case '1':
this.switchAnimation('walk');
break;
case '2':
this.switchAnimation('jump');
break;
case ' ':
this.createExplosion(this.camera.position.add(this.camera.getDirection(new BABYLON.Vector3(0, 0, 1)).scale(10)));
break;
}
});
}
private handleObjectInteraction(mesh: BABYLON.Mesh): void {
// Visual feedback
this.createClickEffect(mesh.position);
// Add impulse if physics object
if (mesh.physicsImpostor && mesh.physicsImpostor.mass > 0) {
const direction = this.camera.position.subtract(mesh.position).normalize();
const impulse = direction.scale(-10);
mesh.physicsImpostor.applyImpulse(impulse, mesh.position);
}
// Scale animation for non-physics objects
if (!mesh.physicsImpostor || mesh.physicsImpostor.mass === 0) {
BABYLON.Animation.CreateAndStartAnimation(
'clickEffect',
mesh,
'scaling',
30,
10,
mesh.scaling.clone(),
mesh.scaling.scale(1.5),
BABYLON.Animation.ANIMATIONLOOPMODE_YOYO
);
}
}
private createClickEffect(position: BABYLON.Vector3): void {
const particleSystem = new BABYLON.ParticleSystem('clickEffect', 100, this.scene);
particleSystem.particleTexture = new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', this.scene);
particleSystem.emitter = position;
particleSystem.minEmitBox = new BABYLON.Vector3(-0.1, -0.1, -0.1);
particleSystem.maxEmitBox = new BABYLON.Vector3(0.1, 0.1, 0.1);
particleSystem.color1 = new BABYLON.Color4(1, 1, 1, 1);
particleSystem.color2 = new BABYLON.Color4(1, 1, 0, 1);
particleSystem.colorDead = new BABYLON.Color4(1, 0, 0, 0);
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.3;
particleSystem.minLifeTime = 0.5;
particleSystem.maxLifeTime = 1.5;
particleSystem.emitRate = 100;
particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
particleSystem.gravity = new BABYLON.Vector3(0, -1, 0);
particleSystem.direction1 = new BABYLON.Vector3(-1, 1, -1);
particleSystem.direction2 = new BABYLON.Vector3(1, 1, 1);
particleSystem.start();
setTimeout(() => {
particleSystem.stop();
}, 100);
}
private createExplosion(position: BABYLON.Vector3): void {
const explosionSystem = new BABYLON.ParticleSystem('explosion', 500, this.scene);
explosionSystem.particleTexture = this.createExplosionTexture();
explosionSystem.emitter = position;
explosionSystem.minEmitBox = new BABYLON.Vector3(-1, -1, -1);
explosionSystem.maxEmitBox = new BABYLON.Vector3(1, 1, 1);
explosionSystem.color1 = new BABYLON.Color4(1, 1, 0, 1);
explosionSystem.color2 = new BABYLON.Color4(1, 0.5, 0, 1);
explosionSystem.colorDead = new BABYLON.Color4(1, 0, 0, 0);
explosionSystem.minSize = 0.5;
explosionSystem.maxSize = 2;
explosionSystem.minLifeTime = 0.5;
explosionSystem.maxLifeTime = 2;
explosionSystem.emitRate = 500;
explosionSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ADD;
explosionSystem.gravity = new BABYLON.Vector3(0, -5, 0);
explosionSystem.direction1 = new BABYLON.Vector3(-2, 2, -2);
explosionSystem.direction2 = new BABYLON.Vector3(2, 5, 2);
explosionSystem.start();
// Apply explosion force to nearby physics objects
this.scene.meshes.forEach(mesh => {
if (mesh.physicsImpostor && mesh.physicsImpostor.mass > 0) {
const distance = BABYLON.Vector3.Distance(mesh.position, position);
if (distance < 10) {
const direction = mesh.position.subtract(position).normalize();
const force = direction.scale(50 / (distance + 1));
mesh.physicsImpostor.applyImpulse(force, mesh.position);
}
}
});
}
private switchAnimation(animationName: string): void {
const animation = this.animations.get(animationName);
if (animation) {
// Stop all other animations
this.animations.forEach((anim) => {
if (anim !== animation) {
anim.stop();
}
});
// Start the requested animation
animation.reset();
animation.play(true);
// Update current animation display
if (this.scene.metadata.currentAnimationText) {
this.scene.metadata.currentAnimationText.text = `Current Animation: ${animationName}`;
}
}
}
private createAnimationUI(): void {
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
// Instructions panel
const instructions = new BABYLON.GUI.TextBlock();
instructions.text = 'Click objects to interact | 1: Walk Animation | 2: Jump Animation | Space: Explosion';
instructions.color = 'white';
instructions.fontSize = 14;
instructions.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
instructions.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
instructions.top = '20px';
advancedTexture.addControl(instructions);
// Current animation display
const currentAnimText = new BABYLON.GUI.TextBlock();
currentAnimText.text = 'Current Animation: walk';
currentAnimText.color = 'yellow';
currentAnimText.fontSize = 16;
currentAnimText.fontWeight = 'bold';
currentAnimText.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
currentAnimText.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP;
currentAnimText.top = '50px';
advancedTexture.addControl(currentAnimText);
this.scene.metadata.currentAnimationText = currentAnimText;
// Performance monitor
const perfText = new BABYLON.GUI.TextBlock();
perfText.text = 'FPS: --';
perfText.color = 'white';
perfText.fontSize = 12;
perfText.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
perfText.textVerticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
perfText.left = '20px';
perfText.top = '-40px';
advancedTexture.addControl(perfText);
// Update FPS counter
let lastTime = performance.now();
let frames = 0;
this.scene.registerBeforeRender(() => {
frames++;
const currentTime = performance.now();
if (currentTime >= lastTime + 1000) {
const fps = Math.round((frames * 1000) / (currentTime - lastTime));
perfText.text = `FPS: ${fps} | Meshes: ${this.scene.meshes.length} | Particles: Active`;
frames = 0;
lastTime = currentTime;
}
});
}
// Texture creation helper methods
private createFireTexture(): BABYLON.Texture {
return new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', this.scene);
}
private createPortalTexture(): BABYLON.Texture {
return new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', this.scene);
}
private createExplosionTexture(): BABYLON.Texture {
return new BABYLON.Texture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==', this.scene);
}
private startRenderLoop(): void {
this.engine.runRenderLoop(() => {
this.scene.render();
});
}
}
export {
AdvancedAnimationSystem
};