🎯 Exemplos recomendados
Balanced sample collections from various categories for you to explore
Exemplos Babylon.js
Exemplos do motor 3D Babylon.js incluindo cenas, meshes, materiais, iluminação, física e aplicações web 3D interativas
💻 Babylon.js Hello World typescript
🟢 simple
⭐
Configuração básica de cena Babylon.js com motor, cena, câmera, luzes e objetos 3D simples
⏱️ 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(canvas, 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(canvas, 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
};
💻 Materiais e Shaders typescript
🟡 intermediate
⭐⭐⭐
Materiais avançados do Babylon.js, shaders PBR, texturas, materiais personalizados e renderização de superfícies realistas
⏱️ 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(canvas, 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(canvas, 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
};
💻 Animações e Interações typescript
🔴 complex
⭐⭐⭐⭐
Animações avançadas, interações do usuário, física, sistemas de partículas e experiências 3D interativas no 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(canvas, 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
};