مقدمة في Three.js
Three.js هي مكتبة JavaScript مفتوحة المصدر تسمح لك بإنشاء وعرض رسومات ثلاثية الأبعاد في المتصفح باستخدام WebGL. تم تطويرها بواسطة Ricardo Cabello (المعروف باسم Mr.doob) وتساهم فيها مجموعة كبيرة من المطورين.
مميزات Three.js
- تجريد بسيط لتقنية WebGL المعقدة
- دعم متعدد المتصفحات
- مجموعة كبيرة من الأشكال الهندسية والمواد الجاهزة
- أدوات لتحميل وعرض النماذج ثلاثية الأبعاد من تنسيقات متعددة
- دعم للإضاءة والظلال والملمس
- مكتبة ضخمة من الأمثلة والتوثيق المتاح
لماذا تستخدم Three.js؟
تسمح Three.js للمطورين بإنشاء تجارب ثلاثية الأبعاد مذهلة على الويب دون الحاجة إلى فهم عميق لـ WebGL المعقدة. يمكن استخدامها في:
- المواقع التفاعلية
- تصور البيانات
- الألعاب ثلاثية الأبعاد
- تجارب الواقع الافتراضي والمعزز
- العروض والنماذج ثلاثية الأبعاد للمنتجات
المعرفة المطلوبة
للبدء مع Three.js، من المفيد أن تكون لديك معرفة بـ:
- أساسيات JavaScript
- معرفة بسيطة بالرياضيات (المتجهات، المصفوفات)
- فهم أساسي للرسوميات ثلاثية الأبعاد
- HTML و CSS للتكامل مع صفحات الويب
ومع ذلك، سنقوم بشرح كل هذه المفاهيم بالتفصيل خلال هذا التوثيق.
البداية وإعداد المكتبة
هناك عدة طرق لاستخدام Three.js في مشروعك. سنشرح الطرق الأكثر شيوعًا:
1. استخدام CDN (شبكة توزيع المحتوى)
الطريقة الأسهل والأسرع هي استخدام CDN لتضمين مكتبة Three.js مباشرة في ملف HTML الخاص بك:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first Three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<!-- استيراد مكتبة Three.js من CDN -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<!-- ملفك البرمجي -->
<script>
// هنا ستكتب كود Three.js الخاص بك
</script>
</body>
</html>
شرح الكود:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
: يقوم بتحميل الإصدار 0.157.0 من مكتبة Three.js من CDN.- يمكنك تغيير رقم الإصدار حسب الحاجة، أو استخدام الإصدار الأحدث دائمًا عن طريق حذف رقم الإصدار.
- الكود الخاص بك سيكتب داخل وسم
<script>
الثاني.
2. التثبيت باستخدام npm
إذا كنت تستخدم بيئة تطوير JavaScript حديثة مع أدوات مثل Webpack أو Vite، يمكنك تثبيت Three.js كحزمة npm:
# باستخدام npm
npm install three
# أو باستخدام yarn
yarn add three
بعد التثبيت، يمكنك استيراد المكتبة في ملفات JavaScript الخاصة بك:
// استيراد المكتبة كاملة
import * as THREE from 'three';
// أو استيراد مكونات محددة فقط لتقليل حجم الحزمة النهائية
import { Scene, PerspectiveCamera, WebGLRenderer } from 'three';
3. تحميل المكتبة محليًا
يمكنك أيضًا تحميل مكتبة Three.js واستخدامها محليًا:
- قم بزيارة صفحة الإصدارات في GitHub
- قم بتنزيل أحدث إصدار (zip أو tar.gz)
- استخرج الملفات ونسخ
build/three.min.js
إلى مشروعك - قم بتضمين الملف في HTML الخاص بك
<script src="path/to/three.min.js"></script>
ملاحظة مهمة حول المكونات الإضافية
تتكون Three.js من المكتبة الأساسية ومجموعة من المكونات الإضافية (OrbitControls، GLTFLoader، إلخ). عند استخدام CDN أو الملفات المحلية، ستحتاج إلى تضمين هذه المكونات بشكل منفصل:
<!-- المكتبة الأساسية -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<!-- مكونات إضافية -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
عند استخدام npm، يمكنك استيراد المكونات الإضافية بهذه الطريقة:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
المشهد والكاميرا والعارض
قبل أن تتمكن من إنشاء أي محتوى ثلاثي الأبعاد، عليك إعداد المكونات الأساسية الثلاثة:
المشهد (Scene)
المشهد هو الحاوية التي تحتوي على جميع العناصر ثلاثية الأبعاد. يمكن تشبيهه بالمسرح الذي توضع عليه العناصر.
الكاميرا (Camera)
الكاميرا تحدد ما سيتم عرضه من المشهد. تمامًا مثل الكاميرا الحقيقية، تحدد زاوية الرؤية وكيفية ظهور العناصر.
العارض (Renderer)
العارض هو المسؤول عن رسم المشهد كما تراه الكاميرا على شاشة المتصفح، عادةً باستخدام WebGL.
إعداد المكونات الأساسية
فيما يلي الكود الأساسي لإعداد هذه المكونات:
// إنشاء مشهد جديد
const scene = new THREE.Scene();
// إنشاء كاميرا
const camera = new THREE.PerspectiveCamera(
75, // زاوية الرؤية (Field of View)
window.innerWidth / window.innerHeight, // نسبة العرض إلى الارتفاع
0.1, // المستوى القريب
1000 // المستوى البعيد
);
// تحديد موقع الكاميرا في المحور Z
camera.position.z = 5;
// إنشاء العارض
const renderer = new THREE.WebGLRenderer({
antialias: true, // تنعيم الحواف
alpha: true // دعم الشفافية
});
// تحديد حجم العارض
renderer.setSize(window.innerWidth, window.innerHeight);
// ضبط كثافة البكسل للشاشات عالية الدقة
renderer.setPixelRatio(window.devicePixelRatio);
// إضافة العارض إلى صفحة HTML
document.body.appendChild(renderer.domElement);
شرح كل سطر:
const scene = new THREE.Scene();
: إنشاء حاوية المشهد التي ستحتوي على جميع العناصر ثلاثية الأبعاد.const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
: إنشاء كاميرا منظورية تحاكي رؤية العين البشرية.- المعامل الأول (75) هو زاوية الرؤية بالدرجات - كلما زادت القيمة، زاد المجال المرئي (عادة بين 45-75).
- المعامل الثاني هو نسبة العرض إلى الارتفاع - يُضبط عادة على نسبة أبعاد الشاشة.
- المعاملان الثالث والرابع يحددان مستويات القص القريب والبعيد - العناصر خارج هذا النطاق لن تُرسم.
camera.position.z = 5;
: تحريك الكاميرا للخلف 5 وحدات على محور Z، وإلا ستكون الكاميرا في مركز المشهد (0,0,0).const renderer = new THREE.WebGLRenderer({...});
: إنشاء عارض WebGL مع خيارات إضافية.renderer.setSize(window.innerWidth, window.innerHeight);
: ضبط حجم العارض ليملأ الشاشة بالكامل.renderer.setPixelRatio(window.devicePixelRatio);
: تحسين العرض للشاشات عالية الدقة (Retina).document.body.appendChild(renderer.domElement);
: إضافة عنصر canvas الذي أنشأه العارض إلى الصفحة.
إعادة ضبط الحجم عند تغيير نافذة المتصفح
عند تغيير حجم نافذة المتصفح، يجب تحديث أبعاد الكاميرا والعارض:
// دالة لإعادة ضبط الأبعاد عند تغيير حجم النافذة
function onWindowResize() {
// تحديث نسبة العرض إلى الارتفاع للكاميرا
camera.aspect = window.innerWidth / window.innerHeight;
// تحديث مصفوفة إسقاط الكاميرا
camera.updateProjectionMatrix();
// تحديث حجم العارض
renderer.setSize(window.innerWidth, window.innerHeight);
}
// إضافة مستمع لحدث تغيير حجم النافذة
window.addEventListener('resize', onWindowResize);
شرح الكود:
camera.aspect = window.innerWidth / window.innerHeight;
: تحديث نسبة العرض إلى الارتفاع لتتناسب مع حجم النافذة الجديد.camera.updateProjectionMatrix();
: هام جدًا! يجب استدعاؤها بعد أي تغيير في خصائص الكاميرا لتطبيق التغييرات.renderer.setSize(window.innerWidth, window.innerHeight);
: تحديث حجم العارض ليناسب حجم النافذة الجديد.
حلقة الرسم (Render Loop)
لعرض المشهد، نحتاج إلى إنشاء حلقة رسم تستدعي وظيفة الرسم بشكل متكرر:
// دالة الرسم والتحريك
function animate() {
// طلب استدعاء الدالة مرة أخرى في الإطار التالي
requestAnimationFrame(animate);
// يمكنك إضافة أي تحديثات للعناصر هنا
// مثال: cube.rotation.x += 0.01;
// رسم المشهد
renderer.render(scene, camera);
}
// بدء حلقة الرسم
animate();
شرح الكود:
requestAnimationFrame(animate);
: يطلب من المتصفح استدعاء الدالة animate قبل الإطار التالي، مما يخلق حلقة متكررة.- هذه الطريقة أفضل من استخدام setInterval لأنها تتزامن مع معدل تحديث الشاشة وتتوقف عندما تكون علامة التبويب غير نشطة.
renderer.render(scene, camera);
: يقوم برسم المشهد من منظور الكاميرا.
نصائح مهمة
عند العمل مع المشهد والكاميرا والعارض، إليك بعض النصائح المهمة:
- يمكنك إنشاء أكثر من كاميرا في المشهد والتبديل بينها حسب الحاجة.
- يمكن تخصيص لون خلفية المشهد:
scene.background = new THREE.Color(0x0000ff);
- استخدم
renderer.setSize(width, height, false);
مع المعامل الثالثfalse
للحفاظ على حجم العرض مع تخفيض دقة الرسم للأداء. - كل العناصر في Three.js تستخدم نظام إحداثيات اليد اليمنى: X (يمين)، Y (لأعلى)، Z (للخارج).
الأشكال الهندسية
الأشكال الهندسية (Geometries) هي تعريفات رياضية للأشكال ثلاثية الأبعاد. تحدد المواقع الفضائية للنقاط (الرؤوس) وكيفية توصيلها لتشكيل الأسطح. توفر Three.js مجموعة واسعة من الأشكال الهندسية الجاهزة.
BoxGeometry
مكعب أو متوازي مستطيلات
const geometry = new THREE.BoxGeometry(
width, // العرض
height, // الارتفاع
depth // العمق
);
SphereGeometry
كرة
const geometry = new THREE.SphereGeometry(
radius, // نصف القطر
widthSegments, // عدد الشرائح أفقياً (اختياري)
heightSegments // عدد الشرائح رأسياً (اختياري)
);
CylinderGeometry
أسطوانة
const geometry = new THREE.CylinderGeometry(
radiusTop, // نصف قطر الأعلى
radiusBottom, // نصف قطر الأسفل
height // الارتفاع
);
ConeGeometry
مخروط
const geometry = new THREE.ConeGeometry(
radius, // نصف القطر
height // الارتفاع
);
PlaneGeometry
مستوى مسطح
const geometry = new THREE.PlaneGeometry(
width, // العرض
height // الارتفاع
);
TorusGeometry
حلقة (دونات)
const geometry = new THREE.TorusGeometry(
radius, // نصف قطر الحلقة
tubeRadius, // سمك الأنبوب
radialSegments // عدد شرائح الأنبوب
);
إنشاء وإضافة شكل هندسي إلى المشهد
لاستخدام شكل هندسي، يجب دمجه مع مادة (Material) لإنشاء شبكة (Mesh) يمكن إضافتها إلى المشهد:
// إنشاء شكل هندسي (مكعب)
const geometry = new THREE.BoxGeometry(1, 1, 1);
// إنشاء مادة بلون أخضر
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// إنشاء شبكة من الشكل الهندسي والمادة
const cube = new THREE.Mesh(geometry, material);
// إضافة الشبكة إلى المشهد
scene.add(cube);
// تغيير موقع المكعب (اختياري)
cube.position.x = 2;
cube.position.y = 0.5;
cube.position.z = -1;
شرح الكود:
new THREE.BoxGeometry(1, 1, 1)
: إنشاء مكعب أبعاده 1x1x1 وحدة.new THREE.MeshBasicMaterial({ color: 0x00ff00 })
: إنشاء مادة أساسية بلون أخضر (سنشرح المواد بالتفصيل في القسم التالي).new THREE.Mesh(geometry, material)
: دمج الشكل الهندسي مع المادة لإنشاء كائن قابل للعرض.scene.add(cube)
: إضافة المكعب إلى المشهد.cube.position.x = 2
: تغيير موقع المكعب على المحور X.
تحريك وتدوير وتغيير حجم الأشكال
يمكن التحكم في موضع ودوران وحجم أي شبكة (Mesh) في المشهد:
// تغيير الموضع
cube.position.set(2, 0.5, -1); // اختصار لتعيين X, Y, Z دفعة واحدة
// تدوير الشكل (بالراديان) حول المحاور X، Y، Z
cube.rotation.x = Math.PI / 4; // 45 درجة
cube.rotation.y = Math.PI / 6; // 30 درجة
cube.rotation.z = 0;
// تغيير الحجم (مقياس)
cube.scale.x = 2; // مضاعفة العرض
cube.scale.y = 0.5; // تقليل الارتفاع للنصف
cube.scale.set(2, 0.5, 1); // تعيين X, Y, Z دفعة واحدة
ملاحظات مهمة:
- الدوران في Three.js يتم قياسه بالراديان، وليس بالدرجات. للتحويل من درجات إلى راديان، استخدم:
radians = degrees * (Math.PI / 180)
- يمكن استخدام
object.position.set(x, y, z)
لتعيين جميع قيم الموضع دفعة واحدة، وكذلكrotation.set()
وscale.set()
.
الأشكال الهندسية المتقدمة
بالإضافة إلى الأشكال الأساسية، توفر Three.js أشكالًا متقدمة وطرقًا لإنشاء أشكال مخصصة:
- BufferGeometry: يمكنك إنشاء أشكال مخصصة بالكامل عن طريق تحديد إحداثيات الرؤوس يدويًا.
- TextGeometry: لإنشاء نص ثلاثي الأبعاد (يتطلب تحميل خط).
- ExtrudeGeometry: لإنشاء أشكال ثلاثية الأبعاد من خلال بثق مسارات ثنائية الأبعاد.
- LatheGeometry: لإنشاء أشكال دوارة مثل الأواني والمزهريات.
- TubeGeometry: لإنشاء أنابيب على طول مسار منحني.
المواد والألوان
المواد (Materials) تحدد مظهر الأشكال الهندسية من حيث اللون والشفافية والملمس وكيفية تفاعلها مع الضوء. توفر Three.js مجموعة متنوعة من المواد المختلفة لتناسب الاحتياجات المختلفة.
أنواع المواد الأساسية
MeshBasicMaterial
مادة أساسية بسيطة لا تتأثر بالإضاءة. تظهر بلون موحد دائمًا.
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00, // اللون الأخضر
wireframe: false, // إظهار الإطار السلكي فقط
transparent: false, // دعم الشفافية
opacity: 1.0, // مستوى التعتيم (1 = معتم تمامًا)
side: THREE.FrontSide // أي جانب يظهر
});
MeshLambertMaterial
مادة غير لامعة تتفاعل مع الإضاءة. مناسبة للأسطح الخشنة.
const material = new THREE.MeshLambertMaterial({
color: 0xff0000, // اللون الأحمر
emissive: 0x000000, // لون الانبعاث الذاتي
emissiveIntensity: 1 // شدة لون الانبعاث
});
MeshPhongMaterial
مادة لامعة تتفاعل مع الإضاءة. مناسبة للأسطح اللامعة مثل البلاستيك.
const material = new THREE.MeshPhongMaterial({
color: 0x0000ff, // اللون الأزرق
shininess: 100, // درجة اللمعان
specular: 0x111111 // لون الانعكاس
});
MeshStandardMaterial
مادة واقعية تستخدم نموذج معالجة فيزيائي أكثر دقة. مناسبة للمظهر الواقعي.
const material = new THREE.MeshStandardMaterial({
color: 0xffffff, // اللون الأبيض
roughness: 0.5, // الخشونة (0 = أملس، 1 = خشن)
metalness: 0.5, // المعدنية (0 = غير معدني، 1 = معدني)
flatShading: false // تظليل ناعم أو خشن
});
مثال تطبيقي: استخدام مواد مختلفة
// إنشاء هندسة مشتركة
const geometry = new THREE.SphereGeometry(1, 32, 32);
// المادة الأساسية البسيطة
const basicMaterial = new THREE.MeshBasicMaterial({
color: 0xff0000,
wireframe: true
});
const basicSphere = new THREE.Mesh(geometry, basicMaterial);
basicSphere.position.x = -3;
scene.add(basicSphere);
// مادة لامبرت (غير لامعة)
const lambertMaterial = new THREE.MeshLambertMaterial({
color: 0x00ff00
});
const lambertSphere = new THREE.Mesh(geometry, lambertMaterial);
lambertSphere.position.x = 0;
scene.add(lambertSphere);
// مادة فونج (لامعة)
const phongMaterial = new THREE.MeshPhongMaterial({
color: 0x0000ff,
shininess: 100,
specular: 0xffffff
});
const phongSphere = new THREE.Mesh(geometry, phongMaterial);
phongSphere.position.x = 3;
scene.add(phongSphere);
// إضافة إضاءة للمشهد لرؤية تأثير المواد
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 1, 2);
scene.add(light);
ملاحظات على المواد:
- تظهر
MeshBasicMaterial
بنفس اللون دائمًا بغض النظر عن الإضاءة في المشهد. - تتطلب
MeshLambertMaterial
وMeshPhongMaterial
وMeshStandardMaterial
وجود إضاءة في المشهد حتى تظهر بشكل صحيح. - يمكن تعيين خاصية
transparent: true
وضبطopacity
لأي مادة لجعلها شفافة. - باستخدام
side: THREE.DoubleSide
، يمكن جعل المادة مرئية من الجانبين، لكن هذا يؤثر على الأداء.
الألوان في Three.js
يمكن تحديد الألوان بعدة طرق في Three.js:
// باستخدام قيمة هيكسا (سداسية عشرية)
material.color = new THREE.Color(0xff0000); // أحمر
// باستخدام اسم اللون
material.color = new THREE.Color('red');
// باستخدام RGB (قيم من 0 إلى 1)
material.color = new THREE.Color(1, 0, 0); // أحمر
// ضبط لون مادة موجودة
const material = new THREE.MeshBasicMaterial();
material.color.set(0x00ff00); // تغيير اللون إلى أخضر
مواد متقدمة وخصائص إضافية
تتيح Three.js مواد وخصائص متقدمة للحصول على تأثيرات بصرية أكثر تعقيدًا:
- MeshToonMaterial: لتأثير الرسوم المتحركة (cartoon/cel shading).
- MeshMatcapMaterial: تستخدم صورة للمحاكاة البصرية بأداء عالٍ.
- MeshNormalMaterial: تظهر ألوانًا قائمة على اتجاهات الأسطح.
- MeshDepthMaterial: تظهر ألوانًا تعتمد على المسافة من الكاميرا.
- ShaderMaterial و RawShaderMaterial: لكتابة برامج shader مخصصة للتحكم الكامل في المظهر.
- يمكن استخدام الخريطة العادية (normal map) والخريطة المجسمة (bump map) لإضافة تفاصيل سطحية دون زيادة عدد المضلعات.
الإضاءة
الإضاءة أساسية لإضفاء العمق والواقعية على المشاهد ثلاثية الأبعاد. توفر Three.js أنواعًا مختلفة من مصادر الضوء لمحاكاة بيئات مختلفة.
AmbientLight
ضوء محيطي يضيء جميع الأشياء بالتساوي من جميع الاتجاهات. لا يخلق ظلالًا.
// ضوء محيطي بلون أبيض وشدة 0.5
const ambientLight = new THREE.AmbientLight(
0xffffff, // اللون
0.5 // الشدة
);
scene.add(ambientLight);
DirectionalLight
ضوء اتجاهي يرسل أشعة متوازية. يشبه ضوء الشمس.
// ضوء اتجاهي
const directionalLight = new THREE.DirectionalLight(
0xffffff, // اللون
1.0 // الشدة
);
directionalLight.position.set(10, 10, 10);
scene.add(directionalLight);
PointLight
نقطة ضوء تشع في جميع الاتجاهات من نقطة واحدة. يشبه المصباح.
// ضوء نقطي
const pointLight = new THREE.PointLight(
0xffffff, // اللون
1.0, // الشدة
100 // مسافة التوهج (0 = لانهائي)
);
pointLight.position.set(0, 10, 0);
scene.add(pointLight);
SpotLight
ضوء مخروطي يشبه ضوء الكشاف أو مصباح الإسقاط.
// ضوء مخروطي
const spotLight = new THREE.SpotLight(
0xffffff, // اللون
1.0, // الشدة
100, // المدى
Math.PI/4, // زاوية الفتح (بالراديان)
0.5, // تدرج الحواف (0-1)
1 // تضاؤل الضوء
);
spotLight.position.set(0, 10, 0);
scene.add(spotLight);
مثال تطبيقي: مزج أنواع الإضاءة
// إضافة ضوء محيطي خافت لتوفير إضاءة أساسية
const ambientLight = new THREE.AmbientLight(0x404040, 0.5); // لون رمادي، شدة 0.5
scene.add(ambientLight);
// إضافة ضوء اتجاهي قوي للظلال والتباين
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 7.5);
scene.add(directionalLight);
// إضافة ضوء نقطي للتفاصيل والتأثيرات الخاصة
const pointLight = new THREE.PointLight(0xff9000, 1, 50);
pointLight.position.set(-5, 5, -5);
scene.add(pointLight);
// إعداد المادة العاكسة للضوء
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
roughness: 0.3,
metalness: 0.7
});
// إنشاء شبكة لإظهار تأثير الإضاءة
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(2, 32, 32),
material
);
scene.add(sphere);
نصائح للإضاءة الفعالة:
- استخدم
AmbientLight
بشدة منخفضة لملء المناطق المظلمة، مع مصادر ضوء أخرى للتباين والتفاصيل. - فكر في وضع 3 مصادر للإضاءة: ضوء رئيسي، ضوء تعبئة خلفي، وضوء حافة لتحديد المعالم (تقنية الإضاءة الثلاثية).
- كلما زاد عدد مصادر الضوء، زاد الحمل على وحدة معالجة الرسومات. حاول تحقيق التوازن بين الجمالية والأداء.
- الإضاءة المعقدة جدًا قد تبطئ عرض المشهد، خاصة عند تفعيل الظلال.
التحكم في مصادر الضوء
// تغيير خصائص الضوء بعد إنشائه
directionalLight.intensity = 0.6; // تغيير شدة الضوء
directionalLight.color.set(0xffff00); // تغيير لون الضوء إلى أصفر
// تحديد هدف للضوء الاتجاهي أو المخروطي
directionalLight.target = targetObject; // توجيه الضوء نحو كائن محدد
scene.add(directionalLight.target); // يجب إضافة الهدف إلى المشهد أيضًا
// تغيير موقع الضوء
pointLight.position.set(5, -10, 2); // تغيير إحداثيات الضوء
// تحديث مصادر الضوء أثناء الحركة
function animate() {
requestAnimationFrame(animate);
// تحريك الضوء في شكل دائري
const time = Date.now() * 0.001; // الوقت بالثواني
pointLight.position.x = Math.sin(time) * 10;
pointLight.position.z = Math.cos(time) * 10;
renderer.render(scene, camera);
}
مصادر ضوء متقدمة
بالإضافة إلى مصادر الضوء الأساسية، توفر Three.js أنواعًا متخصصة من الإضاءة:
- HemisphereLight: يوفر إضاءة لطيفة تشبه ضوء السماء، مع لونين مختلفين من الأعلى والأسفل.
- RectAreaLight: يحاكي مصادر الضوء المستطيلة مثل النوافذ أو الشاشات المضيئة (يتطلب استخدام RectAreaLightUniformsLib).
- LightProbe: يستخدم لإضاءة المشهد استنادًا إلى صورة محيطية (environment map)، مما يوفر إضاءة واقعية ويعكس البيئة.
توفر هذه المصادر المتقدمة مزيدًا من الخيارات للحصول على إضاءة واقعية، لكنها قد تتطلب المزيد من الموارد.
الحركة والانيميشن
الحركة تضيف الحياة إلى المشاهد ثلاثية الأبعاد. في Three.js، يمكن إنشاء الحركة من خلال تحديث خصائص الكائنات في كل إطار من إطارات الرسم.
الحركة الأساسية
// نموذج دوران بسيط
function animate() {
// طلب استدعاء الدالة في الإطار التالي
requestAnimationFrame(animate);
// تدوير المكعب
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
// رسم المشهد
renderer.render(scene, camera);
}
// بدء حلقة الرسم
animate();
شرح الكود:
requestAnimationFrame(animate)
: يطلب من المتصفح استدعاء الدالة animate قبل الإطار التالي.cube.rotation.x += 0.01
: يزيد زاوية دوران المكعب حول محور X بمقدار 0.01 راديان في كل إطار.renderer.render(scene, camera)
: يرسم المشهد المحدث.
الحركة المستقلة عن معدل الإطارات
لضمان تجربة متسقة على جميع الأجهزة بغض النظر عن معدل الإطارات، يجب أن تكون الحركة مستقلة عن معدل الإطارات:
// تعريف متغيرات الوقت
let clock = new THREE.Clock();
let previousTime = 0;
function animate() {
requestAnimationFrame(animate);
// حساب الوقت المنقضي منذ الإطار السابق
const elapsedTime = clock.getElapsedTime();
const deltaTime = elapsedTime - previousTime;
previousTime = elapsedTime;
// تحديث موقع الكائن بناءً على الوقت المنقضي
cube.rotation.x += 1.0 * deltaTime; // دوران بمعدل 1 راديان في الثانية
cube.rotation.y += 0.5 * deltaTime; // دوران بمعدل 0.5 راديان في الثانية
renderer.render(scene, camera);
}
مزايا الحركة المستقلة عن معدل الإطارات:
- ستتحرك الكائنات بنفس السرعة بغض النظر عن معدل الإطارات على الجهاز.
- أكثر قابلية للتنبؤ وأسهل في الضبط (يمكنك تحديد "وحدات في الثانية" بدلاً من "وحدات في الإطار").
- ضرورية للمحاكاة الفيزيائية والحركات المعقدة.
حركات أكثر تعقيدًا
function animate() {
requestAnimationFrame(animate);
const time = clock.getElapsedTime();
// حركة دائرية
object1.position.x = Math.cos(time) * 5;
object1.position.z = Math.sin(time) * 5;
// حركة متموجة
object2.position.y = Math.sin(time * 2) * 0.5 + 1;
// حركة تتبع مسار (Lissajous curve)
object3.position.x = Math.sin(time * 1.5) * 3;
object3.position.y = Math.sin(time * 2.3) * 1;
object3.position.z = Math.cos(time * 1.8) * 3;
// دوران مع مواجهة اتجاه الحركة
object3.lookAt(new THREE.Vector3(0, 0, 0));
renderer.render(scene, camera);
}
مكتبة GSAP للحركة
لإنشاء حركات سلسة ومتقدمة، يمكن استخدام مكتبة GSAP مع Three.js:
// أولاً، قم بتضمين مكتبة GSAP
// <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
// حركة بسيطة باستخدام GSAP
gsap.to(cube.position, {
x: 2, // الموقع النهائي على محور X
duration: 2, // مدة الحركة بالثواني
ease: "power2.out" // دالة التسهيل
});
// حركة متسلسلة
gsap.timeline()
.to(cube.position, { x: 2, duration: 1 })
.to(cube.position, { y: 1, duration: 1 })
.to(cube.rotation, { z: Math.PI * 2, duration: 2 })
.to(cube.scale, { x: 2, y: 2, z: 2, duration: 1 });
// في دالة الرسم، لا تحتاج إلى تحديث موقع الكائن يدويًا
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
مزايا استخدام GSAP:
- دوال تسهيل متقدمة (Easing functions) للحركات الطبيعية.
- تنسيق الحركات المتعددة وتسلسلها.
- أداء محسّن والتعامل مع معدل الإطارات تلقائيًا.
- واجهة برمجة سهلة الاستخدام للحركات المعقدة.
نصائح للحركة الفعالة
- استخدم Stats.js لمراقبة معدل الإطارات وتحسين الأداء.
- استخدم التكرارية (Recursion) بحذر - تأكد من عدم وجود استدعاءات متداخلة لـ
requestAnimationFrame
. - حدد الحاجة - لا تقم بتحديث كل كائن في كل إطار إذا لم يكن ذلك ضروريًا.
- استخدم requestAnimationFrame بدلاً من setInterval أو setTimeout للحصول على حركة أكثر سلاسة.
- جرب مكتبة Tween.js كبديل أخف وزنًا من GSAP إذا كنت تحتاج إلى حركات بسيطة فقط.
التحكم بالكاميرا
يوفر Three.js وحدات تحكم تفاعلية للكاميرا تتيح للمستخدمين التفاعل مع المشهد ثلاثي الأبعاد عن طريق التحريك والتدوير والتكبير/التصغير.
OrbitControls
أكثر وحدات التحكم استخدامًا، تتيح للمستخدم الدوران حول نقطة محددة (عادة مركز المشهد):
// استيراد OrbitControls
// إذا كنت تستخدم npm:
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// إذا كنت تستخدم CDN:
// <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js"></script>
// إنشاء وحدة تحكم OrbitControls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// تعيين نقطة الهدف (مركز الدوران)
controls.target.set(0, 0, 0);
// تعيين حدود لزاوية الارتفاع العمودية (بالراديان)
controls.minPolarAngle = 0; // أعلى نقطة
controls.maxPolarAngle = Math.PI / 1.5; // حوالي 120 درجة
// تمكين التنعيم للحركة السلسة
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// تحديث وحدة التحكم في حلقة الرسم
function animate() {
requestAnimationFrame(animate);
// هام: يجب استدعاء هذه الدالة في كل إطار عند تمكين خاصية التنعيم
controls.update();
renderer.render(scene, camera);
}
خصائص مفيدة في OrbitControls:
enableZoom
: تمكين/تعطيل التكبير والتصغير (true افتراضيًا).enableRotate
: تمكين/تعطيل الدوران (true افتراضيًا).enablePan
: تمكين/تعطيل التحريك الجانبي (true افتراضيًا).minDistance
وmaxDistance
: تحديد حدود التكبير والتصغير.autoRotate
: دوران تلقائي حول الهدف (false افتراضيًا).autoRotateSpeed
: سرعة الدوران التلقائي.
TrackballControls
مشابهة لـ OrbitControls ولكن بدون قيود على الدوران العمودي، مما يسمح بالدوران الحر بالكامل:
// استيراد TrackballControls
// import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
const controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
// حلقة الرسم
function animate() {
requestAnimationFrame(animate);
// هام: يجب استدعاء update في كل إطار
controls.update();
renderer.render(scene, camera);
}
FlyControls و FirstPersonControls
توفران تجربة طيران أو تنقل من منظور الشخص الأول، مناسبة للألعاب وتطبيقات التجول:
// FlyControls - للتنقل الحر في الفضاء
// import { FlyControls } from 'three/examples/jsm/controls/FlyControls.js';
const controls = new THREE.FlyControls(camera, renderer.domElement);
controls.movementSpeed = 5; // سرعة الحركة
controls.rollSpeed = Math.PI / 4; // سرعة الدوران
controls.autoForward = false; // التقدم التلقائي
controls.dragToLook = true; // منع النظر عند السحب
// حلقة الرسم مع حساب الوقت
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
controls.update(delta); // تمرير الوقت المنقضي
renderer.render(scene, camera);
}
PointerLockControls
تحكم من منظور الشخص الأول يستخدم Pointer Lock API للتحكم الكامل بالماوس، مثالي للألعاب:
// import { PointerLockControls } from 'three/examples/jsm/controls/PointerLockControls.js';
const controls = new THREE.PointerLockControls(camera, document.body);
// إضافة حدث لتمكين قفل المؤشر عند النقر
document.addEventListener('click', function() {
controls.lock();
});
// الاستماع لأحداث المفاتيح للحركة
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
let moveForward = false;
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
function onKeyDown(event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
moveForward = true;
break;
case 'ArrowLeft':
case 'KeyA':
moveLeft = true;
break;
case 'ArrowDown':
case 'KeyS':
moveBackward = true;
break;
case 'ArrowRight':
case 'KeyD':
moveRight = true;
break;
}
}
function onKeyUp(event) {
switch (event.code) {
case 'ArrowUp':
case 'KeyW':
moveForward = false;
break;
case 'ArrowLeft':
case 'KeyA':
moveLeft = false;
break;
case 'ArrowDown':
case 'KeyS':
moveBackward = false;
break;
case 'ArrowRight':
case 'KeyD':
moveRight = false;
break;
}
}
وحدات تحكم إضافية
توفر Three.js أنواعًا أخرى من وحدات التحكم للاستخدامات المتخصصة:
- TransformControls: تتيح للمستخدم تحريك وتدوير وتغيير حجم الكائنات باستخدام واجهة رسومية.
- DragControls: تتيح سحب الكائنات في المشهد باستخدام الماوس.
- DeviceOrientationControls: تستخدم مستشعرات الجهاز المحمول للتحكم بالكاميرا.
- MapControls: مشابهة لـ OrbitControls ولكن مع محاور مختلفة للحركة، مناسبة للخرائط.
يمكن دمج بعض وحدات التحكم معًا للحصول على تجربة تفاعلية أكثر تعقيدًا، لكن يجب الحذر من التداخل في وظائفها.
الملمس والقوام
الملمس (Textures) هي صور تستخدم لإضافة التفاصيل والواقعية إلى الأسطح ثلاثية الأبعاد. تسمح لك بإضافة الألوان والتفاصيل والعمق للأشكال دون الحاجة لزيادة التعقيد الهندسي.
تحميل الملمس الأساسي
// إنشاء محمل الملمس
const textureLoader = new THREE.TextureLoader();
// تحميل ملمس وتطبيقه على مادة
const colorTexture = textureLoader.load('textures/wood.jpg');
// استخدام الملمس في مادة
const material = new THREE.MeshStandardMaterial({
map: colorTexture // خريطة اللون الأساسية
});
// إنشاء شبكة باستخدام المادة
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
material
);
scene.add(cube);
دوال رد الاتصال (Callbacks) للتحميل:
const texture = textureLoader.load(
'textures/wood.jpg', // مسار الملف
function (texture) { // عند اكتمال التحميل
console.log('تم تحميل الملمس بنجاح');
},
function (xhr) { // أثناء التقدم
console.log((xhr.loaded / xhr.total * 100) + '% تم تحميله');
},
function (error) { // عند حدوث خطأ
console.error('حدث خطأ أثناء تحميل الملمس', error);
}
);
أنواع الملمس المختلفة
خريطة اللون (Color/Albedo Map)
تحدد لون السطح الأساسي ونمطه.
material.map = colorTexture;
خريطة العمق (Normal Map)
تضيف تفاصيل سطحية دون زيادة عدد المضلعات.
material.normalMap = normalTexture;
خريطة النتوء (Bump Map)
مشابهة لخريطة العمق لكن أبسط، تستخدم الألوان كمقياس للارتفاع.
material.bumpMap = bumpTexture;
material.bumpScale = 0.05; // شدة التأثير
خريطة الإزاحة (Displacement Map)
تغير الهندسة الفعلية للشكل (تتطلب تقسيمًا كافيًا للهندسة).
material.displacementMap = displacementTexture;
material.displacementScale = 0.2; // شدة التأثير
خريطة الصقل (Roughness Map)
تحدد مدى خشونة السطح وتأثيره على انعكاس الضوء.
material.roughnessMap = roughnessTexture;
خريطة المعدنية (Metalness Map)
تحدد أي أجزاء من السطح معدنية وأيها غير معدنية.
material.metalnessMap = metalnessTexture;
خريطة التوهج (Emissive Map)
تحدد الأجزاء التي تشع ضوءًا (دون إضاءة المشهد فعليًا).
material.emissiveMap = emissiveTexture;
material.emissive = new THREE.Color(0xff0000); // لون التوهج
material.emissiveIntensity = 0.5; // شدة التوهج
خريطة الانسداد المحيطي (Ambient Occlusion)
تضيف ظلالًا واقعية في المناطق المحجوبة عن الضوء.
material.aoMap = aoTexture;
material.aoMapIntensity = 1.0; // شدة التأثير
مثال لتطبيق ملمس متكامل
// إنشاء محمل الملمس
const textureLoader = new THREE.TextureLoader();
// تحميل جميع أنواع الملمس
const colorTexture = textureLoader.load('textures/brick/color.jpg');
const normalTexture = textureLoader.load('textures/brick/normal.jpg');
const roughnessTexture = textureLoader.load('textures/brick/roughness.jpg');
const aoTexture = textureLoader.load('textures/brick/ao.jpg');
// إعداد المادة مع جميع الملمس
const material = new THREE.MeshStandardMaterial({
map: colorTexture,
normalMap: normalTexture,
roughnessMap: roughnessTexture,
aoMap: aoTexture,
aoMapIntensity: 1
});
// لاستخدام خريطة الانسداد المحيطي، يجب تحديد إحداثيات UV ثانية
const geometry = new THREE.BoxGeometry(1, 1, 1);
geometry.setAttribute(
'uv2',
new THREE.BufferAttribute(geometry.attributes.uv.array, 2)
);
// إنشاء الشبكة
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
خصائص الملمس
// الخصائص الشائعة للملمس
texture.wrapS = THREE.RepeatWrapping; // طريقة التكرار الأفقي
texture.wrapT = THREE.RepeatWrapping; // طريقة التكرار الرأسي
texture.repeat.set(2, 3); // تكرار الملمس (2 أفقيًا، 3 رأسيًا)
texture.offset.set(0.5, 0); // إزاحة الملمس
texture.rotation = Math.PI / 4; // تدوير الملمس (بالراديان)
texture.center.set(0.5, 0.5); // مركز الدوران
// تصفية الملمس
texture.magFilter = THREE.NearestFilter; // عند تكبير الملمس
texture.minFilter = THREE.NearestFilter; // عند تصغير الملمس
// تحميل مسبق للملمس (لتجنب التأخير)
texture.generateMipmaps = false; // إيقاف توليد المصغرات عند استخدام NearestFilter
خيارات التكرار (Wrapping):
THREE.RepeatWrapping
: تكرار الملمس.THREE.ClampToEdgeWrapping
: تمديد آخر بكسل (الافتراضي).THREE.MirroredRepeatWrapping
: تكرار الملمس مع عكس في كل تكرار.
مرشحات الملمس (Filtering):
THREE.LinearFilter
: تنعيم (الافتراضي).THREE.NearestFilter
: بكسلات واضحة (نمط بكسل آرت).
نصائح لاستخدام الملمس
- الحجم والأبعاد: يفضل استخدام أبعاد من مضاعفات 2 (مثل 512×512، 1024×1024) للأداء الأمثل.
- صيغة الملفات: استخدم JPG للصور العادية وPNG للصور التي تحتاج إلى شفافية.
- التحميل المسبق: قم بتحميل جميع الملمس قبل بدء العرض لتجنب التأخير والتوقف المؤقت أثناء التشغيل.
- الضغط: يمكن استخدام صيغ ملفات مضغوطة مثل .jpg بجودة مقبولة لتقليل حجم التحميل وتحسين الأداء.
- Atlas الملمس: دمج عدة ملمس في صورة واحدة كبيرة واستخدام إحداثيات UV للوصول إلى أجزاء مختلفة.
- احذر من الذاكرة: الملمس ذات الدقة العالية تستهلك ذاكرة GPU بكثافة، استخدم الحجم المناسب لاحتياجاتك.
تحميل النماذج الجاهزة
أحد أهم مميزات Three.js هي القدرة على تحميل النماذج ثلاثية الأبعاد المنشأة في برامج التصميم المختلفة مثل Blender أو Maya أو 3ds Max. تدعم المكتبة العديد من صيغ الملفات الشائعة للنماذج ثلاثية الأبعاد.
صيغ الملفات المدعومة
GLTF/GLB
صيغة حديثة تعتبر المعيار الذهبي للويب. تدعم الهندسة والمواد والملمس والحركة في ملف واحد.
OBJ
صيغة قديمة شائعة، تدعم الهندسة والمواد الأساسية مع ملفات MTL المرافقة للمواد والملمس.
FBX
صيغة شائعة تدعم الهندسة والمواد والحركة والتشوهات.
STL
صيغة بسيطة شائعة في الطباعة ثلاثية الأبعاد، تدعم الهندسة فقط دون مواد أو ملمس.
COLLADA (DAE)
صيغة XML تدعم الهندسة والمواد والحركة وبعض الخصائص المتقدمة.
3DS, PLY, وغيرها
صيغ إضافية مدعومة من خلال محملات مخصصة.
تحميل نموذج GLTF
GLTF (GL Transmission Format) هي الصيغة الموصى بها في Three.js، ويمكن تحميلها باستخدام GLTFLoader:
// استيراد GLTFLoader
// import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
// إنشاء المحمل
const gltfLoader = new THREE.GLTFLoader();
// تحميل النموذج
gltfLoader.load(
'models/robot.gltf', // مسار الملف
function (gltf) { // عند اكتمال التحميل
// إضافة النموذج إلى المشهد
scene.add(gltf.scene);
// الوصول إلى أجزاء النموذج
const model = gltf.scene;
// ضبط مقياس النموذج
model.scale.set(0.5, 0.5, 0.5);
// ضبط موضع النموذج
model.position.set(0, 0, 0);
// الوصول إلى الحركات (إذا كانت موجودة)
const animations = gltf.animations;
if (animations && animations.length) {
// إنشاء مشغل الحركة
const mixer = new THREE.AnimationMixer(model);
// تشغيل الحركة الأولى
const action = mixer.clipAction(animations[0]);
action.play();
// تحديث المشغل في حلقة الرسم
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer.update(delta);
renderer.render(scene, camera);
}
animate();
}
},
function (xhr) { // أثناء التقدم
console.log((xhr.loaded / xhr.total * 100) + '% تم تحميله');
},
function (error) { // عند حدوث خطأ
console.error('حدث خطأ أثناء تحميل النموذج', error);
}
);
تحميل نموذج OBJ
OBJ هي صيغة شائعة أخرى، ويمكن تحميلها باستخدام OBJLoader:
// استيراد OBJLoader
// import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
// استيراد MTLLoader للمواد
// import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js';
// تحميل ملف المواد أولاً (إذا كان متوفرًا)
const mtlLoader = new THREE.MTLLoader();
mtlLoader.load('models/car.mtl', function(materials) {
materials.preload();
// إنشاء محمل OBJ وتعيين المواد
const objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
// تحميل النموذج
objLoader.load('models/car.obj', function(object) {
// ضبط خصائص النموذج
object.scale.set(0.5, 0.5, 0.5);
// إضافة النموذج للمشهد
scene.add(object);
});
});
مدير تحميل (Loading Manager)
يمكن استخدام LoadingManager لتتبع وإدارة عملية تحميل الموارد المتعددة:
// إنشاء مدير تحميل
const loadingManager = new THREE.LoadingManager();
// تعريف الدوال الخاصة بالأحداث
loadingManager.onStart = function(url, itemsLoaded, itemsTotal) {
console.log('بدأ التحميل: ' + url);
// يمكن إظهار شاشة التحميل هنا
};
loadingManager.onProgress = function(url, itemsLoaded, itemsTotal) {
console.log('تم تحميل: ' + url + ' (' + itemsLoaded + '/' + itemsTotal + ')');
// يمكن تحديث شريط التقدم هنا
};
loadingManager.onLoad = function() {
console.log('تم تحميل جميع الموارد!');
// يمكن إخفاء شاشة التحميل هنا
};
loadingManager.onError = function(url) {
console.error('خطأ في تحميل: ' + url);
};
// استخدام مدير التحميل مع المحملات المختلفة
const textureLoader = new THREE.TextureLoader(loadingManager);
const gltfLoader = new THREE.GLTFLoader(loadingManager);
// تحميل الموارد باستخدام هذه المحملات
textureLoader.load('textures/ground.jpg');
gltfLoader.load('models/character.gltf');
نصائح للعمل مع النماذج
- حجم الملفات: حاول تحسين حجم النماذج بتقليل عدد المضلعات وضغط الملمس قبل استخدامها في تطبيق الويب.
- تنسيق GLTF/GLB: استخدم هذه التنسيقات كلما أمكن لأنها مصممة خصيصًا للويب وتتميز بالأداء الجيد والحجم المثالي.
- تحميل مسبق: قم بتحميل النماذج مسبقًا قبل عرضها للمستخدم، مع إظهار مؤشر تحميل لتحسين تجربة المستخدم.
- Draco Compression: استخدم ضغط Draco مع نماذج GLTF لتقليل حجم الملفات بشكل كبير (يتطلب DRACOLoader).
- LOD (Level of Detail): استخدم نماذج بتفاصيل مختلفة بناءً على المسافة من الكاميرا لتحسين الأداء.
الظلال
الظلال تضيف عمقًا وواقعية كبيرة للمشهد ثلاثي الأبعاد، لكنها تتطلب موارد حسابية إضافية. تدعم Three.js عدة أنواع من الظلال المسقطة (shadow mapping).
تمكين الظلال
لإضافة الظلال إلى المشهد، يجب اتباع ثلاث خطوات أساسية:
// 1. تمكين الظلال في العارض
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // نوع الظلال (ظلال ناعمة)
// 2. تحديد العناصر التي تلقي الظلال
// فقط الأضواء التالية يمكنها إلقاء الظلال: DirectionalLight, SpotLight, PointLight
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true; // تمكين إلقاء الظلال لهذا الضوء
// ضبط خصائص خريطة الظلال للضوء
directionalLight.shadow.mapSize.width = 1024; // دقة الظلال (افتراضيًا 512)
directionalLight.shadow.mapSize.height = 1024; // كلما زادت الدقة، تحسنت جودة الظلال
directionalLight.shadow.camera.near = 0.5; // الحد الأدنى لمجال رؤية الظلال
directionalLight.shadow.camera.far = 50; // الحد الأقصى لمجال رؤية الظلال
// للضوء الاتجاهي، يمكن ضبط حجم منطقة الظلال
directionalLight.shadow.camera.left = -10;
directionalLight.shadow.camera.right = 10;
directionalLight.shadow.camera.top = 10;
directionalLight.shadow.camera.bottom = -10;
scene.add(directionalLight);
// 3. تحديد الكائنات التي تلقي الظلال وتستقبلها
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
sphere.position.y = 1;
sphere.castShadow = true; // الكرة تلقي ظلالًا
scene.add(sphere);
// إنشاء أرضية لاستقبال الظلال
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ color: 0xcccccc })
);
floor.rotation.x = -Math.PI / 2; // تدوير الأرضية لتكون أفقية
floor.receiveShadow = true; // الأرضية تستقبل الظلال
scene.add(floor);
أنواع الظلال
يمكن اختيار نوع خريطة الظلال حسب الجودة والأداء المطلوب:
// النوع الأساسي (أسرع ولكن أقل جودة)
renderer.shadowMap.type = THREE.BasicShadowMap;
// ظلال مع تنعيم بسيط (افتراضي)
renderer.shadowMap.type = THREE.PCFShadowMap;
// ظلال ناعمة عالية الجودة (أبطأ ولكن أكثر واقعية)
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// خريطة الظلال بالدقة العالية (التقنية الأكثر تقدمًا ولكن الأبطأ)
renderer.shadowMap.type = THREE.VSMShadowMap;
تحسين الظلال للضوء النقطي والمخروطي
// ضوء نقطي مع ظلال
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 10, 0);
pointLight.castShadow = true;
// الظلال من الضوء النقطي تستخدم كاميرا منظورية في جميع الاتجاهات
pointLight.shadow.mapSize.width = 1024;
pointLight.shadow.mapSize.height = 1024;
pointLight.shadow.camera.near = 0.1;
pointLight.shadow.camera.far = 30;
scene.add(pointLight);
// ضوء مخروطي مع ظلال
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(5, 10, 5);
spotLight.angle = Math.PI / 6; // زاوية الضوء المخروطي
spotLight.castShadow = true;
// ضبط خصائص الظلال
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;
spotLight.shadow.camera.near = 0.5;
spotLight.shadow.camera.far = 20;
spotLight.shadow.camera.fov = 30; // زاوية الرؤية لكاميرا الظلال
scene.add(spotLight);
تحسين أداء الظلال
الظلال تؤثر بشكل كبير على أداء التطبيق. إليك بعض النصائح لتحسين الأداء:
- استخدم الظلال بحكمة: مكن الظلال فقط للكائنات المهمة والمرئية في المشهد.
- ضبط حجم خريطة الظلال: استخدم أصغر حجم ممكن مع الحفاظ على الجودة المقبولة (512×512 أو 1024×1024).
- ضبط كاميرا الظلال: قلل من مجال الرؤية للظلال بحيث يغطي فقط المنطقة المهمة في المشهد.
- استخدم بديل للظلال: مثل خرائط الانسداد المحيطي (Ambient Occlusion) للظلال الثابتة، أو الظلال المسقطة (Shadow Projection) للكائنات البسيطة.
- تحديث الظلال بشكل انتقائي: إذا كان المشهد ثابتًا، يمكنك حساب الظلال مرة واحدة فقط وليس في كل إطار:
renderer.shadowMap.autoUpdate = false; // إيقاف التحديث التلقائي renderer.shadowMap.needsUpdate = true; // تحديث يدوي عند الحاجة
التفاعل والأحداث
إضافة التفاعل للمشهد ثلاثي الأبعاد يتطلب استخدام أحداث المستخدم (مثل النقر بالماوس) مع تقنيات اكتشاف التقاطع مع الكائنات ثلاثية الأبعاد.
اكتشاف النقر على الكائنات
يمكن اكتشاف النقر على الكائنات باستخدام Raycaster، الذي يرسل شعاعًا من موضع الماوس ويكتشف تقاطعه مع الكائنات في المشهد:
// إنشاء Raycaster وناقل لتخزين موضع الماوس
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// مصفوفة لتخزين الكائنات القابلة للنقر
const clickableObjects = [];
// إضافة كائنات للمشهد وتسجيلها كقابلة للنقر
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(1, 32, 32),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
sphere.position.set(0, 1, 0);
scene.add(sphere);
clickableObjects.push(sphere);
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshStandardMaterial({ color: 0x00ff00 })
);
cube.position.set(2, 0.5, 0);
scene.add(cube);
clickableObjects.push(cube);
// دالة للتعامل مع حدث النقر
function onClick(event) {
// حساب موضع الماوس بإحداثيات قياسية (-1 إلى +1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// ضبط الشعاع وفقًا لموضع الماوس والكاميرا
raycaster.setFromCamera(mouse, camera);
// البحث عن تقاطعات مع الكائنات القابلة للنقر
const intersects = raycaster.intersectObjects(clickableObjects);
// إذا تم النقر على كائن
if (intersects.length > 0) {
// الكائن الأول الذي تم النقر عليه
const selectedObject = intersects[0].object;
// تنفيذ إجراء على الكائن المحدد
if (selectedObject === sphere) {
// غير لون الكرة
sphere.material.color.set(Math.random() * 0xffffff);
} else if (selectedObject === cube) {
// دور المكعب
gsap.to(cube.rotation, { y: cube.rotation.y + Math.PI * 2, duration: 1 });
}
}
}
// إضافة مستمع للنقر
window.addEventListener('click', onClick);
تحريك الكائنات بالسحب والإفلات
يمكن تنفيذ السحب والإفلات للكائنات باستخدام مزيج من الأحداث:
// متغيرات لتتبع حالة السحب
let selectedObject = null;
let dragPlane = new THREE.Plane();
let dragOffset = new THREE.Vector3();
// إنشاء النواقل اللازمة
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const intersectionPoint = new THREE.Vector3();
// دالة بدء السحب
function onMouseDown(event) {
// تحديث موضع الماوس
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// إعداد الشعاع
raycaster.setFromCamera(mouse, camera);
// اختبار التقاطع مع الكائنات القابلة للسحب
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
// تعطيل وحدات التحكم في الكاميرا إذا كانت موجودة
if (controls) controls.enabled = false;
// حفظ الكائن المحدد
selectedObject = intersects[0].object;
// إنشاء مستوى للسحب عمودي على اتجاه الكاميرا ويمر عبر الكائن
dragPlane.setFromNormalAndCoplanarPoint(
camera.getWorldDirection(dragPlane.normal),
selectedObject.position
);
// حساب نقطة التقاطع على المستوى
raycaster.ray.intersectPlane(dragPlane, intersectionPoint);
// حفظ الإزاحة بين موضع الكائن ونقطة النقر
dragOffset.copy(selectedObject.position).sub(intersectionPoint);
}
}
// دالة السحب
function onMouseMove(event) {
// تحديث موضع الماوس
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// إذا كان هناك كائن محدد
if (selectedObject) {
// إعداد الشعاع
raycaster.setFromCamera(mouse, camera);
// حساب نقطة التقاطع مع مستوى السحب
raycaster.ray.intersectPlane(dragPlane, intersectionPoint);
// تحديث موضع الكائن مع الإزاحة المحفوظة
selectedObject.position.copy(intersectionPoint.add(dragOffset));
}
}
// دالة إنهاء السحب
function onMouseUp() {
if (selectedObject) {
// إعادة تمكين وحدات التحكم في الكاميرا
if (controls) controls.enabled = true;
// مسح الكائن المحدد
selectedObject = null;
}
}
// إضافة مستمعات الأحداث
window.addEventListener('mousedown', onMouseDown);
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
إضافة تلميحات ومعلومات
يمكن إضافة تلميحات عند تمرير المؤشر فوق الكائنات:
// إنشاء عنصر HTML للتلميح
const tooltip = document.createElement('div');
tooltip.style.position = 'absolute';
tooltip.style.background = 'rgba(0, 0, 0, 0.7)';
tooltip.style.color = 'white';
tooltip.style.padding = '5px 10px';
tooltip.style.borderRadius = '4px';
tooltip.style.fontSize = '14px';
tooltip.style.display = 'none';
document.body.appendChild(tooltip);
// دالة تمرير المؤشر
function onMouseMove(event) {
// تحديث موضع الماوس
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// تحديث موضع التلميح
tooltip.style.left = event.clientX + 10 + 'px';
tooltip.style.top = event.clientY + 10 + 'px';
// اختبار التقاطع مع الكائنات
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(clickableObjects);
if (intersects.length > 0) {
// عرض التلميح المناسب للكائن
if (intersects[0].object === sphere) {
tooltip.textContent = 'كرة حمراء - انقر لتغيير اللون';
tooltip.style.display = 'block';
} else if (intersects[0].object === cube) {
tooltip.textContent = 'مكعب أخضر - انقر للتدوير';
tooltip.style.display = 'block';
}
} else {
// إخفاء التلميح عند عدم وجود كائن تحت المؤشر
tooltip.style.display = 'none';
}
}
// إضافة مستمع لحدث تحريك المؤشر
window.addEventListener('mousemove', onMouseMove);
دعم شاشات اللمس
لدعم الأجهزة المحمولة، يجب التعامل مع أحداث اللمس:
// تحويل حدث اللمس إلى موضع ماوس
function getTouchPosition(event) {
const touch = event.touches[0];
return {
clientX: touch.clientX,
clientY: touch.clientY
};
}
// تعديل مستمعات الأحداث للأجهزة المحمولة
window.addEventListener('touchstart', (event) => {
event.preventDefault(); // منع السلوك الافتراضي
onMouseDown(getTouchPosition(event));
});
window.addEventListener('touchmove', (event) => {
event.preventDefault();
onMouseMove(getTouchPosition(event));
});
window.addEventListener('touchend', () => {
onMouseUp();
});
أنظمة الجسيمات
أنظمة الجسيمات هي تقنية قوية لمحاكاة الظواهر الطبيعية المعقدة مثل النار، الدخان، المطر، الثلج، الغبار وغيرها. في Three.js، يمكن إنشاء أنظمة جسيمات بطرق مختلفة اعتمادًا على مستوى التعقيد المطلوب.
نظام الجسيمات الأساسي
أبسط طريقة لإنشاء نظام جسيمات هي استخدام Points مع BufferGeometry:
// إنشاء هندسة للجسيمات
const particlesGeometry = new THREE.BufferGeometry();
const particleCount = 5000;
// مصفوفة لتخزين إحداثيات الجسيمات (x, y, z لكل جسيم)
const positions = new Float32Array(particleCount * 3);
// إنشاء جسيمات في شكل مكعب
for (let i = 0; i < particleCount; i++) {
// حساب موقع المؤشر في المصفوفة
const i3 = i * 3;
// توزيع عشوائي في مكعب
positions[i3] = (Math.random() - 0.5) * 10; // x
positions[i3 + 1] = (Math.random() - 0.5) * 10; // y
positions[i3 + 2] = (Math.random() - 0.5) * 10; // z
}
// إضافة المواقع كسمة للهندسة
particlesGeometry.setAttribute(
'position',
new THREE.BufferAttribute(positions, 3)
);
// إنشاء مادة للجسيمات
const particlesMaterial = new THREE.PointsMaterial({
size: 0.05, // حجم الجسيمات
color: 0xffffff, // لون الجسيمات
transparent: true, // السماح بالشفافية
opacity: 0.8, // مستوى التعتيم
// إضافة ملمس (اختياري)
// map: textureLoader.load('textures/particles/circle.png'),
// alphaTest: 0.001,
// depthWrite: false, // يمنع مشاكل الترتيب مع الأجسام الشفافة
});
// إنشاء نظام الجسيمات
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particles);
// تحريك الجسيمات (في حلقة الرسم)
function animate() {
requestAnimationFrame(animate);
// تدوير نظام الجسيمات بالكامل
particles.rotation.y += 0.001;
renderer.render(scene, camera);
}
جسيمات مع ملمس
لتحسين مظهر الجسيمات، يمكن استخدام ملمس:
// تحميل ملمس للجسيمات
const textureLoader = new THREE.TextureLoader();
const particleTexture = textureLoader.load('textures/particles/circle.png');
// إنشاء مادة الجسيمات مع الملمس
const particlesMaterial = new THREE.PointsMaterial({
size: 0.1,
sizeAttenuation: true, // تصغير الجسيمات البعيدة
map: particleTexture,
transparent: true,
alphaMap: particleTexture, // استخدام نفس الملمس للشفافية
depthWrite: false, // مهم لتجنب مشاكل ترتيب العمق
blending: THREE.AdditiveBlending, // مزج إضافي لتأثير التوهج
color: 0x88ccff
});
ملاحظات هامة:
depthWrite: false
: يمنع كتابة معلومات العمق للجسيمات، مما يحل مشاكل الترتيب مع الكائنات الشفافة.blending: THREE.AdditiveBlending
: يضيف لون الجسيمات إلى الألوان الموجودة خلفها، مما يخلق تأثير توهج.sizeAttenuation
: تصغير الجسيمات البعيدة لمحاكاة المنظور.
تحريك الجسيمات بشكل فردي
يمكن تحريك كل جسيم بشكل مستقل عن طريق تحديث مواقعها:
// إنشاء مصفوفات لتخزين السرعات والمواقع
const positions = new Float32Array(particleCount * 3);
const velocities = []; // سرعات الجسيمات
// تهيئة المواقع والسرعات
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// تهيئة المواقع
positions[i3] = (Math.random() - 0.5) * 10;
positions[i3 + 1] = (Math.random() - 0.5) * 10;
positions[i3 + 2] = (Math.random() - 0.5) * 10;
// تهيئة السرعات
velocities.push({
x: (Math.random() - 0.5) * 0.02,
y: (Math.random() - 0.5) * 0.02,
z: (Math.random() - 0.5) * 0.02
});
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// حلقة الرسم
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta(); // الوقت المنقضي منذ الإطار السابق
// تحديث مواقع الجسيمات
const positions = particlesGeometry.attributes.position.array;
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// تحديث المواقع حسب السرعة
positions[i3] += velocities[i].x;
positions[i3 + 1] += velocities[i].y;
positions[i3 + 2] += velocities[i].z;
// إعادة الجسيمات التي خرجت من الحدود
if (positions[i3] > 5 || positions[i3] < -5) velocities[i].x *= -1;
if (positions[i3 + 1] > 5 || positions[i3 + 1] < -5) velocities[i].y *= -1;
if (positions[i3 + 2] > 5 || positions[i3 + 2] < -5) velocities[i].z *= -1;
}
// يجب تعيين هذه العلامة لإخبار Three.js بتحديث الهندسة
particlesGeometry.attributes.position.needsUpdate = true;
renderer.render(scene, camera);
}
ألوان الجسيمات المتعددة
يمكن إضافة ألوان فردية لكل جسيم:
// إضافة ألوان متعددة للجسيمات
const particleCount = 5000;
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
const i3 = i * 3;
// تهيئة المواقع
positions[i3] = (Math.random() - 0.5) * 10;
positions[i3 + 1] = (Math.random() - 0.5) * 10;
positions[i3 + 2] = (Math.random() - 0.5) * 10;
// تهيئة الألوان (RGB، كل قيمة بين 0 و 1)
colors[i3] = Math.random(); // R
colors[i3 + 1] = Math.random(); // G
colors[i3 + 2] = Math.random(); // B
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particlesGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
// مادة تدعم الألوان الفردية
const particlesMaterial = new THREE.PointsMaterial({
size: 0.1,
sizeAttenuation: true,
transparent: true,
alphaMap: particleTexture,
depthWrite: false,
blending: THREE.AdditiveBlending,
vertexColors: true // تمكين الألوان من السمات
});
أنظمة جسيمات متقدمة
لأنظمة الجسيمات الأكثر تعقيدًا، يمكن استخدام مكتبات إضافية:
- GPUComputationRenderer: يستخدم GPU لحساب حركة آلاف الجسيمات بكفاءة.
- ShaderMaterial: يمكن كتابة برامج shader مخصصة للجسيمات.
- مكتبات خارجية: مثل three-nebula أو three-projected-material للجسيمات المتقدمة.
مثال على تأثيرات يمكن تحقيقها باستخدام الجسيمات:
- نظام جسيمات للنار مع ألوان متغيرة ودورة حياة.
- محاكاة الثلج والمطر مع التفاعل مع البيئة.
- تأثيرات الدخان والضباب بدرجات شفافية متفاوتة.
- جسيمات تتحرك بناءً على حقول قوى أو تتبع مسارات منحنية.
تحسين الأداء
تطبيقات الويب ثلاثية الأبعاد يمكن أن تكون مكلفة من حيث موارد الحاسوب. تحسين الأداء ضروري لتوفير تجربة سلسة، خاصة على الأجهزة المحمولة أو الأقل قوة.
مراقبة الأداء
الخطوة الأولى في التحسين هي قياس الأداء الحالي:
// استخدام Stats.js لمراقبة معدل الإطارات
// import Stats from 'three/examples/jsm/libs/stats.module.js';
const stats = new Stats();
document.body.appendChild(stats.dom);
// إضافة إلى حلقة الرسم
function animate() {
requestAnimationFrame(animate);
// تحديث الإحصائيات
stats.update();
// رسم المشهد
renderer.render(scene, camera);
}
تقنيات تحسين الهندسة
// 1. تقليل عدد المضلعات
// استخدام هندسة أقل تعقيدًا
const lowPolySphere = new THREE.SphereGeometry(1, 8, 8); // بدلاً من 32, 32
// 2. دمج الهندسة للكائنات المتشابهة
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries([
cube1.geometry, cube2.geometry, cube3.geometry
]);
const mergedMesh = new THREE.Mesh(mergedGeometry, material);
scene.add(mergedMesh);
// وإزالة المكعبات الفردية من المشهد
// 3. استخدام InstancedMesh للنسخ المتعددة من نفس الهندسة
const instanceCount = 1000;
const instancedMesh = new THREE.InstancedMesh(
boxGeometry, material, instanceCount
);
// ضبط موضع وتدوير كل نسخة
const matrix = new THREE.Matrix4();
for (let i = 0; i < instanceCount; i++) {
matrix.setPosition(
(Math.random() - 0.5) * 50,
(Math.random() - 0.5) * 50,
(Math.random() - 0.5) * 50
);
instancedMesh.setMatrixAt(i, matrix);
}
scene.add(instancedMesh);
مزايا InstancedMesh:
- يمكن رسم آلاف الكائنات المتشابهة بكفاءة عالية.
- استدعاء رسم واحد فقط بدلاً من واحد لكل كائن.
- مناسب للأشجار، العشب، النجوم، وأي عناصر متكررة.
تحسين الملمس والمواد
// 1. ضغط الملمس وتقليل أبعادها
// استخدم jpg بدلاً من png عندما لا تكون الشفافية ضرورية
// حدد أحجام ملمس معقولة (512x512 أو 1024x1024)
// 2. إعادة استخدام المواد
const sharedMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const sphere1 = new THREE.Mesh(geometry1, sharedMaterial);
const sphere2 = new THREE.Mesh(geometry2, sharedMaterial);
// 3. استخدام مواد أبسط عندما يكون ذلك ممكنًا
// MeshBasicMaterial أسرع من MeshStandardMaterial
const basicMaterial = new THREE.MeshBasicMaterial({ map: texture });
// 4. تعطيل توليد المصغرات إذا كنت تستخدم THREE.NearestFilter
texture.generateMipmaps = false;
texture.minFilter = THREE.NearestFilter;
تحسين عملية الرسم
// 1. استخدام Frustum Culling لتجاهل الكائنات خارج مجال الرؤية
object.frustumCulled = true; // محددة افتراضيًا
// 2. تقسيم المشهد إلى مناطق (استراتيجية LOD - مستوى التفاصيل)
const lod = new THREE.LOD();
// إضافة نماذج بدقة مختلفة حسب المسافة
lod.addLevel(highDetailModel, 0); // نموذج عالي الدقة للمسافات القريبة
lod.addLevel(mediumDetailModel, 10); // نموذج متوسط الدقة للمسافات المتوسطة
lod.addLevel(lowDetailModel, 50); // نموذج منخفض الدقة للمسافات البعيدة
scene.add(lod);
// 3. تقليل دقة الرسم للأجهزة الضعيفة
function adjustQualityForPerformance() {
if (isLowEndDevice()) {
renderer.setPixelRatio(1);
renderer.setSize(window.innerWidth, window.innerHeight, false);
}
}
// 4. تحديث الظلال فقط عند الحاجة
if (sceneIsStatic) {
renderer.shadowMap.autoUpdate = false;
renderer.shadowMap.needsUpdate = true; // تحديث لمرة واحدة
}
تقنيات متقدمة
// 1. استخدام OffscreenCanvas لنقل عمليات الرسم إلى Worker
// (مدعوم في المتصفحات الحديثة)
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('renderer.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
// 2. تقنيات الرسم المؤجل
// استخدام WebGLRenderTarget لرسم المشهد على نسيج أولاً
const renderTarget = new THREE.WebGLRenderTarget(
window.innerWidth, window.innerHeight
);
// رسم المشهد إلى الهدف
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
// ثم استخدام النسيج في مشهد أبسط
renderer.setRenderTarget(null);
renderer.render(postProcessingScene, postProcessingCamera);
ملخص نصائح الأداء
تحسينات الهندسة:
- استخدم نماذج بعدد مضلعات أقل.
- دمج الهندسة المتشابهة.
- استخدم InstancedMesh للكائنات المتكررة.
- استخدم BufferGeometry دائمًا.
تحسينات المواد والملمس:
- أعد استخدام المواد والملمس.
- ضغط حجم الملمس واستخدام الصيغ المناسبة.
- استخدم مواد أبسط عندما يكون ذلك ممكنًا.
تحسينات الإضاءة:
- قلل عدد مصادر الضوء.
- استخدم ضوء محيطي بدلاً من الضوء الحقيقي عندما يكون ذلك ممكنًا.
- قلل دقة خرائط الظلال وحدِّثها فقط عند الحاجة.
تحسينات عامة:
- استخدم التكيف التلقائي للجودة حسب الجهاز.
- أزل الكائنات غير المستخدمة من الذاكرة (scene.remove و geometry.dispose).
- استخدم رسمًا انتقائيًا لأجزاء المشهد النشطة فقط.
استمر في التعلم!
هذا الدليل يغطي الأساسيات الرئيسية لمكتبة Three.js، لكن هناك الكثير من الميزات والتقنيات المتقدمة لاكتشافها!
المراجع الرسمية
استكشف توثيق Three.js الرسمي ومعرض الأمثلة للتعمق أكثر.
مشاريع عملية
قم بتطبيق ما تعلمته من خلال بناء مشاريع متنوعة، من العارض البسيط إلى التجارب التفاعلية.
استكشاف المزيد
اكتشف تقنيات متقدمة مثل ما بعد المعالجة، محاكاة الفيزياء، والواقع الافتراضي.
تذكر أن أفضل طريقة للتعلم هي التجربة والممارسة المستمرة. استمتع برحلتك في عالم الرسوميات ثلاثية الأبعاد!