درس عمل مجسم للكرة الأرضية
هذا المثال أخته من موقع
وهذا هو المثال
http://www.codesampler.com/source/ogl_textured_sphere.zip
سئبدأ بتعريف المثال
مثال عمل مجسم دائري تسطيع إكسائه بخريطت الأرض
الآن نذهب الى البرمجة
bool g_bUseLighting = true; bool g_bRenderInWireFrame = false; int g_nPrecision = 32; float g_fSpinX = 0.0f; float g_fSpinY = 0.0f;
المتغير الأول للإضائة ويأخد قيمتين
True=هنالك إضائة
False=عدم وجود إضائة
المتغير الثاني للمجسم وهو ليريك المجسم على هيئت خطوط بمعنا آخر (مثلثات)
المتغير الثالث هو عبارة عن عدد المثلثات من جهه واحدة ولذلك جعلناه عدد صحيح
المتغير الرابع والخامس لتدوير الكرة
ندهب الى الدوال
LoadTexture
Init
Render
Shutdown
RenderSphere
WindowProc
WinMain
الدالة الأولى للإكساء(إذهب الى دروس الشمري فأعتقد أنها أفضل مرجع عربي في الإكساء)
الدالة الثانية للإعدادات مثلا تحتوي على الإضائة
الدالة الثالثة وهي للعرض يعني لو مثلا تبي ترسم مثلث فقط تعطيهاإحداثيات النقاط وهي تعرضه على الشاشة
الدالة الرابعة للمسح
الدالة الخامسة هي التي سنضع فيها بيانات المجسم الكرة لكي تضهرها الدالة
Render
الدالة السادسة للتحكم بالكيبورد والموس
الدالة السابعة هي الدالة الرئيسية
سأبد بالدالة WindowProc
static POINT ptLastMousePosit; static POINT ptCurrentMousePosit;
المتغير الأول ليسجل إحداثيات السيني والصادي للموس (ولن يسجل الإحداثيات إلا إذا ضغط على زر الأيسر للفأرة)بمعنا آخر موقع الفأرة السيني والصادي عند النقر على الزر الأيسر
المتغير الثاني وهو موقع الحالي للفأرة أي يسجل بعد التسجيل المتغير الأول
لمتغيران السابقان يفيدونا في الدوران
static bool bMousing;
هذا المتغير لكي نعلم إذا كان زر الفأرة مضغوط أم لا (يفيدنا في إذا كان سيحدث الدوران أم لا)
switch( msg )
{
case WM_KEYDOWN:
{
switch( wParam )
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
case VK_F1:
if( g_nPrecision > 5 )
g_nPrecision -= 2;
break;
case VK_F2:
if( g_nPrecision < 30000 )
g_nPrecision += 2;
break;
case VK_F3:
g_bRenderInWireFrame = !g_bRenderInWireFrame;
break;
case VK_F4:
g_bUseLighting = !g_bUseLighting;
break;
}
}
break;case VK_ESCAPE:
نضع فيها أمر الخروج
case VK_F1: if( g_nPrecision > 5 ) g_nPrecision -= 2;
هل تذكروا g_nPrecision
وهو لعدد المثلثات فإذا كان عدد المثلثات أكبر من خمسة ننقص منهم إثنان كل ماضغجط على زر F1
case VK_F2: if( g_nPrecision < 30000 ) g_nPrecision += 2;
وهو لزيادة عدد المثلثات إذا كانوا أقل من 30000
case VK_F3: g_bRenderInWireFrame = !g_bRenderInWireFrame; case VK_F4: g_bUseLighting = !g_bUseLighting;
السطرين السابقين واضحين
فقط إذا كان المتغير يحمل قيمة true
سيصبح false
والعكس صحيح
case WM_LBUTTONDOWN:
{
ptLastMousePosit.x = ptCurrentMousePosit.x = LOWORD (lParam);
ptLastMousePosit.y = ptCurrentMousePosit.y = HIWORD (lParam);
bMousing = true;
}WM_LBUTTONDOWN هو حدث الضغط علر زر الفأرة الأيسر
أماهذا الكود لم أفهم مامعنا وفائدة
LOWORD (lParam);
و
HIWORD (lParam);
أما bMousing = true;
فيعني أن زر الفأرة مضغوط
case WM_LBUTTONUP:
{
bMousing = false;
}
عكس WM_LBUTTONDOWN WM_LBUTTONUP
case WM_MOUSEMOVE:
{
ptCurrentMousePosit.x = LOWORD (lParam);
ptCurrentMousePosit.y = HIWORD (lParam);
if( bMousing )
{
g_fSpinX -= (ptCurrentMousePosit.x - ptLastMousePosit.x);
g_fSpinY -= (ptCurrentMousePosit.y - ptLastMousePosit.y);
}
ptLastMousePosit.x = ptCurrentMousePosit.x;
ptLastMousePosit.y = ptCurrentMousePosit.y;
}WM_MOUSEMOVEهو حدث حركت الفأرة
if( bMousing )
{
g_fSpinX -= (ptCurrentMousePosit.x - ptLastMousePosit.x);
g_fSpinY -= (ptCurrentMousePosit.y - ptLastMousePosit.y);
}الكود السابق يعني أن إذا كانت الفأرة تتحرك وأيضا زر الفأرة الأيسر مضغوط فأنه يحسب قيمت
g_fSpinXو g_fSpinY
وأنا قلت أنهم سيفيدونا في تدوير الجسم وهم في الحقيقة سنعوض بهم في دالت الدوران بمعنا
(زواية)
نذهب إلى دالة renderSphere
سأشرح المطلوب من الدالة أولا
مثال
نريد عمل كوكب مثل الأرض في خصائصه والتي منها
دورانه حول محوره (بستخدام دالة الدوران (glRotatef))
ودورانه حول الشمس
فل نفرض مثلا أن الشمس تقع في الإحداثيات التالية(0و0و0)
والأرض تقع في الإحداثيات (10و0و0)
فبعد الأرض عن الشمس =10وحدة طولية أي نصف قطر الدائرة
أي أن 10= قيمة ثابته
الآن نرجع الى الدرس
void renderSphere( float cx, float cy, float cz, float r, int p )
الوسيط الأول وهو نصف قطر الدورة الأفقية
الوسيط الثاني وهو نصف قطر الدورة العمودية
الوسيط الثالث وهو نفس الأول والثاني بس في البعد العيني
الوسيط الرابع وهو حجم الكرة (الكوكب)
الوسيط الخامس وهو عدد المثلثات من الجهة الواحدة
const float PI = 3.14159265358979f; const float TWOPI = 6.28318530717958f; const float PIDIV2 = 1.57079632679489f;
الأول هو الباي والثاني باي*2 والثالث باي/2
float theta1 = 0.0; float theta2 = 0.0; float theta3 = 0.0; float ex = 0.0f; float ey = 0.0f; float ez = 0.0f; float px = 0.0f; float py = 0.0f; float pz = 0.0f;
لا أعلم ما فئدتهم بشكل خاص
ولكن بشكل عام ستفيدنا في رسم الكرة
if( r < 0 ) r = -r; if( p < 0 ) p = -p;
العبارة الشرطية الأولة لكي لاتسمح بالأعداد السالبة لأننا نعرف أن إذا كان حجم الكرة يساوي صفر سيكون الناتج نقطة ونعلم أن النقطة هي أصغر شيء نسططيع رسمه
العبارة الشرطية الثانية نفس فكرة الأولى ولكن على عدد المثلثات
if( p < 4 || r <= 0 )
{
glBegin( GL_POINTS );
glVertex3f( cx, cy, cz );
glEnd();
return;
}أما هذا الشرط لكي نرسم نقطة طبعا إذا تحقق الشرط
for( int i = 0; i < p/2; ++i )
{
theta1 = i * TWOPI / p - PIDIV2;
theta2 = (i + 1) * TWOPI / p - PIDIV2;
glBegin( GL_TRIANGLE_STRIP );
{
for( int j = 0; j <= p; ++j )
{
theta3 = j * TWOPI / p;
ex = cosf(theta2) * cosf(theta3);
ey = sinf(theta2);
ez = cosf(theta2) * sinf(theta3);
px = cx + r * ex;
py = cy + r * ey;
pz = cz + r * ez;
glNormal3f( ex, ey, ez );
glTexCoord2f( -(j/(float)p) , 2*(i+1)/(float)p );
glVertex3f( px, py, pz );
ex = cosf(theta1) * cosf(theta3);
ey = sinf(theta1);
ez = cosf(theta1) * sinf(theta3);
px = cx + r * ex;
py = cy + r * ey;
pz = cz + r * ez;
glNormal3f( ex, ey, ez );
glTexCoord2f( -(j/(float)p), 2*i/(float)p );
glVertex3f( px, py, pz );
}
}
glEnd();الكود السابق هو عبارة عن معادلات لرسم الكرة بواسطة المثلثات المتصلة
هذا كل اللي أعرفه ولكن تستطيع حفضه على أنه كود رسم كرة الى أن تفهمه
طبعا إذا كان عدد المثلثات كبير سترسم الكرة أم إذا قل فيتغير الشكل
نذهب الى دالت render
glTranslatef( 0.0f, 0.0f, -5.0f ); glRotatef( -g_fSpinY, 1.0f, 0.0f, 0.0f ); glRotatef( -g_fSpinX, 0.0f, 1.0f, 0.0f );
السطر الأول هي دالت اللإزاحة أعتقد أن الكل يعرفها
السطرالثاني هو دالت التدوير الوسيط الأول قيمت الزاوية
وسنضع فيه قيمت المتغير g_fSpinY
ولكن بقيمة سالبة
ملحوظة
تستطيع حدف الإشارة السالبة ولكن بتغير الإشارة السالبة إلى موجبة في المعادلة
g_fSpinY -= (ptCurrentMousePosit.y - ptLastMousePosit.y);
بحيث تصبح
g_fSpiny += (ptCurrentMousePosit.y - ptLastMousePosit.y);
طبعا جعلنا الوسيط الثاني =1
لكي نجعل محور الدوران للكرة يوازي المحور السيني
ونفس الفكرة للسطر الثالث
if( g_bRenderInWireFrame == true ) glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); else glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
السطر الأول للتحقق إذا كان مg_bRenderInWireFrame ==true
أو لا فإذا كان يساوي true
نجعل البرنامج ينفد الكود الفعلي ليريك الكرة على هيأت خطوط وهذا هو الكود
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
الويسيط الثاني في الدالة glPolygonMode
إذا كان GL_LINE
تعني على هيأت خطوط
أما GL_FILL
تعيده هو الوضع الأصلي
if( g_bUseLighting == true ) glEnable( GL_LIGHTING ); else glDisable( GL_LIGHTING )
ونفس الشيء مع الإضائة
طبعا أنا لم أشرح شيئ عن الإضائة ولكن إن شاءالله نشرحها في درس لوحده
طبعا توجد هناك الكثير من الأخطاء و هناك الكثير من الأشياء اللي ما أعرفها في المثال وذللك لأني لم يمضي على دراستي للغت السي بلس بلس ثلاثة أسابيع ومن نهايت الأسبوع الأول دخلت في opengl
والموضوع مفتوح للنقاش والإضافة ولاتنسو التصحيح
وإن شاء الله في الدرس الثاني سنعدل على هذا المثال أشياء بسيطة التحريك عمل كوكب آخر
وشكرا