🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Exemples Three.js
Exemples de la bibliothèque graphique 3D Three.js incluant scènes, meshes, éclairage, matériaux, animations et contenu 3D interactif
💻 Three.js Hello World javascript
🟢 simple
⭐
Configuration de base Three.js avec scènes, caméras, renderers et objets 3D simples
⏱️ 15 min
🏷️ threejs, 3d, webgl, graphics
Prerequisites:
JavaScript basics, WebGL concepts
// Three.js Hello World Examples
import * as THREE from 'three';
// 1. Basic Three.js Scene Setup
class BasicScene {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
// Create scene
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x87CEEB);
// Create camera
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.z = 5;
// Create renderer
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
this.container.appendChild(this.renderer.domElement);
// Add lights
this.addLights();
// Add objects
this.addObjects();
// Handle window resize
window.addEventListener('resize', this.onWindowResize.bind(this));
// Start animation loop
this.animate();
}
addLights() {
// Ambient light
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
this.scene.add(ambientLight);
// Directional light
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 5);
directionalLight.castShadow = true;
this.scene.add(directionalLight);
}
addObjects() {
// Add a rotating cube
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshPhongMaterial({
color: 0x00ff00,
specular: 0x444444,
shininess: 100
});
this.cube = new THREE.Mesh(geometry, material);
this.cube.castShadow = true;
this.scene.add(this.cube);
// Add a sphere
const sphereGeometry = new THREE.SphereGeometry(0.7, 32, 32);
const sphereMaterial = new THREE.MeshPhongMaterial({
color: 0xff0000,
specular: 0x444444,
shininess: 100
});
this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
this.sphere.position.x = -2;
this.sphere.castShadow = true;
this.scene.add(this.sphere);
// Add a torus
const torusGeometry = new THREE.TorusGeometry(0.6, 0.2, 16, 100);
const torusMaterial = new THREE.MeshPhongMaterial({
color: 0x0000ff,
specular: 0x444444,
shininess: 100
});
this.torus = new THREE.Mesh(torusGeometry, torusMaterial);
this.torus.position.x = 2;
this.torus.castShadow = true;
this.scene.add(this.torus);
// Add ground plane
const planeGeometry = new THREE.PlaneGeometry(20, 20);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0x808080,
roughness: 0.8,
metalness: 0.2
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -2;
plane.receiveShadow = true;
this.scene.add(plane);
}
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
// Rotate objects
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
this.sphere.rotation.y += 0.02;
this.torus.rotation.x += 0.01;
this.torus.rotation.y += 0.02;
this.renderer.render(this.scene, this.camera);
}
}
// 2. Text and Labels in 3D Scene
class Text3D {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.z = 10;
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
this.addLights();
this.createHelloWorldText();
this.animate();
}
addLights() {
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 0.8);
pointLight.position.set(10, 10, 10);
this.scene.add(pointLight);
}
createHelloWorldText() {
// Create text using sprite (simpler approach)
const loader = new THREE.FontLoader();
// Create text with canvas (fallback method)
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 512;
canvas.height = 128;
context.fillStyle = '#ffffff';
context.font = 'Bold 60px Arial';
context.textAlign = 'center';
context.fillText('Hello Three.js!', 256, 80);
const texture = new THREE.CanvasTexture(canvas);
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(8, 2, 1);
sprite.position.set(0, 2, 0);
this.scene.add(sprite);
// Add decorative objects around text
const geometry = new THREE.SphereGeometry(0.3, 32, 32);
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
for (let i = 0; i < 6; i++) {
const material = new THREE.MeshPhongMaterial({ color: colors[i] });
const sphere = new THREE.Mesh(geometry, material);
const angle = (i / 6) * Math.PI * 2;
sphere.position.set(
Math.cos(angle) * 4,
2,
Math.sin(angle) * 4
);
this.scene.add(sphere);
}
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.renderer.render(this.scene, this.camera);
}
}
// 3. Interactive Scene with Mouse Controls
class InteractiveScene {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.mouse = new THREE.Vector2();
this.raycaster = new THREE.Raycaster();
this.intersectedObject = null;
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x000033);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.z = 8;
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
this.addLights();
this.createInteractiveObjects();
this.addEventListeners();
this.animate();
}
addLights() {
const ambientLight = new THREE.AmbientLight(0x404040, 0.4);
this.scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1);
this.camera.add(pointLight);
this.scene.add(this.camera);
}
createInteractiveObjects() {
this.objects = [];
// Create multiple clickable objects
const geometries = [
new THREE.BoxGeometry(1, 1, 1),
new THREE.SphereGeometry(0.6, 32, 32),
new THREE.ConeGeometry(0.6, 1, 32),
new THREE.TorusGeometry(0.6, 0.2, 16, 100),
new THREE.DodecahedronGeometry(0.8),
new THREE.OctahedronGeometry(0.7)
];
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
for (let i = 0; i < 6; i++) {
const material = new THREE.MeshPhongMaterial({
color: colors[i],
specular: 0x444444,
shininess: 100
});
const mesh = new THREE.Mesh(geometries[i], material);
const angle = (i / 6) * Math.PI * 2;
mesh.position.set(
Math.cos(angle) * 3,
Math.sin(angle) * 0.5,
Math.sin(angle) * 3
);
mesh.userData = {
originalColor: colors[i],
originalScale: mesh.scale.x
};
this.objects.push(mesh);
this.scene.add(mesh);
}
}
addEventListeners() {
window.addEventListener('mousemove', (event) => {
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});
window.addEventListener('click', () => {
if (this.intersectedObject) {
// Spin the clicked object
this.tweens = this.tweens || [];
const tween = {
object: this.intersectedObject,
rotation: { x: 0, y: 0 },
target: { y: Math.PI * 2 },
duration: 1000,
startTime: Date.now()
};
this.tweens.push(tween);
}
});
}
animate() {
requestAnimationFrame(this.animate.bind(this));
// Update raycasting
this.raycaster.setFromCamera(this.mouse, this.camera);
const intersects = this.raycaster.intersectObjects(this.objects);
// Reset all objects
this.objects.forEach(obj => {
obj.material.color.setHex(obj.userData.originalColor);
obj.scale.setScalar(obj.userData.originalScale);
});
// Highlight intersected object
if (intersects.length > 0) {
this.intersectedObject = intersects[0].object;
this.intersectedObject.material.color.setHex(0xffffff);
this.intersectedObject.scale.setScalar(1.2);
} else {
this.intersectedObject = null;
}
// Update tweens
if (this.tweens) {
this.tweens = this.tweens.filter(tween => {
const elapsed = Date.now() - tween.startTime;
const progress = Math.min(elapsed / tween.duration, 1);
tween.object.rotation.y = tween.rotation.y + (tween.target.y - tween.rotation.y) * progress * 0.1;
return progress < 1;
});
}
// Rotate all objects slowly
this.objects.forEach((obj, index) => {
obj.rotation.x += 0.005;
obj.rotation.z += 0.003;
});
this.renderer.render(this.scene, this.camera);
}
}
// 4. Particle System
class ParticleSystem {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x000000);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.z = 50;
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
this.createParticles();
this.animate();
}
createParticles() {
const particleCount = 10000;
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const sizes = new Float32Array(particleCount);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// Position
positions[i3] = (Math.random() - 0.5) * 100;
positions[i3 + 1] = (Math.random() - 0.5) * 100;
positions[i3 + 2] = (Math.random() - 0.5) * 100;
// Color
const color = new THREE.Color();
color.setHSL(Math.random(), 0.7, 0.5);
colors[i3] = color.r;
colors[i3 + 1] = color.g;
colors[i3 + 2] = color.b;
// Size
sizes[i] = Math.random() * 2;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const material = new THREE.PointsMaterial({
size: 1,
vertexColors: true,
blending: THREE.AdditiveBlending,
transparent: true,
opacity: 0.8
});
this.particles = new THREE.Points(geometry, material);
this.scene.add(this.particles);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.particles.rotation.x += 0.001;
this.particles.rotation.y += 0.002;
const positions = this.particles.geometry.attributes.position.array;
const time = Date.now() * 0.001;
for (let i = 0; i < positions.length; i += 3) {
positions[i + 1] += Math.sin(time + positions[i]) * 0.01;
}
this.particles.geometry.attributes.position.needsUpdate = true;
this.renderer.render(this.scene, this.camera);
}
}
// 5. Wireframe Scene
class WireframeScene {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x111111);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.z = 5;
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
this.createWireframeObjects();
this.animate();
}
createWireframeObjects() {
// Create wireframe sphere
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: true
});
this.sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
this.sphere.position.x = -2;
this.scene.add(this.sphere);
// Create wireframe cube
const cubeGeometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
const cubeMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
});
this.cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
this.cube.position.x = 0;
this.scene.add(this.cube);
// Create wireframe torus
const torusGeometry = new THREE.TorusGeometry(1, 0.4, 16, 100);
const torusMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
wireframe: true
});
this.torus = new THREE.Mesh(torusGeometry, torusMaterial);
this.torus.position.x = 2;
this.scene.add(this.torus);
// Add grid helper
const gridHelper = new THREE.GridHelper(20, 20, 0x444444, 0x222222);
gridHelper.position.y = -2;
this.scene.add(gridHelper);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.sphere.rotation.y += 0.01;
this.sphere.rotation.z += 0.005;
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
this.torus.rotation.x += 0.005;
this.torus.rotation.y += 0.01;
this.renderer.render(this.scene, this.camera);
}
}
// Usage examples:
// const basicScene = new BasicScene('container1');
// const text3D = new Text3D('container2');
// const interactiveScene = new InteractiveScene('container3');
// const particleSystem = new ParticleSystem('container4');
// const wireframeScene = new WireframeScene('container5');
export {
BasicScene,
Text3D,
InteractiveScene,
ParticleSystem,
WireframeScene
};
💻 Matériaux et Éclairage javascript
🟡 intermediate
⭐⭐⭐
Matériaux avancés Three.js, configurations d'éclairage, ombres et rendu de surfaces réalistes
⏱️ 30 min
🏷️ threejs, materials, lighting, pbr
Prerequisites:
Three.js basics, 3D graphics concepts, Material properties
// Three.js Materials and Lighting Examples
import * as THREE from 'three';
// 1. Comprehensive Material Showcase
class MaterialShowcase {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x1a1a1a);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 5, 10);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.container.appendChild(this.renderer.domElement);
this.setupLighting();
this.createMaterialSamples();
this.createGround();
this.animate();
// Add orbit controls for better viewing
this.addOrbitControls();
}
setupLighting() {
// Ambient light for base illumination
this.ambientLight = new THREE.AmbientLight(0x404040, 0.3);
this.scene.add(this.ambientLight);
// Main directional light (sun)
this.directionalLight = new THREE.DirectionalLight(0xffffff, 1);
this.directionalLight.position.set(10, 10, 5);
this.directionalLight.castShadow = true;
this.directionalLight.shadow.mapSize.width = 2048;
this.directionalLight.shadow.mapSize.height = 2048;
this.directionalLight.shadow.camera.near = 0.5;
this.directionalLight.shadow.camera.far = 50;
this.scene.add(this.directionalLight);
// Fill light
this.fillLight = new THREE.DirectionalLight(0x87ceeb, 0.5);
this.fillLight.position.set(-5, 3, -5);
this.scene.add(this.fillLight);
// Rim light
this.rimLight = new THREE.DirectionalLight(0xff6b6b, 0.3);
this.rimLight.position.set(0, 5, -10);
this.scene.add(this.rimLight);
// Point light for dramatic lighting
this.pointLight = new THREE.PointLight(0xffaa00, 1, 20);
this.pointLight.position.set(0, 8, 0);
this.pointLight.castShadow = true;
this.scene.add(this.pointLight);
}
createMaterialSamples() {
this.materialObjects = [];
const geometry = new THREE.SphereGeometry(0.8, 64, 64);
// Basic Materials
const materials = [
// Basic Material
new THREE.MeshBasicMaterial({
color: 0xff0000,
name: 'Basic'
}),
// Lambert Material
new THREE.MeshLambertMaterial({
color: 0x00ff00,
name: 'Lambert'
}),
// Phong Material
new THREE.MeshPhongMaterial({
color: 0x0000ff,
specular: 0x444444,
shininess: 100,
name: 'Phong'
}),
// Standard Material (PBR)
new THREE.MeshStandardMaterial({
color: 0xffd700,
metalness: 0.5,
roughness: 0.5,
name: 'Standard PBR'
}),
// Physical Material (Advanced PBR)
new THREE.MeshPhysicalMaterial({
color: 0xff1493,
metalness: 0.7,
roughness: 0.3,
clearcoat: 1.0,
clearcoatRoughness: 0.0,
name: 'Physical PBR'
}),
// Toon Material
new THREE.MeshToonMaterial({
color: 0x00ff7f,
gradientMap: this.createGradientMap(),
name: 'Toon'
}),
// Normal Material (shows normals)
new THREE.MeshNormalMaterial({
name: 'Normal'
}),
// Depth Material
new THREE.MeshDepthMaterial({
name: 'Depth'
})
];
// Create spheres with different materials
materials.forEach((material, index) => {
const mesh = new THREE.Mesh(geometry, material);
const row = Math.floor(index / 4);
const col = index % 4;
mesh.position.set(
(col - 1.5) * 2.5,
2,
(row - 1) * 2.5
);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.userData = {
originalPosition: mesh.position.clone(),
material: material
};
this.materialObjects.push(mesh);
this.scene.add(mesh);
// Add label
this.addLabel(mesh.position, material.name);
});
}
createGradientMap() {
const canvas = document.createElement('canvas');
canvas.width = 256;
const context = canvas.getContext('2d');
const gradient = context.createLinearGradient(0, 0, 0, 256);
gradient.addColorStop(0, '#ffffff');
gradient.addColorStop(0.5, '#888888');
gradient.addColorStop(1, '#000000');
context.fillStyle = gradient;
context.fillRect(0, 0, 256, 256);
const texture = new THREE.CanvasTexture(canvas);
return texture;
}
addLabel(position, text) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 256;
canvas.height = 64;
context.fillStyle = '#ffffff';
context.font = '20px Arial';
context.textAlign = 'center';
context.fillText(text, 128, 40);
const texture = new THREE.CanvasTexture(canvas);
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.copy(position);
sprite.position.y -= 1.5;
sprite.scale.set(2, 0.5, 1);
this.scene.add(sprite);
}
createGround() {
const groundGeometry = new THREE.PlaneGeometry(30, 30);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x333333,
roughness: 0.8,
metalness: 0.2
});
this.ground = new THREE.Mesh(groundGeometry, groundMaterial);
this.ground.rotation.x = -Math.PI / 2;
this.ground.receiveShadow = true;
this.scene.add(this.ground);
}
addOrbitControls() {
// Simple mouse controls
this.mouseDown = false;
this.mouseX = 0;
this.mouseY = 0;
this.renderer.domElement.addEventListener('mousedown', (e) => {
this.mouseDown = true;
this.mouseX = e.clientX;
this.mouseY = e.clientY;
});
this.renderer.domElement.addEventListener('mouseup', () => {
this.mouseDown = false;
});
this.renderer.domElement.addEventListener('mousemove', (e) => {
if (!this.mouseDown) return;
const deltaX = e.clientX - this.mouseX;
const deltaY = e.clientY - this.mouseY;
this.camera.position.x = this.camera.position.x * Math.cos(deltaX * 0.01) -
this.camera.position.z * Math.sin(deltaX * 0.01);
this.camera.position.z = this.camera.position.x * Math.sin(deltaX * 0.01) +
this.camera.position.z * Math.cos(deltaX * 0.01);
this.camera.position.y += deltaY * 0.01;
this.camera.lookAt(0, 0, 0);
this.mouseX = e.clientX;
this.mouseY = e.clientY;
});
}
animate() {
requestAnimationFrame(this.animate.bind(this));
// Animate point light
const time = Date.now() * 0.001;
this.pointLight.position.x = Math.sin(time) * 8;
this.pointLight.position.z = Math.cos(time) * 8;
// Animate material objects
this.materialObjects.forEach((obj, index) => {
const offset = index * Math.PI / 4;
obj.position.y = obj.userData.originalPosition.y + Math.sin(time + offset) * 0.2;
obj.rotation.y += 0.01;
obj.rotation.x = Math.sin(time + offset) * 0.1;
});
this.camera.lookAt(0, 0, 0);
this.renderer.render(this.scene, this.camera);
}
}
// 2. Advanced Lighting Techniques
class LightingDemo {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x000000);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 8, 15);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.container.appendChild(this.renderer.domElement);
this.setupScene();
this.setupLightingTypes();
this.animate();
}
setupScene() {
// Create demo objects
const sphereGeometry = new THREE.SphereGeometry(1, 64, 64);
const boxGeometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100);
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0.7,
roughness: 0.3
});
this.demoObjects = [
new THREE.Mesh(sphereGeometry, material.clone()),
new THREE.Mesh(boxGeometry, material.clone()),
new THREE.Mesh(torusGeometry, material.clone())
];
this.demoObjects.forEach((obj, index) => {
const angle = (index / this.demoObjects.length) * Math.PI * 2;
obj.position.set(
Math.cos(angle) * 4,
0,
Math.sin(angle) * 4
);
obj.castShadow = true;
obj.receiveShadow = true;
this.scene.add(obj);
});
// Create ground
const groundGeometry = new THREE.PlaneGeometry(50, 50);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x222222,
roughness: 0.8
});
this.ground = new THREE.Mesh(groundGeometry, groundMaterial);
this.ground.rotation.x = -Math.PI / 2;
this.ground.receiveShadow = true;
this.scene.add(this.ground);
// Create light indicators (small spheres)
this.lightIndicators = [];
}
setupLightingTypes() {
// Ambient light
this.ambientLight = new THREE.AmbientLight(0x404040, 0.4);
this.scene.add(this.ambientLight);
this.addLightIndicator(this.ambientLight, 0x404040);
// Directional light with shadow
this.directionalLight = new THREE.DirectionalLight(0xffffff, 1);
this.directionalLight.position.set(10, 10, 5);
this.directionalLight.castShadow = true;
this.directionalLight.shadow.camera.left = -10;
this.directionalLight.shadow.camera.right = 10;
this.directionalLight.shadow.camera.top = 10;
this.directionalLight.shadow.camera.bottom = -10;
this.scene.add(this.directionalLight);
this.addLightIndicator(this.directionalLight, 0xffffff);
// Point light
this.pointLight1 = new THREE.PointLight(0xff0000, 1, 20);
this.pointLight1.position.set(-5, 5, 0);
this.pointLight1.castShadow = true;
this.scene.add(this.pointLight1);
this.addLightIndicator(this.pointLight1, 0xff0000);
this.pointLight2 = new THREE.PointLight(0x0000ff, 1, 20);
this.pointLight2.position.set(5, 5, 0);
this.pointLight2.castShadow = true;
this.scene.add(this.pointLight2);
this.addLightIndicator(this.pointLight2, 0x0000ff);
// Spot light
this.spotLight = new THREE.SpotLight(0x00ff00, 1);
this.spotLight.position.set(0, 8, 0);
this.spotLight.angle = Math.PI / 6;
this.spotLight.penumbra = 0.3;
this.spotLight.decay = 2;
this.spotLight.distance = 30;
this.spotLight.castShadow = true;
this.scene.add(this.spotLight);
// Add spot light helper
const spotLightHelper = new THREE.SpotLightHelper(this.spotLight);
this.scene.add(spotLightHelper);
// RectAreaLight (if supported)
if (THREE.RectAreaLight) {
this.areaLight = new THREE.RectAreaLight(0xffffff, 2, 10, 10);
this.areaLight.position.set(0, 10, 5);
this.areaLight.lookAt(0, 0, 0);
this.scene.add(this.areaLight);
}
// Hemisphere light
this.hemisphereLight = new THREE.HemisphereLight(0x87ceeb, 0x8b4513, 0.5);
this.scene.add(this.hemisphereLight);
}
addLightIndicator(light, color) {
const geometry = new THREE.SphereGeometry(0.2, 16, 16);
const material = new THREE.MeshBasicMaterial({ color: color });
const indicator = new THREE.Mesh(geometry, material);
if (light.position) {
indicator.position.copy(light.position);
}
this.lightIndicators.push({ indicator, light });
this.scene.add(indicator);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
const time = Date.now() * 0.001;
// Animate demo objects
this.demoObjects.forEach((obj, index) => {
obj.rotation.x += 0.01;
obj.rotation.y += 0.02;
obj.position.y = Math.sin(time + index) * 0.5;
});
// Animate point lights
this.pointLight1.position.x = Math.sin(time) * 7;
this.pointLight1.position.z = Math.cos(time) * 7;
this.pointLight2.position.x = Math.sin(time + Math.PI) * 7;
this.pointLight2.position.z = Math.cos(time + Math.PI) * 7;
// Animate spot light
this.spotLight.position.x = Math.sin(time * 0.5) * 5;
this.spotLight.position.z = Math.cos(time * 0.5) * 5;
this.spotLight.target.position.x = Math.sin(time * 0.3) * 3;
this.spotLight.target.position.z = Math.cos(time * 0.3) * 3;
// Update light indicators
this.lightIndicators.forEach(({ indicator, light }) => {
if (light.position) {
indicator.position.copy(light.position);
}
});
// Animate camera
this.camera.position.x = Math.sin(time * 0.1) * 15;
this.camera.position.z = Math.cos(time * 0.1) * 15;
this.camera.lookAt(0, 0, 0);
this.renderer.render(this.scene, this.camera);
}
}
// 3. Texture and Material Mapping
class TextureMaterials {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x222222);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 2, 8);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
this.setupLighting();
this.createTexturedMaterials();
this.animate();
}
setupLighting() {
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 5);
this.scene.add(directionalLight);
}
createTexturedMaterials() {
// Create procedural textures
const textures = this.createProceduralTextures();
const geometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
// Different material types with textures
const materials = [
// Basic material with color
new THREE.MeshBasicMaterial({
map: textures.checkerboard,
name: 'Basic + Texture'
}),
// Lambert material
new THREE.MeshLambertMaterial({
map: textures.noise,
name: 'Lambert + Texture'
}),
// Phong material with specular map
new THREE.MeshPhongMaterial({
map: textures.wood,
specularMap: textures.checkerboard,
shininess: 100,
name: 'Phong + Maps'
}),
// Standard material with PBR textures
new THREE.MeshStandardMaterial({
map: textures.metal,
metalnessMap: textures.checkerboard,
roughnessMap: textures.noise,
normalMap: textures.normal,
name: 'PBR + Maps'
}),
// Material with emissive map
new THREE.MeshStandardMaterial({
map: textures.noise,
emissiveMap: textures.checkerboard,
emissive: new THREE.Color(0x444444),
name: 'Emissive Map'
}),
// Material with alpha map
new THREE.MeshStandardMaterial({
map: textures.checkerboard,
alphaMap: textures.alpha,
transparent: true,
name: 'Alpha Map'
})
];
materials.forEach((material, index) => {
const mesh = new THREE.Mesh(geometry.clone(), material);
const angle = (index / materials.length) * Math.PI * 2;
mesh.position.set(
Math.cos(angle) * 4,
0,
Math.sin(angle) * 4
);
this.scene.add(mesh);
// Add label
this.addLabel(mesh.position, material.name);
});
}
createProceduralTextures() {
const textures = {};
// Checkerboard texture
const checkerSize = 64;
const checkerCanvas = document.createElement('canvas');
checkerCanvas.width = checkerSize;
checkerCanvas.height = checkerSize;
const checkerContext = checkerCanvas.getContext('2d');
const tileSize = checkerSize / 8;
for (let i = 0; i < 8; i++) {
for (let j = 0; j < 8; j++) {
checkerContext.fillStyle = (i + j) % 2 === 0 ? '#ffffff' : '#000000';
checkerContext.fillRect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
textures.checkerboard = new THREE.CanvasTexture(checkerCanvas);
// Noise texture
const noiseSize = 256;
const noiseCanvas = document.createElement('canvas');
noiseCanvas.width = noiseSize;
noiseCanvas.height = noiseSize;
const noiseContext = noiseCanvas.getContext('2d');
for (let i = 0; i < noiseSize; i++) {
for (let j = 0; j < noiseSize; j++) {
const value = Math.random() * 255;
noiseContext.fillStyle = `rgb(${value}, ${value}, ${value})`;
noiseContext.fillRect(i, j, 1, 1);
}
}
textures.noise = new THREE.CanvasTexture(noiseCanvas);
// Wood texture
const woodCanvas = document.createElement('canvas');
woodCanvas.width = 256;
woodCanvas.height = 256;
const woodContext = woodCanvas.getContext('2d');
for (let y = 0; y < 256; y++) {
const darkness = 50 + Math.sin(y * 0.05) * 50;
woodContext.fillStyle = `rgb(${darkness}, ${darkness * 0.5}, 0)`;
woodContext.fillRect(0, y, 256, 1);
}
textures.wood = new THREE.CanvasTexture(woodCanvas);
// Metal texture
const metalCanvas = document.createElement('canvas');
metalCanvas.width = 256;
metalCanvas.height = 256;
const metalContext = metalCanvas.getContext('2d');
for (let y = 0; y < 256; y++) {
const brightness = 180 + Math.sin(y * 0.1) * 20 + Math.random() * 10;
metalContext.fillStyle = `rgb(${brightness}, ${brightness}, ${brightness})`;
metalContext.fillRect(0, y, 256, 1);
}
textures.metal = new THREE.CanvasTexture(metalCanvas);
// Normal map (simplified)
const normalCanvas = document.createElement('canvas');
normalCanvas.width = 256;
normalCanvas.height = 256;
const normalContext = normalCanvas.getContext('2d');
for (let i = 0; i < 256; i++) {
for (let j = 0; j < 256; j++) {
const x = (Math.sin(i * 0.05) * 127 + 128);
const y = (Math.cos(j * 0.05) * 127 + 128);
const z = 255;
normalContext.fillStyle = `rgb(${x}, ${y}, ${z})`;
normalContext.fillRect(i, j, 1, 1);
}
}
textures.normal = new THREE.CanvasTexture(normalCanvas);
// Alpha map
const alphaCanvas = document.createElement('canvas');
alphaCanvas.width = 256;
alphaCanvas.height = 256;
const alphaContext = alphaCanvas.getContext('2d');
for (let i = 0; i < 256; i++) {
for (let j = 0; j < 256; j++) {
const value = Math.random() > 0.5 ? 255 : 0;
alphaContext.fillStyle = `rgb(${value}, ${value}, ${value})`;
alphaContext.fillRect(i, j, 1, 1);
}
}
textures.alpha = new THREE.CanvasTexture(alphaCanvas);
return textures;
}
addLabel(position, text) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 256;
canvas.height = 64;
context.fillStyle = '#ffffff';
context.font = '16px Arial';
context.textAlign = 'center';
context.fillText(text, 128, 32);
const texture = new THREE.CanvasTexture(canvas);
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.copy(position);
sprite.position.y -= 2;
sprite.scale.set(3, 0.8, 1);
this.scene.add(sprite);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
// Rotate all objects
this.scene.children.forEach(child => {
if (child.isMesh) {
child.rotation.y += 0.01;
}
});
this.camera.lookAt(0, 0, 0);
this.renderer.render(this.scene, this.camera);
}
}
export {
MaterialShowcase,
LightingDemo,
TextureMaterials
};
💻 Animations et Contrôles javascript
🔴 complex
⭐⭐⭐⭐
Animations 3D, contrôles de caméra, interactions utilisateur et systèmes d'animation dans Three.js
⏱️ 45 min
🏷️ threejs, animation, controls, interaction
Prerequisites:
Three.js basics, JavaScript ES6+, Animation principles
// Three.js Animations and Controls Examples
import * as THREE from 'three';
// 1. Advanced Animation System
class AnimationSystem {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.animations = [];
this.mixer = null;
this.clock = new THREE.Clock();
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x0a0a0a);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 5, 10);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.shadowMap.enabled = true;
this.container.appendChild(this.renderer.domElement);
this.setupLighting();
this.createAnimatedObjects();
this.setupControls();
this.createAnimationUI();
this.animate();
}
setupLighting() {
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 5);
directionalLight.castShadow = true;
this.scene.add(directionalLight);
const pointLight = new THREE.PointLight(0xff6b6b, 1, 20);
pointLight.position.set(-5, 5, -5);
this.scene.add(pointLight);
}
createAnimatedObjects() {
this.animatableObjects = [];
// Create multiple animated objects
this.createPulsatingSphere();
this.createRotatingCube();
this.createBouncingBall();
this.createWavingPlane();
this.createOrbitalSystem();
this.createCharacterAnimation();
this.createParticleSystem();
this.create MorphingShapes();
}
createPulsatingSphere() {
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
emissive: 0x440000,
shininess: 100
});
const sphere = new THREE.Mesh(geometry, material);
sphere.position.set(-6, 2, 0);
sphere.castShadow = true;
// Add pulsating animation
const animation = {
object: sphere,
type: 'pulsate',
baseScale: 1,
amplitude: 0.3,
frequency: 2,
phase: 0
};
this.animatableObjects.push(sphere);
this.animations.push(animation);
this.scene.add(sphere);
}
createRotatingCube() {
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshPhongMaterial({
color: 0x00ff00,
specular: 0x444444,
shininess: 100
});
const cube = new THREE.Mesh(geometry, material);
cube.position.set(-2, 2, 0);
cube.castShadow = true;
// Add complex rotation animation
const animation = {
object: cube,
type: 'complex-rotation',
rotationSpeed: { x: 0.01, y: 0.02, z: 0.015 },
oscillation: { axis: 'x', amplitude: 0.5, frequency: 1 }
};
this.animatableObjects.push(cube);
this.animations.push(animation);
this.scene.add(cube);
}
createBouncingBall() {
const geometry = new THREE.SphereGeometry(0.8, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: 0x0000ff,
emissive: 0x000044,
shininess: 100
});
const ball = new THREE.Mesh(geometry, material);
ball.position.set(2, 3, 0);
ball.castShadow = true;
// Add bouncing animation
const animation = {
object: ball,
type: 'bounce',
baseY: 3,
amplitude: 2,
frequency: 2,
gravity: 0.01,
squash: 0.8
};
this.animatableObjects.push(ball);
this.animations.push(animation);
this.scene.add(ball);
}
createWavingPlane() {
const geometry = new THREE.PlaneGeometry(4, 4, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: 0xffff00,
side: THREE.DoubleSide,
wireframe: false
});
const plane = new THREE.Mesh(geometry, material);
plane.position.set(6, 0, 0);
plane.rotation.x = -Math.PI / 2;
plane.castShadow = true;
// Add waving animation
const animation = {
object: plane,
type: 'wave',
amplitude: 0.5,
frequency: 2,
waveSpeed: 0.02,
geometry: geometry
};
this.animatableObjects.push(plane);
this.animations.push(animation);
this.scene.add(plane);
}
createOrbitalSystem() {
// Central sun
const sunGeometry = new THREE.SphereGeometry(1.5, 32, 32);
const sunMaterial = new THREE.MeshBasicMaterial({
color: 0xffa500,
emissive: 0xffa500
});
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
sun.position.set(0, -2, 0);
// Planets
const planets = [];
const planetData = [
{ radius: 0.3, distance: 3, speed: 1, color: 0x8b4513 }, // Mercury
{ radius: 0.5, distance: 4, speed: 0.8, color: 0xffa500 }, // Venus
{ radius: 0.5, distance: 5.5, speed: 0.6, color: 0x0000ff }, // Earth
{ radius: 0.4, distance: 7, speed: 0.5, color: 0xff0000 } // Mars
];
planetData.forEach((data, index) => {
const planetGeometry = new THREE.SphereGeometry(data.radius, 16, 16);
const planetMaterial = new THREE.MeshPhongMaterial({
color: data.color,
shininess: 50
});
const planet = new THREE.Mesh(planetGeometry, planetMaterial);
planets.push({ mesh: planet, ...data });
// Create orbit line
const orbitGeometry = new THREE.RingGeometry(data.distance - 0.1, data.distance + 0.1, 64);
const orbitMaterial = new THREE.MeshBasicMaterial({
color: 0x444444,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.3
});
const orbit = new THREE.Mesh(orbitGeometry, orbitMaterial);
orbit.rotation.x = -Math.PI / 2;
orbit.position.set(0, -2, 0);
this.scene.add(orbit);
});
// Add orbital animation
const animation = {
type: 'orbital',
sun: sun,
planets: planets,
center: new THREE.Vector3(0, -2, 0)
};
this.animations.push(animation);
this.scene.add(sun);
planets.forEach(planet => this.scene.add(planet.mesh));
}
createCharacterAnimation() {
// Simple character made of multiple parts
const character = new THREE.Group();
// Body
const bodyGeometry = new THREE.BoxGeometry(1, 2, 0.5);
const bodyMaterial = new THREE.MeshPhongMaterial({ color: 0x888888 });
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
character.add(body);
// Head
const headGeometry = new THREE.SphereGeometry(0.5, 16, 16);
const headMaterial = new THREE.MeshPhongMaterial({ color: 0xffdbac });
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.y = 1.5;
character.add(head);
// Arms
const armGeometry = new THREE.BoxGeometry(0.2, 1.5, 0.2);
const armMaterial = new THREE.MeshPhongMaterial({ color: 0x888888 });
const leftArm = new THREE.Mesh(armGeometry, armMaterial);
leftArm.position.set(-0.7, 0.5, 0);
character.add(leftArm);
const rightArm = new THREE.Mesh(armGeometry, armMaterial);
rightArm.position.set(0.7, 0.5, 0);
character.add(rightArm);
// Legs
const legGeometry = new THREE.BoxGeometry(0.3, 1.5, 0.3);
const legMaterial = new THREE.MeshPhongMaterial({ color: 0x4444ff });
const leftLeg = new THREE.Mesh(legGeometry, legMaterial);
leftLeg.position.set(-0.3, -1.5, 0);
character.add(leftLeg);
const rightLeg = new THREE.Mesh(legGeometry, legMaterial);
rightLeg.position.set(0.3, -1.5, 0);
character.add(rightLeg);
character.position.set(0, 0, 4);
// Add walking animation
const animation = {
type: 'character-walk',
character: character,
parts: { head, leftArm, rightArm, leftLeg, rightLeg },
walkSpeed: 2,
bobAmount: 0.1,
armSwingAmount: 0.5,
legSwingAmount: 0.5
};
this.animations.push(animation);
this.scene.add(character);
}
createParticleSystem() {
const particleCount = 1000;
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const velocities = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// Initial positions (fountain effect)
positions[i3] = 0;
positions[i3 + 1] = -2;
positions[i3 + 2] = 0;
// Random colors
const color = new THREE.Color();
color.setHSL(Math.random(), 0.7, 0.5);
colors[i3] = color.r;
colors[i3 + 1] = color.g;
colors[i3 + 2] = color.b;
// Initial velocities
velocities[i3] = (Math.random() - 0.5) * 2;
velocities[i3 + 1] = Math.random() * 5 + 2;
velocities[i3 + 2] = (Math.random() - 0.5) * 2;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));
const material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
blending: THREE.AdditiveBlending,
transparent: true,
opacity: 0.8
});
const particles = new THREE.Points(geometry, material);
particles.position.set(0, -2, 0);
const animation = {
type: 'particle-fountain',
particles: particles,
geometry: geometry,
gravity: -0.02,
life: 3,
spawnRate: 50
};
this.animations.push(animation);
this.scene.add(particles);
}
createMorphingShapes() {
// Create geometry that can morph between different shapes
const baseGeometry = new THREE.IcosahedronGeometry(1, 2);
const material = new THREE.MeshPhongMaterial({
color: 0xff00ff,
wireframe: false,
flatShading: true
});
const morphMesh = new THREE.Mesh(baseGeometry, material);
morphMesh.position.set(-3, -2, 0);
// Create different shape targets
const sphereGeometry = new THREE.SphereGeometry(1, 16, 16);
const boxGeometry = new THREE.BoxGeometry(1.5, 1.5, 1.5);
const torusGeometry = new THREE.TorusGeometry(1, 0.3, 16, 100);
const animation = {
type: 'morph-shapes',
mesh: morphMesh,
shapes: [baseGeometry, sphereGeometry, boxGeometry, torusGeometry],
currentIndex: 0,
targetIndex: 1,
morphSpeed: 0.02,
morphProgress: 0
};
this.animations.push(animation);
this.scene.add(morphMesh);
}
setupControls() {
this.mouse = new THREE.Vector2();
this.raycaster = new THREE.Raycaster();
// Mouse controls for camera
this.renderer.domElement.addEventListener('mousemove', (event) => {
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Update camera position based on mouse
this.camera.position.x = this.mouse.x * 5;
this.camera.position.y = 5 + this.mouse.y * 2;
this.camera.lookAt(0, 0, 0);
});
// Click to trigger animations
this.renderer.domElement.addEventListener('click', () => {
this.triggerRandomAnimation();
});
}
createAnimationUI() {
// Create simple UI overlay
const ui = document.createElement('div');
ui.style.position = 'absolute';
ui.style.top = '10px';
ui.style.left = '10px';
ui.style.color = 'white';
ui.style.fontFamily = 'Arial, sans-serif';
ui.style.fontSize = '14px';
ui.style.backgroundColor = 'rgba(0,0,0,0.7)';
ui.style.padding = '10px';
ui.style.borderRadius = '5px';
ui.innerHTML = `
<h3>Animation Controls</h3>
<p>Move mouse to control camera</p>
<p>Click to trigger effects</p>
<div id="animation-info">
<p>Active animations: 0</p>
</div>
`;
this.container.appendChild(ui);
this.uiInfo = document.getElementById('animation-info');
}
triggerRandomAnimation() {
// Create explosion effect at random position
const position = new THREE.Vector3(
(Math.random() - 0.5) * 10,
Math.random() * 5,
(Math.random() - 0.5) * 10
);
this.createExplosion(position);
}
createExplosion(position) {
const particleCount = 100;
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
const velocities = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
positions[i3] = position.x;
positions[i3 + 1] = position.y;
positions[i3 + 2] = position.z;
const color = new THREE.Color();
color.setHSL(Math.random() * 0.1, 1, 0.5); // Red-yellow explosion
colors[i3] = color.r;
colors[i3 + 1] = color.g;
colors[i3 + 2] = color.b;
const speed = Math.random() * 5 + 2;
const theta = Math.random() * Math.PI * 2;
const phi = Math.random() * Math.PI;
velocities[i3] = speed * Math.sin(phi) * Math.cos(theta);
velocities[i3 + 1] = speed * Math.cos(phi);
velocities[i3 + 2] = speed * Math.sin(phi) * Math.sin(theta);
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));
const material = new THREE.PointsMaterial({
size: 0.2,
vertexColors: true,
blending: THREE.AdditiveBlending,
transparent: true,
opacity: 1
});
const particles = new THREE.Points(geometry, material);
const animation = {
type: 'explosion',
particles: particles,
geometry: geometry,
life: 2,
maxLife: 2
};
this.animations.push(animation);
this.scene.add(particles);
}
updateAnimations(deltaTime) {
this.animations.forEach((animation, index) => {
const time = this.clock.getElapsedTime();
switch (animation.type) {
case 'pulsate':
const scale = animation.baseScale +
Math.sin(time * animation.frequency * Math.PI * 2 + animation.phase) *
animation.amplitude;
animation.object.scale.setScalar(scale);
break;
case 'complex-rotation':
animation.object.rotation.x += animation.rotationSpeed.x;
animation.object.rotation.y += animation.rotationSpeed.y;
animation.object.rotation.z += animation.rotationSpeed.z;
if (animation.oscillation) {
const oscillationValue = Math.sin(time * animation.oscillation.frequency) *
animation.oscillation.amplitude;
animation.object.position[animation.oscillation.axis] = oscillationValue;
}
break;
case 'bounce':
const bounceHeight = Math.abs(Math.sin(time * animation.frequency)) *
animation.amplitude;
animation.object.position.y = animation.baseY + bounceHeight;
// Squash and stretch
const squashFactor = 1 - (bounceHeight / animation.amplitude) *
(1 - animation.squash);
animation.object.scale.y = squashFactor;
animation.object.scale.x = animation.object.scale.z = 1 / squashFactor;
break;
case 'wave':
const positions = animation.geometry.attributes.position.array;
for (let i = 0; i < positions.length; i += 3) {
const x = positions[i];
const y = positions[i + 1];
positions[i + 2] = Math.sin(x * 0.5 + time * animation.waveSpeed * 10) *
animation.amplitude;
}
animation.geometry.attributes.position.needsUpdate = true;
break;
case 'orbital':
animation.planets.forEach((planet) => {
const angle = time * planet.speed;
planet.mesh.position.x = animation.center.x +
Math.cos(angle) * planet.distance;
planet.mesh.position.z = animation.center.z +
Math.sin(angle) * planet.distance;
planet.mesh.rotation.y += 0.02;
});
animation.sun.rotation.y += 0.005;
break;
case 'character-walk':
const walkCycle = Math.sin(time * animation.walkSpeed);
// Body bob
animation.character.position.y = walkCycle * animation.bobAmount;
// Arm swing
animation.parts.leftArm.rotation.z = walkCycle * animation.armSwingAmount;
animation.parts.rightArm.rotation.z = -walkCycle * animation.armSwingAmount;
// Leg swing
animation.parts.leftLeg.rotation.x = -walkCycle * animation.legSwingAmount;
animation.parts.rightLeg.rotation.x = walkCycle * animation.legSwingAmount;
// Character movement
animation.character.position.x = Math.sin(time * 0.5) * 2;
animation.character.rotation.y = -Math.cos(time * 0.5) > 0 ? 0 : Math.PI;
break;
case 'particle-fountain':
const particlePositions = animation.geometry.attributes.position.array;
const particleVelocities = animation.geometry.attributes.velocity.array;
for (let i = 0; i < particlePositions.length; i += 3) {
// Update positions
particlePositions[i] += particleVelocities[i] * deltaTime;
particlePositions[i + 1] += particleVelocities[i + 1] * deltaTime;
particlePositions[i + 2] += particleVelocities[i + 2] * deltaTime;
// Apply gravity
particleVelocities[i + 1] += animation.gravity;
// Reset particles that fall below ground
if (particlePositions[i + 1] < -2) {
particlePositions[i] = 0;
particlePositions[i + 1] = -2;
particlePositions[i + 2] = 0;
particleVelocities[i] = (Math.random() - 0.5) * 2;
particleVelocities[i + 1] = Math.random() * 5 + 2;
particleVelocities[i + 2] = (Math.random() - 0.5) * 2;
}
}
animation.geometry.attributes.position.needsUpdate = true;
break;
case 'morph-shapes':
animation.morphProgress += animation.morphSpeed;
if (animation.morphProgress >= 1) {
animation.morphProgress = 0;
animation.currentIndex = animation.targetIndex;
animation.targetIndex = (animation.targetIndex + 1) % animation.shapes.length;
}
// Simple morphing by lerping vertices (simplified version)
animation.mesh.scale.x = 1 + Math.sin(animation.morphProgress * Math.PI) * 0.2;
animation.mesh.scale.y = 1 + Math.cos(animation.morphProgress * Math.PI) * 0.2;
break;
case 'explosion':
animation.life -= deltaTime;
const explosionPositions = animation.geometry.attributes.position.array;
const explosionVelocities = animation.geometry.attributes.velocity.array;
for (let i = 0; i < explosionPositions.length; i += 3) {
explosionPositions[i] += explosionVelocities[i] * deltaTime;
explosionPositions[i + 1] += explosionVelocities[i + 1] * deltaTime;
explosionPositions[i + 2] += explosionVelocities[i + 2] * deltaTime;
// Apply drag
explosionVelocities[i] *= 0.98;
explosionVelocities[i + 1] *= 0.98;
explosionVelocities[i + 2] *= 0.98;
}
animation.geometry.attributes.position.needsUpdate = true;
animation.particles.material.opacity = animation.life / animation.maxLife;
if (animation.life <= 0) {
this.scene.remove(animation.particles);
this.animations.splice(index, 1);
}
break;
}
});
// Update UI
if (this.uiInfo) {
this.uiInfo.innerHTML = `<p>Active animations: ${this.animations.length}</p>`;
}
}
animate() {
requestAnimationFrame(this.animate.bind(this));
const deltaTime = this.clock.getDelta();
this.updateAnimations(deltaTime);
this.renderer.render(this.scene, this.camera);
}
}
// 2. Camera Control System
class CameraControls {
constructor(containerId) {
this.container = document.getElementById(containerId);
this.init();
}
init() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x87CEEB);
this.camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(0, 5, 15);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.container.appendChild(this.renderer.domElement);
this.setupScene();
this.setupCameraControls();
this.createControlUI();
this.animate();
}
setupScene() {
// Add lights
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 10, 5);
this.scene.add(directionalLight);
// Create reference objects
this.createReferenceObjects();
}
createReferenceObjects() {
// Create grid of objects
const geometries = [
new THREE.BoxGeometry(1, 1, 1),
new THREE.SphereGeometry(0.6, 32, 32),
new THREE.ConeGeometry(0.6, 1, 32),
new THREE.TorusGeometry(0.6, 0.2, 16, 100)
];
const colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 4; j++) {
const geometry = geometries[j];
const material = new THREE.MeshPhongMaterial({
color: colors[j],
shininess: 100
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(
(i - 1.5) * 3,
0,
(j - 1.5) * 3
);
this.scene.add(mesh);
}
}
// Add ground plane
const planeGeometry = new THREE.PlaneGeometry(20, 20);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0x808080,
roughness: 0.8
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -2;
this.scene.add(plane);
// Add coordinate axes helper
this.axesHelper = new THREE.AxesHelper(5);
this.scene.add(this.axesHelper);
}
setupCameraControls() {
this.controls = {
mode: 'orbit',
speed: 5,
zoom: 1
};
this.keys = {};
this.mouse = { x: 0, y: 0, down: false, startX: 0, startY: 0 };
// Keyboard controls
window.addEventListener('keydown', (e) => {
this.keys[e.key.toLowerCase()] = true;
// Switch control modes
if (e.key === '1') this.controls.mode = 'orbit';
if (e.key === '2') this.controls.mode = 'fly';
if (e.key === '3') this.controls.mode = 'fps';
});
window.addEventListener('keyup', (e) => {
this.keys[e.key.toLowerCase()] = false;
});
// Mouse controls
this.renderer.domElement.addEventListener('mousedown', (e) => {
this.mouse.down = true;
this.mouse.startX = e.clientX;
this.mouse.startY = e.clientY;
});
window.addEventListener('mouseup', () => {
this.mouse.down = false;
});
window.addEventListener('mousemove', (e) => {
if (this.mouse.down) {
const deltaX = e.clientX - this.mouse.startX;
const deltaY = e.clientY - this.mouse.startY;
this.handleMouseMovement(deltaX, deltaY);
this.mouse.startX = e.clientX;
this.mouse.startY = e.clientY;
}
});
// Wheel for zoom
this.renderer.domElement.addEventListener('wheel', (e) => {
e.preventDefault();
this.controls.zoom *= e.deltaY > 0 ? 1.1 : 0.9;
this.controls.zoom = Math.max(0.1, Math.min(10, this.controls.zoom));
});
}
handleMouseMovement(deltaX, deltaY) {
switch (this.controls.mode) {
case 'orbit':
// Orbit around origin
const spherical = new THREE.Spherical();
spherical.setFromVector3(this.camera.position);
spherical.theta -= deltaX * 0.01;
spherical.phi += deltaY * 0.01;
spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi));
spherical.radius *= this.controls.zoom;
this.camera.position.setFromSpherical(spherical);
this.camera.lookAt(0, 0, 0);
break;
case 'fly':
// Look around
this.camera.rotation.y -= deltaX * 0.005;
this.camera.rotation.x -= deltaY * 0.005;
this.camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, this.camera.rotation.x));
break;
case 'fps':
// FPS-style mouse look
this.camera.rotation.y -= deltaX * 0.002;
this.camera.rotation.x -= deltaY * 0.002;
this.camera.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, this.camera.rotation.x));
break;
}
}
updateCameraFromKeyboard() {
const speed = this.controls.speed * 0.1;
const forward = new THREE.Vector3(0, 0, -1);
const right = new THREE.Vector3(1, 0, 0);
switch (this.controls.mode) {
case 'fly':
case 'fps':
forward.applyQuaternion(this.camera.quaternion);
right.applyQuaternion(this.camera.quaternion);
if (this.keys['w']) {
this.camera.position.addScaledVector(forward, speed);
}
if (this.keys['s']) {
this.camera.position.addScaledVector(forward, -speed);
}
if (this.keys['a']) {
this.camera.position.addScaledVector(right, -speed);
}
if (this.keys['d']) {
this.camera.position.addScaledVector(right, speed);
}
if (this.keys[' ']) { // Space
this.camera.position.y += speed;
}
if (this.keys['shift']) {
this.camera.position.y -= speed;
}
break;
case 'orbit':
// Zoom in/out with keys
if (this.keys['w'] || this.keys['+']) {
this.controls.zoom *= 0.95;
}
if (this.keys['s'] || this.keys['-']) {
this.controls.zoom *= 1.05;
}
this.controls.zoom = Math.max(0.1, Math.min(10, this.controls.zoom));
// Update camera position
const spherical = new THREE.Spherical();
spherical.setFromVector3(this.camera.position);
spherical.radius *= this.controls.zoom;
this.camera.position.setFromSpherical(spherical);
this.camera.lookAt(0, 0, 0);
this.controls.zoom = 1; // Reset zoom after applying
break;
}
}
createControlUI() {
const ui = document.createElement('div');
ui.style.position = 'absolute';
ui.style.top = '10px';
ui.style.right = '10px';
ui.style.color = 'white';
ui.style.fontFamily = 'Arial, sans-serif';
ui.style.fontSize = '14px';
ui.style.backgroundColor = 'rgba(0,0,0,0.7)';
ui.style.padding = '10px';
ui.style.borderRadius = '5px';
ui.innerHTML = `
<h3>Camera Controls</h3>
<p><strong>1:</strong> Orbit Mode</p>
<p><strong>2:</strong> Fly Mode</p>
<p><strong>3:</strong> FPS Mode</p>
<p><strong>W/A/S/D:</strong> Move</p>
<p><strong>Space/Shift:</strong> Up/Down</p>
<p><strong>Mouse:</strong> Look</p>
<p><strong>Scroll:</strong> Zoom</p>
<div id="mode-display" style="margin-top: 10px; font-weight: bold;">
Current Mode: Orbit
</div>
`;
this.container.appendChild(ui);
this.modeDisplay = document.getElementById('mode-display');
// Update mode display
setInterval(() => {
const modeNames = { orbit: 'Orbit', fly: 'Fly', fps: 'FPS' };
this.modeDisplay.textContent = `Current Mode: ${modeNames[this.controls.mode]}`;
}, 100);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
this.updateCameraFromKeyboard();
// Rotate reference objects
this.scene.children.forEach(child => {
if (child.isMesh && child.position.y === 0) {
child.rotation.x += 0.01;
child.rotation.y += 0.02;
}
});
this.renderer.render(this.scene, this.camera);
}
}
export {
AnimationSystem,
CameraControls
};