🎲 Three.js — 3D Mängud Brauseris
WebGL, 3D stseenid, valgustus, materjalid, kaamera, füüsika — kogu 3D maailm su brauseris!
⚡ Samm 1 — Three.js Põhialused
npm create vite@latest my-3d-game -- --template vanilla-ts
cd my-3d-game
npm install three @types/three
npm install -D vite
npm run dev
import * as THREE from 'three';
// 1. Stseen — konteiner kõigele
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a2e);
scene.fog = new THREE.Fog(0x0a0a2e, 10, 50);
// 2. Kaamera — vaade maailma
const camera = new THREE.PerspectiveCamera(
75, // FOV (vaateväli)
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near plane
1000 // Far plane
);
camera.position.set(0, 5, 10);
camera.lookAt(0, 0, 0);
// 3. Renderer — joonistab stseeni
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// 4. Valgus
const ambientLight = new THREE.AmbientLight(0x404080, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
scene.add(directionalLight);
// 5. Põrand
const floorGeometry = new THREE.PlaneGeometry(50, 50);
const floorMaterial = new THREE.MeshStandardMaterial({
color: 0x1a1a3e,
roughness: 0.8,
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
// 6. Kuubik (mängija)
const playerGeometry = new THREE.BoxGeometry(1, 1, 1);
const playerMaterial = new THREE.MeshStandardMaterial({
color: 0x22c55e,
metalness: 0.3,
roughness: 0.4,
});
const player = new THREE.Mesh(playerGeometry, playerMaterial);
player.position.y = 0.5;
player.castShadow = true;
scene.add(player);
// 7. Mängutsükkel
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
// Mängija rotatsioon
player.rotation.y += delta;
renderer.render(scene, camera);
}
animate();
// Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
🏃 Samm 2 — Third-Person Kontroller
class PlayerController {
private velocity = new THREE.Vector3();
private direction = new THREE.Vector3();
private speed = 8;
private keys: Record<string, boolean> = {};
constructor(
private mesh: THREE.Mesh,
private camera: THREE.PerspectiveCamera
) {
window.addEventListener('keydown', (e) => this.keys[e.key.toLowerCase()] = true);
window.addEventListener('keyup', (e) => this.keys[e.key.toLowerCase()] = false);
}
update(delta: number): void {
this.direction.set(0, 0, 0);
if (this.keys['w']) this.direction.z -= 1;
if (this.keys['s']) this.direction.z += 1;
if (this.keys['a']) this.direction.x -= 1;
if (this.keys['d']) this.direction.x += 1;
if (this.direction.length() > 0) {
this.direction.normalize();
// Liikumine kaamera suunas
const cameraDirection = new THREE.Vector3();
this.camera.getWorldDirection(cameraDirection);
cameraDirection.y = 0;
cameraDirection.normalize();
const sideDirection = new THREE.Vector3();
sideDirection.crossVectors(this.camera.up, cameraDirection).normalize();
const moveDirection = new THREE.Vector3();
moveDirection.addScaledVector(cameraDirection, -this.direction.z);
moveDirection.addScaledVector(sideDirection, -this.direction.x);
moveDirection.normalize();
this.mesh.position.addScaledVector(moveDirection, this.speed * delta);
// Pööra mängija liikumise suunas
const angle = Math.atan2(moveDirection.x, moveDirection.z);
this.mesh.rotation.y = THREE.MathUtils.lerp(
this.mesh.rotation.y, angle, 0.15
);
}
// Kaamera jälgib mängijat
const cameraOffset = new THREE.Vector3(0, 5, 10);
const targetPos = this.mesh.position.clone().add(cameraOffset);
this.camera.position.lerp(targetPos, 0.05);
this.camera.lookAt(this.mesh.position);
}
}
💡 Samm 3 — Materjalid ja Valgustus
// Erinevad materjali tüübid
const materials = {
// Metall
metal: new THREE.MeshStandardMaterial({
color: 0x888888,
metalness: 1.0,
roughness: 0.2,
}),
// Puit
wood: new THREE.MeshStandardMaterial({
color: 0x8B4513,
metalness: 0,
roughness: 0.9,
}),
// Klaas
glass: new THREE.MeshPhysicalMaterial({
color: 0x88ccff,
transmission: 0.9,
thickness: 0.5,
roughness: 0,
metalness: 0,
}),
// Helendav (ei vaja valgust)
glow: new THREE.MeshBasicMaterial({
color: 0x22c55e,
}),
// Toon shader (joonistusfilm stiil)
toon: new THREE.MeshToonMaterial({
color: 0x44aa88,
}),
};
// Valguse tüübid
function setupLighting(scene: THREE.Scene): void {
// 1. Ambient — ühtlane taustvalgus
scene.add(new THREE.AmbientLight(0x404080, 0.4));
// 2. Directional — päikesevalgus (paralleelsed kiired)
const sun = new THREE.DirectionalLight(0xffeedd, 1.2);
sun.position.set(10, 20, 10);
sun.castShadow = true;
scene.add(sun);
// 3. Point — lambipirn (kiirgab kõikjale)
const torch = new THREE.PointLight(0xff6600, 2, 15);
torch.position.set(0, 3, 0);
torch.castShadow = true;
scene.add(torch);
// 4. Spot — prožektor (koonus)
const spot = new THREE.SpotLight(0xffffff, 3, 20, Math.PI / 6);
spot.position.set(0, 10, 0);
spot.castShadow = true;
scene.add(spot);
// 5. Hemisphere — taevas + maapind
scene.add(new THREE.HemisphereLight(0x87ceeb, 0x362d1b, 0.3));
}
🔫 Samm 4 — Raycasting (Klikkimine 3D-s)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
canvas.addEventListener('click', (event) => {
// Normaliseeri hiire positsioon (-1 kuni +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
// Kontrolli mille peale klikkisid
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const hit = intersects[0];
console.log('Tabatud objekt:', hit.object.name);
console.log('Tabamise punkt:', hit.point);
console.log('Kaugus:', hit.distance);
// Visuaalne tagasiside
if (hit.object instanceof THREE.Mesh) {
const original = (hit.object.material as THREE.MeshStandardMaterial).color.clone();
(hit.object.material as THREE.MeshStandardMaterial).color.set(0xff0000);
setTimeout(() => {
(hit.object.material as THREE.MeshStandardMaterial).color.copy(original);
}, 200);
}
}
});
📝 Harjutused
-
Esimene 3D stseen
Seadista Three.js + Vite projekt. Loo stseen: põrand, 5 erinevat geomeetriat (kuubik, sfäär, silinder, torus, koonus), kaamera orbiting. Lisa varjud.
-
Third-person kontroller
Loo WASD liikumine 3D-s, kaamera jälgib mängijat. Lisa hüppamine (gravitatsiooniga), kaamera orbit hiire liigutamisega. Maapinna kokkupõrge.
-
Materjalide galerii
Loo 10 sfääri erinevate materjalidega: metall, puit, klaas, kivi, helendav jne. Lisa GUI sliderid (roughness, metalness, color) reaalajas muutmiseks.
-
Valgustuse labor
Lisa stseenile iga valgusetüüp: ambient, directional, point, spot, hemisphere. GUI kontrollid iga valguse jaoks (positsioon, värv, intensiivsus). Lisa real-time varjud.
-
3D objektide kogumine
Loo mäng: 20 hõljuvat münti 3D maailmas. Mängija liigub ja kogub münte. Raycasting hiire klikkimisega alternatiivina. Skoor ja animatsioonid.
-
Protseduurne 3D maastik
Genereeri 3D maastik Perlin noise'iga (muuda PlaneGeometry vertex'ite Y koordinaate). Lisa värvid kõrguse järgi. Lisa vesi udustas planeti pind.
-
Partikliefektid 3D-s
Loo Three.js Points süsteemiga 3D partiklid: tulekahju, lumesadu, ilutulestik. Lisa custom shaderid partiklite kujutamiseks.