{
سأعلمك في هذا الدرس كيفية استخدام ثلاث طرق مختلفة لتصفية النسيج.
يعلمك كيفية استخدام لوحة المفاتيح لتحريك الكائنات في المشهد، ويعلمك أيضًا كيفية تطبيق الإضاءة البسيطة في مشهد OpenGL.
يحتوي هذا الدرس على الكثير من المحتوى إذا كانت لديك أسئلة حول الدروس السابقة، فارجع إليها وراجعها أولاً.
قبل الدخول في الكود الكامن وراء ذلك، من المهم أن يكون لديك فهم جيد للأساسيات.
مازلنا نعدل الكود من الدرس الأول.
الفرق عن السابق هو أنه كلما حدث أي تغيير كبير، سأكتب الكود بالكامل.
علينا أولاً إضافة وحدة SysUtils ووحدة Glux.
}
الاستخدامات
سيسوتيلس,
برنامج,
النوافذ,
رسائل،
جلوكس في "../../GLAUX/Glaux.pas"؛
// تضيف الأسطر التالية متغيرات جديدة.
// نضيف ثلاثة متغيرات منطقية.
// يتتبع متغير الضوء ما إذا كان الضوء قيد التشغيل.
// يتم استخدام المتغيرات lp و fp لتخزين ما إذا تم الضغط على المفتاحين "L" و "F".
// سأشرح أهمية هذه المتغيرات لاحقا. في الوقت الحالي، ضع ذلك جانبًا.
ضوء: منطقي // مصدر الضوء تشغيل/إيقاف
lp : Boolean; // هل تم الضغط على المفتاح L؟
fp : منطقية؛ // هل تم الضغط على المفتاح F؟
// الآن قم بتعيين 5 متغيرات للتحكم في حجم خطوة زاوية الدوران حول المحور السيني والمحور الصادي،
// وسرعة الدوران حول المحور السيني والمحور الصادي.
// بالإضافة إلى ذلك، يتم إنشاء متغير z للتحكم في المسافة إلى عمق الشاشة.
xrot : GLfloat;
yrot : GLfloat؛ // Y التناوب
xspeed : GLfloat؛ // X سرعة الدوران
yspeed : GLfloat؛ // Y سرعة الدوران
z : GLfloat = -5.0 f; // المسافة في عمق الشاشة
// ثم قم بتعيين المصفوفة المستخدمة لإنشاء مصدر الضوء.
// سوف نستخدم مصباحين مختلفين.
// الأول يسمى الضوء المحيط. الضوء المحيط يأتي من جميع الاتجاهات.
// جميع الكائنات الموجودة في المشهد مضاءة بالضوء المحيط.
// النوع الثاني من مصدر الضوء يسمى الضوء المنتشر.
// يتم إنشاء الضوء المنتشر بواسطة مصدر ضوء محدد ويخلق انعكاسات على أسطح الكائنات في المشهد الخاص بك.
// أي سطح كائن مضاء مباشرة بالضوء المنتشر يصبح ساطعًا جدًا،
// المناطق التي بالكاد مضاءة تبدو أكثر قتامة.
// سينتج عن ذلك تأثير ظل جيد جدًا على حواف الصندوق الخشبي الذي أنشأناه.
// عملية إنشاء مصدر الضوء هي نفسها تمامًا عملية إنشاء اللون.
// المعلمات الثلاثة الأولى هي مكونات RGB ثلاثية الألوان، والأخيرة هي معلمة قناة ألفا.
// لذلك، في الكود التالي نحصل على ضوء محيط أبيض نصف ساطع (0.5f).
// إذا لم يكن هناك ضوء محيط، فإن المناطق التي لا يصيبها الضوء المنتشر ستصبح مظلمة للغاية.
LightAmbient: Array[0..3] Of GLfloat = (0.5, 0.5, 0.5, 1.0); // معلمات الإضاءة المحيطة (جديدة)
// السطر التالي من التعليمات البرمجية نقوم بإنشاء ألمع الضوء المنتشر.
// يتم أخذ جميع قيم المعلمات إلى الحد الأقصى لقيمة 1.0f.
// سوف يلمع على مقدمة صندوقنا الخشبي وسيبدو جيدًا.
LightDiffuse: Array[0..3] Of GLfloat = (1.0, 1.0, 1.0, 1.0); // معلمات الضوء المنتشرة (جديد)
// أخيرًا نحفظ موضع مصدر الضوء.
// المعلمات الثلاثة الأولى هي نفسها الموجودة في glTranslate.
// الإزاحات على محور XYZ هي على التوالي.
// نظرًا لأننا نريد أن يسلط الضوء مباشرة على مقدمة الصندوق الخشبي، فإن الإزاحات على المحور XY تكون 0.0.
// القيمة الثالثة هي الإزاحة على المحور Z.
// للتأكد من أن الضوء موجود دائمًا أمام الصندوق الخشبي،
// لذلك نقوم بتحريك مصدر الضوء بعيدًا عن الشاشة باتجاه المراقب (وهو أنت).
// نطلق عادةً على موضع الشاشة، أي زجاج الشاشة الخاص بالشاشة، نقطة 0.0 للمحور Z.
// لذا تم ضبط الإزاحة على المحور Z أخيرًا على 2.0.
// إذا كان بإمكانك رؤية مصدر الضوء، فإنه يطفو أمام شاشتك.
//بالطبع، لا يمكنك رؤية الصندوق إذا لم يكن خلف زجاج شاشة الشاشة.
//『ملاحظة المترجم: أنا أقدر صبر NeHe.
// لأكون صادقًا، أحيانًا أشعر بالانزعاج، لماذا يتحدث بهذا القدر من الهراء عن شيء بسيط كهذا؟
// ولكن إذا كان كل شيء واضحًا، فهل ستظل تقلب صفحات كهذه وتقرأ إلى ما لا نهاية؟ 』
// يتم أخذ المعلمة الأخيرة على أنها 1.0f.
// سيُعلم هذا برنامج OpenGL أن الإحداثيات المحددة هنا هي مواضع مصدر الضوء، وسأشرح المزيد في البرامج التعليمية المستقبلية.
LightPosition: Array[0..3] Of GLfloat = (0.0, 0.0, 2.0, 1.0); // موضع مصدر الضوء (جديد)
// يتتبع متغير المرشح نوع النسيج المستخدم عند العرض.
// تم إنشاء الملمس الأول (الملمس 0) باستخدام طريقة التصفية gl_nearest (غير السلس).
// يستخدم الملمس الثاني (الملمس 1) طريقة gl_linear (التصفية الخطية)،
// الصورة الأقرب إلى الشاشة تبدو أكثر سلاسة.
// يستخدم الملمس الثالث (الملمس 2) طريقة التصفية mipmapped،
// سيؤدي هذا إلى إنشاء نسيج جميل المظهر.
// اعتمادًا على نوع الاستخدام لدينا، فإن قيمة متغير المرشح تساوي 0 أو 1 أو 2 على التوالي.
// لنبدأ بالملمس الأول.
// الملمس يخصص مساحة تخزين لثلاثة مواد مختلفة.
// وهي موجودة في الملمس[0] والملمس[1] والملمس[2] على التوالي.
عامل التصفية: GLuint؛ // نوع المرشح
نسيج: صفيف [0..2] من GLuint؛ // مساحة تخزين لثلاث مواد
PROcedure glGenTextures(n: GLsizei; var contexts: GLuint);
opengl32;
الإجراء glBindTexture(target: GLenum;texture: GLuint);
opengl32;
وظيفة gluBuild2DMipmaps (الهدف: GLenum؛ المكونات، العرض، الارتفاع: GLint؛
التنسيق، النوع: GLenum؛ البيانات: المؤشر): عدد صحيح؛ اسم glu32 الخارجي؛
'gluBuild2DMipmaps';
{
الآن قم بتحميل صورة نقطية واستخدمها لإنشاء ثلاثة مواد مختلفة.
يستخدم هذا الدرس مكتبة glaux المساعدة لتحميل الصور النقطية.
لذلك يجب عليك التأكد من تضمين مكتبة glaux عند التجميع.
أعلم أن كلاً من دلفي وVC++ يشتملان على مكتبة glaux، لكن اللغات الأخرى ليست مضمونة بوجودها.
"ملاحظة المترجم: glaux هي مكتبة مساعدة لـ OpenGL. وفقًا لخصائص الأنظمة الأساسية المشتركة لـ OpenGL،
يجب أن يكون الرمز مشتركًا عبر جميع الأنظمة الأساسية. لكن المكتبة المساعدة ليست مكتبة OpenGL القياسية الرسمية.
غير متوفر على جميع المنصات. ولكن يحدث أن يكون متاحًا على النظام الأساسي Win32.
هاها، بالطبع BCB لا توجد مشكلة أيضا. 』هنا أقوم فقط بالتعليق على الكود المضاف حديثًا.
إذا كانت لديك أسئلة حول سطر معين من التعليمات البرمجية، فيرجى مراجعة البرنامج التعليمي 6.
يشرح هذا الدرس تحميل وإنشاء الأنسجة بتفصيل كبير.
بعد الجزء السابق من الكود وقبل glResizeWnd ()،
أضفنا الكود التالي. وهذا مطابق تقريبًا للكود المستخدم لتحميل الصورة النقطية في الدرس 6.
}
الوظيفة LoadBmp(اسم الملف: pchar): PTAUX_RGBImageRec;
فار
BitmapFile: Thandle؛
يبدأ
إذا كان اسم الملف = ''، فتأكد من توفير اسم الملف.
النتيجة := لا شيء // إذا لم يتم توفيره، قم بإرجاع NULL
BitmapFile := FileOpen(Filename, fmOpenWrite); // حاول فتح الملف
إذا كان BitmapFile > 0 إذن // هل الملف موجود؟
يبدأ
FileClose(BitmapFile); // إغلاق المقبض
النتيجة := auxDIBImageLoadA(filename); // قم بتحميل الصورة النقطية وأرجع المؤشر
نهاية
آخر
النتيجة:= لا شيء // إذا فشل التحميل، قم بإرجاع NiL.
نهاية؛
الوظيفة LoadTexture: boolean; // قم بتحميل الصورة النقطية وتحويلها إلى نسيج
فار
الحالة: منطقية؛
TextureImage : Array[0..1] Of PTAUX_RGBImageRec;
يبدأ
الحالة := خطأ؛
ZeroMemory(@TextureImage, sizeof(TextureImage)); // اضبط المؤشر على NULL
TextureImage[0] := LoadBMP('Walls.bmp');
إذا TextureImage[0] <> Nil إذن
يبدأ
الحالة := TRUE؛ // اضبط الحالة على TRUE
glGenTextures(1, الملمس[0]); // إنشاء نسيج
// في الدرس السادس، استخدمنا تخطيط النسيج المُصفى الخطيًا.
// يتطلب هذا قدرًا كبيرًا إلى حد ما من قوة المعالجة من الجهاز، لكنها تبدو جيدة جدًا.
// في هذا الدرس، يستخدم النسيج الأول الذي سنقوم بإنشائه طريقة GL_NEAREST.
// من حيث المبدأ، لا تقوم هذه الطريقة بالتصفية فعليًا.
// تستهلك طاقة معالجة قليلة جدًا وتبدو سيئة.
// الميزة الوحيدة هي أن مشروعنا يمكن أن يعمل بشكل طبيعي على كل من الأجهزة السريعة والبطيئة.
// ستلاحظ أننا نستخدم GL_NEAREST لكل من MIN وMAG،
//يمكنك مزج GL_NEAREST وGL_LINEAR.
// سيبدو النسيج أفضل، لكننا نهتم بالسرعة أكثر، لذلك نستخدم جميع الخرائط منخفضة الجودة.
يتم استخدام //MIN_FILTER عندما يتم رسم الصورة بحجم أصغر من الحجم الأصلي للنسيج.
يتم استخدام //MAG_FILTER عندما يتم رسم الصورة بحجم أكبر من الحجم الأصلي للنسيج.
// إنشاء أقرب خريطة مرشح
glBindTexture(GL_TEXTURE_2D, الملمس[0]);
// توليد الملمس
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,
TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[0].data);
// المادة التالية هي نفسها الموجودة في الدرس السادس، التصفية الخطية. والفرق الوحيد هو أنه تم وضعه هذه المرة
// الملمس [1]. لأن هذا هو الملمس الثاني. إذا وضعت
//texture[0]، فإنه سيحل محل نسيج GL_NEAREST الذي تم إنشاؤه مسبقًا.
glBindTexture(GL_TEXTURE_2D, الملمس[1]); // استخدم نسيجًا نموذجيًا تم إنشاؤه من بيانات الصورة النقطية
// توليد الملمس
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,
TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[0].data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // التصفية الخطية
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // التصفية الخطية
// فيما يلي طريقة جديدة لإنشاء القوام. رسم الخرائط!
//『ملاحظة المترجم: لا أستطيع ترجمة هذه الكلمة باللغة الصينية، لكن هذا لا يهم. بعد قراءة هذه الفقرة ستعرف أن المعنى هو الأهم. 』
// قد تلاحظ أنه عندما تصبح الصورة صغيرة جدًا على الشاشة، يتم فقدان الكثير من التفاصيل.
// النمط الذي بدا جيدًا الآن أصبح قبيحًا. عندما تطلب من OpenGL إنشاء نسيج mipmapped،
// سيحاول OpenGL إنشاء مواد عالية الجودة بأحجام مختلفة. عندما تقوم برسم نسيج mipmapped على الشاشة،
// سيختار OpenGL المظهر الأفضل (بمزيد من التفاصيل) الذي قام بإنشائه للاعتماد عليه،
// بدلاً من مجرد تغيير حجم الصورة الأصلية (مما سيؤدي إلى فقدان التفاصيل).
// قلت ذات مرة أن هناك طرقًا للتغلب على القيود التي يضعها OpenGL على عرض النسيج وارتفاعه - 64، 128، 256، إلخ.
// الحل هو gluBuild2DMipmaps. مما وجدته، يمكنك استخدام الصور النقطية العشوائية لإنشاء الأنسجة.
// سيقوم OpenGL بتحجيمه تلقائيًا إلى الحجم الطبيعي.
// نظرًا لأنه الملمس الثالث، فإننا نحفظه في الملمس[2]. بهذه الطريقة، تم إنشاء جميع الأنسجة الثلاثة في هذا الدرس.
// إنشاء نسيج MipMapped
glBindTexture(GL_TEXTURE_2D, الملمس[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST // (جديد) ؛
// يقوم السطر التالي بإنشاء نسيج mipmapped.
// نستخدم ثلاثة ألوان (الأحمر والأخضر والأزرق) لإنشاء نسيج ثنائي الأبعاد.
//TextureImage[0].sizeX هو عرض الصورة النقطية،
//TextureImage[0].sizeY هو ارتفاع الصورة النقطية،
//(==== لسبب ما، هذه الوظيفة في دلفي لا تحتوي على معلمة الارتفاع،
// ولكن هناك مساعدة لا أعرف ما الذي ستفعله دلفي بعد الآن، مما يجعلني أشعر بالاكتئاب...
// أخيرًا، كتبت gluBuild2DMipmaps بنفسي سابقًا،
// لتحميل وظيفة gluBuild2DMipmaps في glu32.dll =====)
//GL_RGB يعني أننا نستخدم ألوان RGB بدورها.
//GL_UNSIGNED_BYTE يعني أن وحدة بيانات النسيج هي بايت.
تشير البيانات //TextureImage[0].data إلى الصورة النقطية التي نستخدمها لإنشاء النسيج.
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0].sizeX,
TextureImage[0].sizey، GL_RGB، GL_UNSIGNED_BYTE،
TextureImage[0].data); //(جديد) }
نهاية؛
إذا تم تعيينه (TextureImage[0]) ثم // ما إذا كان النسيج موجودًا أم لا
إذا تم تعيينه (TextureImage[0].data) ثم // ما إذا كانت صورة النسيج موجودة أم لا
TextureImage[0].data := Nil; // حرر الذاكرة التي تشغلها صورة النسيج
TextureImage[0] := Nil; // حرر بنية الصورة
النتيجة: = الحالة // حالة العودة
نهاية؛
// ثم حان وقت تحميل النسيج وتهيئة إعدادات OpenGL.
// يستخدم السطر الأول من وظيفة GLInit الكود أعلاه لتحميل النسيج.
// بعد إنشاء النسيج، نسمي glEnable(GL_TEXTURE_2D) لتمكين تعيين النسيج ثنائي الأبعاد.
// تم ضبط وضع الظل على التظليل السلس (التظليل السلس).
// تم ضبط لون الخلفية على اللون الأسود، ونقوم بتمكين اختبار العمق، ثم نقوم بتمكين حسابات المنظور المحسنة.
الإجراء glInit(); // ابدأ جميع إعدادات OpenGL هنا
يبدأ
إذا (لم يتم تحميل النص) ثم // اتصل بالروتين الفرعي لتحميل النسيج
خروج؛ // إذا فشل التحميل، قم بالخروج
glEnable(GL_TEXTURE_2D); // تمكين تعيين النسيج
glShadeModel(GL_SMOOTH); // تمكين تجانس الظل
glClearColor(0.0, 0.0, 0.0, 0.0); // خلفية سوداء
glClearDepth(1.0); // تعيين المخزن المؤقت للعمق
glEnable(GL_DEPTH_TEST); // تمكين اختبار العمق
glDepthFunc(GL_LESS); // نوع اختبار العمق الذي تم إجراؤه
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // حساب إسقاط المنظور الأمثل للغاية
// ابدأ الآن في إعداد مصدر الضوء. السطر التالي أدناه يحدد كمية الضوء المحيط المنبعث،
// يبدأ مصدر الضوء light1 في إصدار الضوء.
// في بداية هذا الدرس، نقوم بتخزين كمية الضوء المحيط في مصفوفة LightAmbient.
// الآن سوف نستخدم هذه المصفوفة (الضوء المحيط بنصف السطوع).
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient[0]); // ضبط الإضاءة المحيطة
// بعد ذلك نقوم بتعيين كمية الضوء المنتشر. يتم تخزينه في مجموعة LightDiffuse (الضوء الأبيض الكامل السطوع).
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse[0]); // ضبط الضوء المنتشر
// ثم اضبط موضع مصدر الضوء.
// يتم تخزين الموضع في مصفوفة LightPosition
//(يقع بالضبط في منتصف مقدمة الصندوق الخشبي، X-0.0، Y-0.0، تحرك وحدتين باتجاه المراقب في الاتجاه Z <خارج الشاشة>).
glLightfv(GL_LIGHT1, GL_POSITION, @LightPosition); // موضع مصدر الضوء
// أخيرًا، قمنا بتمكين مصدر الضوء رقم واحد. لم نقم بتمكين GL_LIGHTING بعد،
// لذلك لا يمكنك رؤية أي ضوء.
// تذكر: لن ينجح مجرد إعداد مصدر الضوء أو تحديد موضعه أو حتى تمكينه.
// ما لم نقوم بتمكين GL_LIGHTING.
glEnable(GL_LIGHT1); // تمكين مصدر الضوء رقم 1
نهاية؛
// الجزء التالي من التعليمات البرمجية يرسم المكعب المزخرف. أنا فقط أقوم بتعليق الكود الجديد.
//إذا كانت لديك أسئلة حول التعليمات البرمجية بدون تعليقات توضيحية، فارجع إلى الدرس 6.
الإجراء glDraw(); // يبدأ كل الرسم من هنا
يبدأ
glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // مسح الشاشة والمخزن المؤقت للعمق
glLoadIdentity(); // إعادة تعيين مصفوفة مراقبة النموذج الحالي
// الأسطر الثلاثة التالية من التعليمات البرمجية تضع مكعب النسيج وتقوم بتدويره.
//glTranslatef(0.0,0.0,z) يحرك وحدات المكعب Z على طول المحور Z.
//glRotatef(xrot,1.0f,0.0f,0.0f) يقوم بتدوير المكعب حول المحور X xrot.
//glRotatef(yrot,0.0f,1.0f,0.0f) يقوم بتدوير المكعب حول المحور Y.
glTranslatef(0.0, 0.0, z); // نقل داخل/خارج وحدات الشاشة z
glRotatef(xrot, 1.0, 0.0, 0.0); // التدوير حول المحور X
glRotatef(yrot, 0.0, 1.0, 0.0); // التدوير حول المحور Y
// السطر التالي مشابه لما فعلناه في الدرس السادس.
// الفرق هو أن النسيج الذي نربطه هذه المرة هو نسيج[فلتر]،
// بدلاً من الملمس[0] في الدرس السابق.
// في أي وقت نضغط فيه على المفتاح F، ستزداد قيمة الفلتر.
// إذا كانت هذه القيمة أكبر من 2، فسيتم إعادة تعيين مرشح المتغير إلى 0.
// عند تهيئة البرنامج، سيتم أيضًا تعيين قيمة مرشح المتغير على 0.
// باستخدام مرشح المتغير يمكننا تحديد أي من القوام الثلاثة.
glBindTexture(GL_TEXTURE_2D, Texture[filter]); // حدد النسيج الذي يحدده المرشح
glBegin(GL_QUADS); // ابدأ في رسم الأشكال الرباعية
//glNormal3f جديد في هذا الدرس. عادي يعني عادي
// يشير ما يسمى بالعادي إلى خط مستقيم يمر عبر نقطة على سطح ما (مضلع) ويكون عموديًا على هذا السطح (مضلع).
// عند استخدام مصدر الضوء، يجب تحديد عادي. يخبر المستوى الطبيعي برنامج OpenGL باتجاه المضلع ويشير إلى الجانبين الأمامي والخلفي للمضلع.
// إذا لم يتم تحديد القيم الطبيعية، فقد تحدث أشياء غريبة: تتم إضاءة الأسطح التي لا ينبغي إضاءتها، كما تتم إضاءة الجانب الخلفي من المضلع....
// بالمناسبة، يجب أن يشير العمودي إلى خارج المضلع. بالنظر إلى الجزء الأمامي من الصندوق ستلاحظ أن العمودي في نفس اتجاه المحور Z الموجب.
// هذا يعني أن الطبيعي يشير نحو المراقب - أنت. وهذا بالضبط ما نأمله.
// بالنسبة للجزء الخلفي من الصندوق الخشبي، تمامًا كما نريد، يكون الوضع الطبيعي بعيدًا عن المشاهد.
// إذا تم تدوير المكعب 180 درجة على طول المحور X أو Y، فإن الطبيعي على الجانب الأمامي لا يزال يواجه المراقب، والطبيعي على الجانب الخلفي لا يزال بعيدًا عن المراقب.
// بمعنى آخر، بغض النظر عن السطح، طالما أنه يواجه المراقب، فإن الوضع الطبيعي لهذا السطح يشير إلى المراقب.
// نظرًا لأن مصدر الضوء مجاور للمراقب مباشرةً، فسيتم إضاءة هذا السطح في أي وقت يواجه فيه الوضع الطبيعي المراقب.
// وكلما اقترب الوضع الطبيعي من مصدر الضوء، كلما ظهر أكثر سطوعًا.
// إذا وضعت نقطة المراقبة داخل المكعب، فسوف تحصل على الظلام داخل الوضع الطبيعي.
// لأن النقاط العادية إلى الخارج. إذا لم يكن هناك مصدر للضوء داخل المكعب، فسيكون لونه أسودًا بالطبع.
// أمام
glNormal3f(0.0, 0.0, 1.0); // نقاط عادية للمراقب
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, -1.0, 1.0); // الملمس والجزء السفلي الأيسر من الرباعية
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, -1.0, 1.0); // الملمس والجزء السفلي الأيمن من الرباعية
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, 1.0); // الملمس والجزء العلوي الأيمن من الرباعية
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0); // الملمس وأعلى يسار الرباعي
// لاحقاً
glNormal3f(0.0, 0.0, -1.0); // الوجوه العادية بعيدة عن العارض
glTexCoord2f(1.0, 0.0);
glVertex3f(-1.0, -1.0, -1.0); // الملمس والجزء السفلي الأيمن من الرباعية
glTexCoord2f(1.0, 1.0);
glVertex3f(-1.0, 1.0, -1.0); // الملمس والجزء العلوي الأيمن من الرباعية
glTexCoord2f(0.0, 1.0);
glVertex3f(1.0, 1.0, -1.0); // الملمس والجزء العلوي الأيسر من الرباعية
glTexCoord2f(0.0, 0.0);
glVertex3f(1.0, -1.0, -1.0); // الملمس والجزء السفلي الأيسر من الرباعية
// السطح العلوي
glNormal3f(0.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, -1.0); // الملمس وأعلى يسار الرباعي
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, 1.0, 1.0); // الملمس والجزء السفلي الأيسر من الرباعية
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, 1.0, 1.0); // الملمس والجزء السفلي الأيمن من الرباعية
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, -1.0); // الملمس والجزء العلوي الأيمن من الرباعية
// قاع
glNormal3f(0.0, -1.0, 0.0); // عادي متجهًا للأسفل
glTexCoord2f(1.0, 1.0);
glVertex3f(-1.0, -1.0, -1.0); // الملمس والجزء العلوي الأيمن من الرباعية
glTexCoord2f(0.0, 1.0);
glVertex3f(1.0, -1.0, -1.0); // الملمس وأعلى يسار الرباعي
glTexCoord2f(0.0, 0.0);
glVertex3f(1.0, -1.0, 1.0); // الملمس والجزء السفلي الأيسر من الرباعية
glTexCoord2f(1.0, 0.0);
glVertex3f(-1.0, -1.0, 1.0); // الملمس والجزء السفلي الأيمن من الرباعية
// يمين
glNormal3f(1.0, 0.0, 0.0); // عادي إلى اليمين
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, -1.0, -1.0); // الملمس والجزء السفلي الأيمن من الرباعية
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, -1.0); // الملمس والجزء العلوي الأيمن من الرباعية
glTexCoord2f(0.0, 1.0);
glVertex3f(1.0, 1.0, 1.0); // الملمس والجزء العلوي الأيسر من الرباعية
glTexCoord2f(0.0, 0.0);
glVertex3f(1.0, -1.0, 1.0); // الملمس والجزء السفلي الأيسر من الرباعية
// غادر
glNormal3f(-1.0, 0.0, 0.0); // عادي إلى اليسار
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, -1.0, -1.0); // الملمس والجزء السفلي الأيسر من الرباعي
glTexCoord2f(1.0, 0.0);
glVertex3f(-1.0, -1.0, 1.0); // الملمس والجزء السفلي الأيمن من الرباعية
glTexCoord2f(1.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0); // الملمس والجزء العلوي الأيمن من الرباعية
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, -1.0); // الملمس وأعلى يسار الرباعي
glEnd();
xrot := xrot + xspeed; // xrot يزيد من وحدات xspeed
yrot := Yrot + yspeed; // yrot يزيد من وحدات yspeed
نهاية؛
// انتقل الآن إلى وظيفة WinMain () الرئيسية.
// سنضيف رمز تحكم هنا لتشغيل وإيقاف مصدر الضوء، وتدوير الصندوق الخشبي، وتبديل طرق التصفية، وتحريك الصندوق الخشبي أقرب وأبعد.
// بالقرب من نهاية وظيفة WinMain () ستشاهد سطر التعليمات البرمجية SwapBuffers(hDC).
// ثم أضف الكود التالي بعد هذا السطر.
// سيتحقق الرمز مما إذا تم الضغط على المفتاح L.
// إذا تم الضغط على المفتاح L، ولكن قيمة lp ليست خاطئة، فهذا يعني أنه لم يتم تحرير المفتاح L، ولن يحدث شيء في هذا الوقت.
SwapBuffers(h_DC); // مبادلة المخزن المؤقت (مخزن مؤقت مزدوج)
إذا (المفاتيح[ord('L')] وليس lp) إذن
يبدأ
// إذا كانت قيمة lp خاطئة،
// يعني أنه لم يتم الضغط على المفتاح L بعد، أو تم تحريره، فسيتم تعيين lp على TRUE.
// سبب التحقق من هذين الشرطين في نفس الوقت هو منع الضغط على المفتاح L.
// يتم تنفيذ هذا الرمز بشكل متكرر ويتسبب في وميض النموذج بشكل مستمر.
// بعد ضبط lp على true، سيعرف الكمبيوتر أنه تم الضغط على المفتاح L.
// يمكننا تشغيل / إيقاف مصدر الضوء وفقًا لذلك: يتحكم الضوء المتغير المنطقي في تشغيل / إيقاف مصدر الضوء.
lp := true; // تم ضبط lp على TRUE
ضوء := ليس ضوءا // قم بتبديل مصدر الضوء إلى TRUE/FALSE
إذا لم يكن الضوء ثم // إذا لم يكن هناك مصدر للضوء
glDisable(GL_LIGHTING) // تعطيل مصدر الضوء
آخر // آخرون
glEnable(GL_LIGHTING); // تمكين مصدر الضوء
نهاية؛
إذا لم تكن المفاتيح [ord('L')] إذن // هل تم تحرير المفتاح L؟
lp := FALSE; // إذا كانت الإجابة بنعم، فاضبط lp على FALSE
// ثم قم بإجراء فحص مماثل للمفتاح "F".
// إذا تم الضغط على المفتاح "F" ولم يتم الضغط على المفتاح "F" أو لم يتم الضغط عليه مطلقًا،
// اضبط المتغير fp على صحيح. هذا يعني أنه يتم الضغط على المفتاح.
// ثم أضف واحدًا إلى متغير التصفية. إذا كان متغير التصفية أكبر من 2
//(لأن المصفوفة التي نستخدمها هنا هي نسيج[3]، والأنسجة الأكبر من 2 غير موجودة)،
// نقوم بإعادة تعيين متغير المرشح إلى 0.
إذا (keys[ord('F')] وليس fp) إذن // هل يتم الضغط على المفتاح F؟
يبدأ
fp := TRUE; // تم ضبط fp على TRUE
inc(filter); // أضف واحدًا إلى قيمة الفلتر
إذا كان المرشح > 2 إذن // هل هو أكبر من 2؟
عامل التصفية := 0; // إذا تمت إعادة التعيين إلى 0
نهاية؛
إذا لم تكن المفاتيح [ord('F')] إذن // هل تم تحرير المفتاح F؟
fp := FALSE; // إذا تم ضبط fp على FALSE
// تتحقق هذه الأسطر الأربعة من الضغط على مفتاح PageUp. إذا كان الأمر كذلك، قم بتقليل قيمة المتغير z. بهذه الطريقة فإن استدعاء glTranslatef(0.0f,0.0f,z) المضمن في وظيفة DrawGLScene سيحرك الصندوق الخشبي بعيدًا عن العارض.
إذا كانت المفاتيح [VK_PRIOR] ثم تم الضغط على //PageUp؟
z := z - 0.02; // في حالة الضغط عليه، قم بتحريك الصندوق الخشبي باتجاه داخل الشاشة.
// تتحقق الأسطر الأربعة التالية من الضغط على مفتاح PageDown. إذا كان الأمر كذلك، قم بزيادة قيمة المتغير z. بهذه الطريقة، فإن استدعاء glTranslatef(0.0f,0.0f,z) المضمن في الدالة DrawGLScene سيحرك الصندوق الخشبي بالقرب من المراقب.
إذا كانت المفاتيح [VK_NEXT] إذن // هل يتم الضغط على PageDown؟
z := z + 0.02; // إذا تم الضغط عليه، حرك الصندوق الخشبي نحو المراقب.
// الآن تحقق من مفاتيح الأسهم. اضغط على مفتاحي الاتجاه الأيسر والأيمن لتقليل سرعة x أو زيادتها وفقًا لذلك.
// اضغط على مفاتيح الاتجاه لأعلى ولأسفل لتقليل أو زيادة yspeed وفقًا لذلك.
// تذكر أنه في البرامج التعليمية المستقبلية إذا زادت قيم xspeed وyspeed، فسيتم تدوير المكعب بشكل أسرع.
// إذا واصلت الضغط على مفتاح اتجاه معين، فسوف يدور المكعب بشكل أسرع في هذا الاتجاه.
إذا كانت المفاتيح [VK_UP] إذن // هل يتم الضغط على مفتاح الاتجاه لأعلى؟
xspeed := xspeed - 0.01; // إذا كانت الإجابة بنعم، فقم بتقليل xspeed
إذا كانت المفاتيح [VK_DOWN] إذن // هل تم الضغط على مفتاح الاتجاه لأسفل؟
xspeed := xspeed + 0.01; // إذا كانت الإجابة بنعم، فقم بزيادة xspeed
إذا كانت المفاتيح [VK_RIGHT] إذن // هل تم الضغط على مفتاح الاتجاه الصحيح؟
yspeed := yspeed + 0.01; //إذا كانت الإجابة بنعم، قم بزيادة yspeed
إذا كانت المفاتيح [VK_LEFT] إذن // هل يتم الضغط على مفتاح الاتجاه الأيسر؟
yspeed := yspeed - 0.01; //إذا كانت الإجابة بنعم، فقم بتقليل yspeed
إذا (مفاتيح [VK_ESCAPE]) ثم // إذا تم الضغط على مفتاح ESC
انتهى := صحيح
تشغيله ورؤية التأثير