🎯 empfohlene Sammlungen
Balanced sample collections from various categories for you to explore
Three.js Beispiele
Three.js 3D-Grafikbibliotheksbeispiele einschließlich Szenen, Meshes, Beleuchtung, Materialien, Animationen und interaktive 3D-Inhalte
💻 Three.js Hello World javascript
🟢 simple
⭐
Grundlegende Three.js-Einrichtung mit Szenen, Kameras, Renderern und einfachen 3D-Objekten
⏱️ 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
};
💻 Materialien und Beleuchtung javascript
🟡 intermediate
⭐⭐⭐
Erweiterte Three.js-Materialien, Beleuchtungseinrichtungen, Schatten und realistisches Oberflächenrendering
⏱️ 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
};
💻 Animationen und Steuerelemente javascript
🔴 complex
⭐⭐⭐⭐
3D-Animationen, Kamerasteuerungen, Benutzerinteraktionen und Animationssysteme in 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
};