8.7 مكتبة openGl و GLUT | كتاب لينكس الشامل | >> |
تعتبر مكتبة openGL أفضل مكتبة للرسم ثلاثي الأبعاد عالي الدقة خصوصا للرسومات المعدة مسبقا والآن مع تطور بطاقات العرض وسرعة الأجهزة بدأت تطرح أيضا كمكتبة للرسم التفاعلي هذا اضافة لكونها مكتبة مفتوحة وتعمل على أكثر من منصة cross-platform، ولكنها مكتبة تفتقر لوظائف تتبع لوحة المفاتيح وعمل نوافذ وقوائم وطباعة نصوص (لأن هذا خارج أهداف تصميمها) لهذا تستخدم م مكتبة أخرى للقيام بذلك مثل اكس (في لينكس) أو GDI في ويندوز ولكن هذا يجعل جزء من الكود يجب أن يكتب لكل نظام تشغيل على حداً وهذا مخالف لأهداف تصميم openGL.
لهذا جاءت glut وهي مكتبة cross-platform أيضا تقوم بالتعامل مع النوافذ ولوحة المفاتيح والفأرة ... إلخ وبهذا يكون البرنامج كاملا cross-platform.
يبدأ البرنامج ب
#include<GL/glut.h>
و
#include<GL/gl.h>
ويمكنك أيضا إضافة (إذا كنت تنوي استعمالها)
#include<GL/glu.h >
ويجب أن تنتبه إلى أن الاسم الدليل GL بالحروف الكبيرة
ويجب إضافة هذه المكتبات برنامج عند تصنيف البرنامج
وذلك باضافة الخيارات التالية إلى خيارات الربط
-lopengl -lglu -lglut
في سطر الأوامر بعد gcc أو g++
أو حتى في linking options إذا كنت تعمل من بيئة تطور متكاملة
وبهذا تصبح ملفاتك مرتبطة بالمكتبات
libopengl.so libglu.so libglut.so
أو في ويندوز مع تطبيق SGI للمكتبة opengl.dll glu.dll glut.dll
اذا كنت تستخدم تعريفات من شركة nVidia انتبه هناك خطأ شائع بأن يكون الربط مع مكتبتهم بدل المكتبة القياسية
في نظام ويندوز هناك نوعان من هذه المكتبات
النوع الأول من الشركة الواضعة للمشروع sgi
وأخرى من مايكروسوفت
الخيارات التي ذكرناها هي لإصدار sgi أما خيارات
مايكروسوفت هي
-lopengl32 -lglu32 -lglut32
وتعتمد ملفات
opengl32.dll glu32.dll glut32.dll
بدلا من
opengl.dll glu.dll glut.dll
لتسهيل تذكر الأسماء الأساسية
اعلم أن الثوابت تكون كلها كبيرة يفصل بين كلماتها _ وتبدأ باسم المكتبة
مثل GLUT_RGB
وأن أسماء الوظائف تبدأ باسم المكتبة بحروف صغيرة
ثم بأحرف استهلالية كبيرة ولايفصل بينها _ مثل
glutInitWindowSize
ويلحق بها أحيانا البعد الذي تتعامل فيع
(اما 2 أو 3 أو 4)
ونوعه
(اما صحيح i كسري أو f أو بضعف الدقة d)
مثلا
glVertex2i(int x,int y)
اما إذا كنا سنضع مؤشر على عنوان الإحداثيات وليس الإحداثيات نفسها نضيف
v مثلا
glVertex3fv(float *fptr)
حيث fptr هي مؤشر إلى مصفوفة array بها ثلاث عناصر x و yو z
float fptr[3]={x,y,z}
أما الأنواع فهي
تبدأ باسم المكتبة بأحرف كبيرة ثم الباقي بأحرف صغيرة مثلا
GLfloat
يبدأ البرنامج بتهيئة مكتبة glut ب
glutInit( &argc, argv );
ثم بتحديد طور العرض مثلا
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH );
لتحديد GLUT_RGBA بدلا من GLUT_INDEX
الأولى تعني ألوان حقيقية والأخرى تعني ألوان محدودة ومفهرسة
لتحديد GLUT_DOUBLE بدلا من GLUT_SINGLE
في الأولى كل ما ترسمه ينقل إلى الشاشة
مباشرة فتبدو الشاشة وكأنها ترمش
وفي الثانية ترسم في ذاكرة مؤقتة buffer
أولا ثم تظهر دفعة واحدة على الشاشة عند الرغبة باستدعاء
glutSwapBuffers();
أما GLUT_DEPTH فهي تستخدم تقنية zbuffer لكي
تخفي أجزاء من الرسم التي التي يوجد شيء أمامها
وهي ضرورية عند الرسم ثلاثي الأبعاء،
ولتشغيل هذه الطريقة عليك استدعاء
glEnable(GL_DEPTH_TEST);
في مكان ما قبل بدء الرسم.
بعد ذلك يأتي
glutDisplayFunc( my_display );
حيث
my_display
هي الوظيفة التي تقوم أنت بكتابتها لتقوم برسم الأشياء التي تريد.
ثم يأتي دور
glutMainLoop();
التي تنتظر حتى تغلق النافذة
أو ينتهي البرنامج
// include the GL lib's #include<GL/gl.h> #include<GL/glu.h> #include<GL/glut.h> void mydisplay() { // drawing calls goes here glClearColor(1.0,1.0,1.0,1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); glutSwapBuffers(); } int main( int argc,char *argv[]) { glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowPosition(0,0); glutInitWindowSize(320,240); glutCreateWindow("My 1st openGL"); glutDisplayFunc( mydisplay ); glutMainLoop(); // return success return 0; }
هذا البرنامج يظهر نافذة بحجم
320x240
مووضوعة في الزاوية اليسرى العليا للشاشة
وقد حددنا هذا ب
glutInitWindowPosition(0,0); glutInitWindowSize(320,240);
وفتحنا نافذة بهذه المواصفات تحمل عنوان My 1st openGL ب
glutCreateWindow
لا تحتوي سوى على مساحة بيضاء وذلك بتحديد لون المسح إلى أبيض
glClearColor(1.0,1.0,1.0,1.0);
ثم مسح الذاكرة المؤقتة بذلك اللون ب
glClear(GL_COLOR_BUFFER_BIT);
وفي المستقبل عند رسم رسومات ثلاثية الأبعاد نستخدم
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ثم قلنا للمكتبة gl أن تكتب كل شيء في الذاكرة المؤقتة
glFlush();
قبل أن ننقل ما فيها إلى الشاشة
glutSwapBuffers();
ولرسم شيء حقيقي نضع الكود الخاص بذلك بين
glColor
و
glFlush
وأسهل الطرق هي استعمال الهيئة التالية:
glBegin(GL_LINE_LOOP) glVertex2i(50,50); glVertex2i(100,50); glVertex2i(100,100); glVertex2i(50,100); glEnd();
بحث نستبدل glVertex2i بالبعد الذي تريد ونستبدل GL_LINE_LOOP بأي شيء من الجدول
GL_POINTS | رسم كل نقطة |
GL_LINES | ترسم الخط الواصل بين النقطة الأولى والثانية ثم الثالثة والرابعة وهكذا |
GL_LINE_STRIP | ترسم خط بين كل نقط والتي قبلها |
GL_LINE_LOOP | ترسم كما في التي قبلها مع وصل النقطة الأخيرة بالأولى |
GL_LINE_POLYGON | كما في التي قبلها ولكن ترسم المضلع بدلا من حدوده |
GL_QUADS | كل أربع نقاط وكأنها مضلع لوحده مثلا 1و2و3و4 مضلع ثم 5و6و7و8 مضلع ثاني وكذا |
GL_TRIANGLES | كل ثلاث نقاط وكأنها مضلع لوحده |
GL_TRIANGLE_STRIP | النقاط الثلاث الأولى ترسم مضلع وكل نقطة جديدة مع آخر اثنتين قبلها تشكل المضلع الجديد |
glColor3f(R,G,B);
حيث RGB هي قيم من 0.0 إلى 1.0
glColor3ub(R,G,B);
حيث RGB هي قيم من 0 إلى 255
ويمكن تحديد حجم النقطة مثلا ب
glPointSize(1.5);
وللتفاعل مع المستخدم توفر مكتبة glut هذه الوظائف التي يمكنك أن تكتبها وتضع فيها كيف يتصرف البرنامج عند حدوث حدث معين
DisplayFunc() | يتم مناداته لتحديث الشاشة |
ReshapeFunc(w,h) | عند تغيير حجم النافذة |
KeyboardFunc(key,x,y) | عند الضغط على لوحة الفاتيح |
SpecialFunc(key,x,y) | عند الضغط على الأزرار الخاصة في لوحة الفاتيح مثل الأسهم |
MouseFunc(btn,state,x,y) | النقر بالفأرة على موقع معين |
MotionFunc(x,y) | تحريك الفأرة |
IdleFunc() | يتم تنفيذه بشكل متكرر عندما لا يعمل شيء |
TimerFunc(value) | يتم تنفيذه بشكل متكرر خلال فترات محددة |
glutDisplayFunc(my_display); glutReshapeFunc(my_resize); glutKeyboardFunc(my_key); glutSpecialFunc(my_key2); glutMouseFunc(my_mouse); glutMotionFunc(my_mouse2); glutIdleFunc(my_idle); glutTimerFunc(msec,my_timer,value);
واذا كنت تتسائل عن الأبعاد التي نرسم فيها،
فاعلم أن openGL تستخدم مصفوفتان رئيسيتان
الأولى هي GL_PROJECTIION وهي مصفوفة
الاسقاط ( عالم الكاميرا) والأخرى هي
GL_MODELVIEW والتي تحدد عالم المجسم نفسه
وقبل أن نحددهما نستدعي
glViewport(0,0,w,h);
وبذلك نخبره بأن لايرسم خارج هذه الحدود ذات البعدين
وتعتبر النقطة 0,0 هي النقطة السفلى اليسرى.
الآن نحدد المصفوفة التي نريد التعامل معها
وهي مصفوفة الإسقاط (الكاميرا) بأمر
glMatrixMode(GL_PROJECTIION);
ونخبره باستعمال المصفوفة المحايدة ب
glLoadIdentity();
ثم نحدد الأبعاد التي نريد
glOrtho(minX,maxX,minY,maxY,minZ,maxZ);
مثلا
glOrtho(-25,25,-25,25,-25,25);
تحدد مكعب مركزه 0,0,0 وطوله 25 لترسم بداخله.
ولكن هذا الكلام يبقى صحيحا إلى أن تغيير حجم النافذة
لذا يجب أن تضع مثل هذا الكود داخل
ReshapeFunc الذي أسميا my_resize ،
ويمكنك استبدال glOrtho ب glFrustum أو gluPerspective
ثم ننتقل إلى طور المجسم ب
glMatrixMode(GL_MODELVIEW);
ونبدأ بالرسم
ولتغيير موضع الكاميرا يمكنك استدعاء
gluLookAt(camX,camY,camZ,atX,atY,atZ,0,1,0);
ويمكن تطبيق عمليات المصفوفة على كلتا المصفوفتين
من ازاحة بمقدار x,y,z من خلال
glTransltef(x,y,z);
وتكبير من خلال
glScalef(x,y,z);
وادارة بمقدار deg درجة حول المحور x,y,z ب
glRotatef(deg,x,y,z);
هذا مثال يوضح ذلك
// include the GL lib's #include<GL/gl.h> #include<GL/glu.h> #include<GL/glut.h> int W,H; // the 8 verteces of a cube of ide lenght 40 float vertices[][3]={ {-20,-20, 20},{20,-20, 20},{20,20, 20},{-20,20, 20}, {-20,-20,-20},{20,-20,-20},{20,20,-20},{-20,20,-20} } // the indices to form the cube unsigned int indeces[24]={ /* 24 = 4 points in 6 sides */ 0,1,2,3, /* the 1st side by connect 0-1-2-3-0 */ 1,5,6,2, 4,5,6,7, 4,0,3,7, 3,2,6,7, 0,1,5,4 } float colors[6][3]={ {1.0,0.0,0.0},{0.0,1.0,0.0}, {0.0,0.0,1.0},{1.0,1.0,0.0}, {0.0,1.0,1.0},{1.0,0.0,1.0} } void my_resize(int w,int h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTIION); glLoadIdentity(); glOrtho(-25,25,-25,25,-25,25); glMatrixMode(GL_MODLEVIEW); W=w; H=h; } void my_display() { int i,j,k=0; glClearColor(1.0,1.0,1.0,1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_QUADS) for (int i=0;i<6;++i) { glColor3fv(colors[i]); for (int j=0;j<4;++j,++k) glVertex3fv(vertecs[indeces[k]]); } glEnd(); glFlush(); glutSwapBuffers(); } int main( int argc,char *argv[]) { glutInit( &argc, argv ); glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH ); glutInitWindowPosition(0,0); glutInitWindowSize(320,240); glutCreateWindow("My 1st openGL"); glEnable(GL_DEPTH_TEST); glutDisplayFunc( my_display ); glutReshapeFunc( my_resize ); glutMainLoop(); // return success return 0; }
glFrustum(minX,maxX,minY,maxY,minZ,maxZ); gluPerspective(fovY,aspct,ner,far); float ptr[16]={...} // matrix glLoadMatrix(ptr); glMultMatrix(ptr); // Current*ptr (from the right) glPushMatrix(); glPopMatrix(); -- glNormal3fv(ptr); glShadeMode(GL_FLAT); -- glEnableClientState(GL_VERTEX_ARRAY); //GL_COLOR_ARRAY & GL_NORMAL_ARRAY glVertexPointer(3,GL_FLOAT,0,ptr); // 3 for xyz glColorPointer(3,GL_FLOAT,0,ptr); glNormalPointer(GL_FLOAT,0,ptr); glDrawElements(GL_GUADS,indN,GL_UNSIGNED_INTEGER,ind_ptr); -- glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); float lpos={x,y,z,1.0}; float ldir={x,y,z,0.0}; float diff={1,0,0,1}; float amb ={1,0,0,1}; float spc={1,1,1,1}; glLightfv(GL_LIGHT0,GL_POSITION,lpos); glLightfv(GL_LIGHT0,GL_DIRECTION,ldir); glLightfv(GL_LIGHT0,GL_AMBIENT,amb); glLightfv(GL_LIGHT0,GL_DIFFUSE,diff); glLightfv(GL_LIGHT0,GL_SPECULAR,spc); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,global_amb_ptr); glLightModelfv(GL_LIGHT_MODEL_DIFFUSE,global_diff_ptr); glLightModelfv(GL_LIGHT_MODEL_SPECULAR,global_spc_ptr); glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,amb_ptr);
<< السابق | كتاب لينكس الشامل | التالي >> |