📚معرفی و ساختار پایه

Three.js یک لایه انتزاعی روی WebGL است که ساخت صحنه‌های سه‌بعدی را در مرورگر آسان می‌کند. هر صحنه Three.js از سه عنصر اصلی تشکیل می‌شود:

// ۱. ساختار پایه
const scene    = new THREE.Scene();
const camera   = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement); // canvas

// ۲. ساخت Mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x88ce02 });
const cube     = new THREE.Mesh(geometry, material);
scene.add(cube);

camera.position.set(0, 0, 3);

// ۳. حلقه انیمیشن
function animate() {
  requestAnimationFrame(animate);
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}
animate();
🏸 دمو: مکعب پایه — Scene + Camera + Renderer
ⓘ PerspectiveCamera(fov, aspect, near, far): fov = زاویه دید (درجه) | aspect = نسبت تصویر | near/far = محدوده رندر

🔷Geometry و Mesh

Geometry شکل هندسی است و Mesh ترکیب Geometry + Material. Three.js شکل‌های آماده زیادی دارد.

new THREE.BoxGeometry(w, h, d)              // جعبه
new THREE.SphereGeometry(r, wSeg, hSeg)      // کره
new THREE.CylinderGeometry(rTop, rBot, h)    // استوانه
new THREE.TorusGeometry(r, tube, rSeg, tSeg) // حلقه
new THREE.ConeGeometry(r, h, seg)            // مخروط
new THREE.PlaneGeometry(w, h)               // صفحه
new THREE.TorusKnotGeometry(r, tube)         // گره توروس

// موقعیت، چرخش، مقیاس
mesh.position.set(x, y, z);
mesh.rotation.set(rx, ry, rz);  // رادیان
mesh.scale.set(sx, sy, sz);
▶ دمو: انواع Geometry

🎨Material

Material ظاهر سطح Mesh را تعریف می‌کند. انتخاب درست Material تأثیر زیادی بر عملکرد و ظاهر دارد.

// MeshBasicMaterial — بدون نور، سریع‌ترین
new THREE.MeshBasicMaterial({ color: 0xff0000 });

// MeshStandardMaterial — PBR، واقعی‌ترین
new THREE.MeshStandardMaterial({
  color:     0x88ce02,
  roughness: 0.4,    // 0=براق، 1=کدر
  metalness: 0.6     // 0=پلاستیک، 1=فلز
});

// MeshPhongMaterial — براق کلاسیک
new THREE.MeshPhongMaterial({ color: 0x3498db, shininess: 100 });

// MeshNormalMaterial — رنگ بر اساس جهت
new THREE.MeshNormalMaterial();

// wireframe
new THREE.MeshBasicMaterial({ color: 0x88ce02, wireframe: true });
▶ دمو: مقایسه Material‌ها
Materialنور لازمکاربرد
MeshBasicMaterialخیرUI، debug، کارت‌های رنگی
MeshLambertMaterialبلهسطوح کدر، بهینه
MeshPhongMaterialبلهپلاستیک، شیشه براق
MeshStandardMaterialبلهPBR واقعی — پیشنهادی
MeshNormalMaterialخیرdebug نرمال‌ها
MeshDepthMaterialخیرسایه، depth effects

💡نور (Lighting)

بدون نور، Materialهایی که به نور نیاز دارند (Standard، Lambert، Phong) کاملاً سیاه هستند.

// AmbientLight — نور محیطی یکنواخت
scene.add(new THREE.AmbientLight(0xffffff, 0.4));

// DirectionalLight — نور جهت‌دار (مثل خورشید)
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 7);
scene.add(dirLight);

// PointLight — نور نقطه‌ای (مثل لامپ)
const ptLight = new THREE.PointLight(0xff8800, 2, 10);
ptLight.position.set(0, 3, 0);
scene.add(ptLight);

// SpotLight — نور مخروطی (مثل نورافکن)
const spot = new THREE.SpotLight(0xffffff, 1);
spot.angle = Math.PI / 6;
spot.penumbra = 0.2;
scene.add(spot);
💡 دمو: PointLight رنگی متحرک
نورویژگی
AmbientLightنور یکنواخت بدون جهت — برای روشنایی کلی
DirectionalLightنور موازی از یک جهت — مانند خورشید
PointLight(color, intensity, distance)نور نقطه‌ای در همه جهات
SpotLightنور مخروطی با angle و penumbra
HemisphereLight(sky, ground)نور آسمان + زمین — طبیعی‌ترین ambient

🎬انیمیشن و حرکت

حلقه انیمیشن با requestAnimationFrame پیاده‌سازی می‌شود. برای زمان‌بندی دقیق از Clock استفاده کنید.

const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);
  const t = clock.getElapsedTime(); // ثانیه از شروع

  // چرخش
  cube.rotation.y = t * 0.5;

  // حرکت موجی
  cube.position.y = Math.sin(t) * 0.5;

  // مقیاس نبضی
  const s = 1 + Math.sin(t * 2) * 0.1;
  cube.scale.set(s, s, s);

  renderer.render(scene, camera);
}
animate();
▶ دمو: انیمیشن — چرخش + موج + نبض

🎥Camera و کنترل

Three.js دو نوع اصلی دوربین دارد. برای کنترل تعاملی OrbitControls پیشنهاد می‌شود.

// PerspectiveCamera — منظر واقعی (پیشنهادی)
const cam = new THREE.PerspectiveCamera(
  75,                  // fov درجه
  w / h,               // aspect ratio
  0.1, 1000           // near, far
);
cam.position.set(0, 2, 5);
cam.lookAt(0, 0, 0);

// OrthographicCamera — بدون تغییر اندازه با فاصله
new THREE.OrthographicCamera(left, right, top, bottom, near, far);

// تغییر اندازه پنجره
window.addEventListener('resize', () => {
  cam.aspect = innerWidth / innerHeight;
  cam.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});
🎥 دمو: Perspective vs Orthographic — کلیک برای تغییر
PerspectiveCamera

📷Texture

Texture تصاویر را روی سطح Mesh نقشه می‌کشد. TextureLoader برای بارگذاری و اعمال استفاده می‌شود.

const loader = new THREE.TextureLoader();

// بارگذاری texture
const texture = loader.load('brick.jpg', () => {
  renderer.render(scene, camera); // رندر بعد از لود
});

// اعمال روی material
const mat = new THREE.MeshStandardMaterial({
  map:         texture,    // رنگ اصلی
  normalMap:   normTex,    // جزئیات سطح
  roughnessMap: roughTex   // نقشه زبری
});

// تکرار texture
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(4, 4); // ۴×۴ تکرار

// Cube map (محیط ۳۶۰)
const cubeMap = new THREE.CubeTextureLoader()
  .load([px, nx, py, ny, pz, nz]);
scene.background = cubeMap;
📷 دمو: Canvas Texture تولیدشده
💡 بهینه‌سازی: اندازه texture باید توان ۲ باشد (256، 512، 1024، 2048). از texture.minFilter = THREE.LinearFilter برای جلوگیری از mipmap‌های اضافی استفاده کنید.

🌓Shadow

سایه‌ها در Three.js باید صریحاً فعال شوند — هم روی renderer، هم روی نور، هم روی Mesh.

// ۱. فعال‌سازی روی Renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // نرم‌تر

// ۲. نور باید سایه بسازد
dirLight.castShadow = true;
dirLight.shadow.mapSize.set(2048, 2048); // کیفیت
dirLight.shadow.camera.far = 50;

// ۳. اشیاء — سایه انداز و سایه‌پذیر
cube.castShadow = true;    // این شیء سایه می‌اندازد
floor.receiveShadow = true; // این شیء سایه می‌پذیرد

// کلی — همه بسازند/بپذیرند
scene.traverse(obj => {
  if (obj.isMesh) { obj.castShadow = true; obj.receiveShadow = true; }
});
🌓 دمو: سایه واقعی — نور گردنده

🔍Raycasting — تعامل با اشیاء

Raycaster یک پرتو از دوربین به صحنه می‌فرستد و اشیاء برخوردی را تشخیص می‌دهد — پایه تعامل موس و کلیک در Three.js.

const raycaster = new THREE.Raycaster();
const mouse     = new THREE.Vector2();

window.addEventListener('mousemove', e => {
  // تبدیل موقعیت موس به فضای normalized (-1 تا 1)
  mouse.x = (e.clientX / innerWidth)  * 2 - 1;
  mouse.y = -(e.clientY / innerHeight) * 2 + 1;
});

function animate() {
  requestAnimationFrame(animate);
  raycaster.setFromCamera(mouse, camera);
  const hits = raycaster.intersectObjects(scene.children);

  if (hits.length > 0) {
    hits[0].object.material.color.set(0xff0000); // hover
  }
  renderer.render(scene, camera);
}
🔍 دمو: hover روی کره‌ها — رنگ تغییر می‌کند

🔥ذرات (Particles) و چیت‌شیت

سیستم ذرات با Points و BufferGeometry پیاده‌سازی می‌شود — بهینه‌ترین راه برای هزاران نقطه.

const count = 5000;
const positions = new Float32Array(count * 3);
for (let i = 0; i < count * 3; i++)
  positions[i] = (Math.random() - 0.5) * 10;

const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));

const mat = new THREE.PointsMaterial({
  size:        0.05,
  color:       0x88ce02,
  transparent: true,
  opacity:     0.8
});

scene.add(new THREE.Points(geo, mat));
✨ دمو: ۵۰۰۰ ذره دوار

📄 چیت‌شیت سریع

عملیاتکد
صحنه پایهnew Scene() + Camera + WebGLRenderer
Meshnew Mesh(geometry, material)
موقعیتmesh.position.set(x,y,z)
چرخشmesh.rotation.y += 0.01
زمانclock.getElapsedTime()
نور محیطیnew AmbientLight(0xffffff, 0.4)
نور جهت‌دارnew DirectionalLight(0xffffff, 1)
texturenew TextureLoader().load('img.jpg')
سایهrenderer.shadowMap.enabled = true
کلیک/hovernew Raycaster().intersectObjects(...)
ذراتnew Points(bufferGeo, PointsMaterial)
حذف از صحنهscene.remove(mesh); mesh.geometry.dispose()
💡 بهینه‌سازی:
  • از BufferGeometry به جای Geometry استفاده کنید
  • اشیاء ثابت را mesh.matrixAutoUpdate = false کنید
  • Geometryها و Materialهای استفاده‌نشده را .dispose() کنید
  • از renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) استفاده کنید
⚠ WebGL Context: هر canvas یک WebGL context مصرف می‌کند. مرورگرها معمولاً ۱۶ context همزمان پشتیبانی می‌کنند. در صفحاتی با چند canvas مراقب باشید.

🚀زمین بازی کد (Playground)

صحنهٔ سه‌بعدی Three.js را همین‌جا بساز و زنده ببین. هندسه، متریال و انیمیشن را تغییر بده و ▶ اجرا را بزن. نسخهٔ r134 آفلاین بارگذاری می‌شود.