الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

درس عمل مجسم للكرة الأرضية

مغلق
بدأه hunterxhunter في 11 يوليو 2006 · 2 رد · 6,225 مشاهدة · في OpenGL
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

درس عمل مجسم للكرة الأرضية

هذا المثال أخته من موقع

http://www.codesampler.com

وهذا هو المثال

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

والموضوع مفتوح للنقاش والإضافة ولاتنسو التصحيح

وإن شاء الله في الدرس الثاني سنعدل على هذا المثال أشياء بسيطة التحريك عمل كوكب آخر

وشكرا

تم تعديل هذه المشاركة بواسطة hunterxhunter في 11 يوليو 2006 في 13:39

#2

السلام عليكم ورحمة الله .

درس جميل شكرا على الافادة . لكن اتمنى ان تضع

 حتى تسهل قراءة الكود علينا وشكرا  ;)  .
#3

السلام عليكم

أخواني الكرام أسمحولي على تأخري

الآن نبدأ بتعديل المثال السابق

سأبد بذكر الأشياء التي سنضيفها

1_ كوكب آخر (يعني قمر الأرض)

2_ سنجعله سنضيف لهم حركة

الأول وهو الأرض سنجعله يدور حول نفسه

والثاني وهو القمر سنجعله يدور حول نفسه وحول الأرض

3_تحريك الكامرة

GLfloat  Rotate_earth =0.0f;

الآن هذا المتغير لدوران الأرض

GLfloat  Rotate_moon =0.0f;

وهذا لدوران القمر

float eyex=0, eyey=1,eyez=0;
float centx=0 ,centy=0,centz=-100;

أما السطرين السابقين فهما للكامرة

الأول لموقع الكامرة والثاني للنقط التي تنضر لها الكامرة

الآن أستبد دالة render

بهذه الدالة

void render( void )
{
	// Clear the screen and the depth buffer
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );


	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity(); 
	gluLookAt( eyex, eyey, eyez, centx, centy, centz, 0, 1, 0 );

   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 );

	glPushMatrix();
{


  glRotatef(Rotate_moon,  0.0,-1.0,0.0);
	renderSphere( 3.5f, 0.0f, 0.0f, 0.5f, g_nPrecision );

}
glPopMatrix();




glPushMatrix();
{


   glRotatef(Rotate_earth, 0.0,-1.0,0.0);
renderSphere( 0.0f, 0.0f, 0.0f, 1.5f, g_nPrecision );

}
glPopMatrix();


	if( g_bRenderInWireFrame == true )
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	else
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

	if( g_bUseLighting == true )
		glEnable( GL_LIGHTING );
	else
		glDisable( GL_LIGHTING );

	// Render a sphere with texture coordinates
  Rotate_moon+=0.5;
Rotate_earth+=0.3;

	SwapBuffers( g_hDC );
}

طبعا هي نفس الدالة فقط زدنا في الكود كم حاجة

	gluLookAt( eyex, eyey, eyez, centx, centy, centz, 0, 1, 0 );

وهو لتحريك الكامرة أعتقد أنها في الأصل تحرك العالم ككل فتضهر كأنها تحرك الكامرة طبعا أنا أتكلم عن مثالنا هذا إذا تبغوا تعرفوا أكثر عن الكامرة روحوا الى موضوع الشمري عن تحريك الكامرة

glPushMatrix();
{


  glRotatef(Rotate_moon,  0.0,-1.0,0.0);
	renderSphere( 3.5f, 0.0f, 0.0f, 0.5f, g_nPrecision );

}
glPopMatrix();

ملحوظه سيرسم جميع الكواكب عند الإحداثيات (0و0و-5)لأنوضعنا دالت الإزاحة

glTranslatef(0.0f,0.0f,-5.0f);

خارج

glPushMatrix();
و
glPopMatrix();

الآن لعمل أي كوكب آخر يجب أن تكتب خصائصه والخاصيه الأهم هي renderSphere

لأنها هي التميز الكوكب عن الآخر

الآن الكود السابق لأنشاءالقمر ولأنشاء أي كوكب يجب أن تضع خصائصه بين دالتي

glPushMatrix();
و
glPopMatrix();

glRotatef(Rotate_moon,  0.0,-1.0,0.0);

هذا الكود لدوران القمر حول نفسه إذ لم تفهم قصدي إذهب الى دروس الأخ علي الدعيجي رحمه الله و أدخله الجنة

(إدعوا له يا إخوان )

renderSphere( 3.5f, 0.0f, 0.0f, 0.5f, g_nPrecision );

في هذه الدالة جعلنا الوسيط الأول =3.5 أي أن القمر سيدور

حول مركز رسمه بدائرة طول نصف قطرها 3.5

glPushMatrix();
{
   glRotatef(Rotate_earth, 0.0,-1.0,0.0);
renderSphere( 0.0f, 0.0f, 0.0f, 1.5f, g_nPrecision );

}
glPopMatrix();

الكود السابق لرسم الأرض

وبنفس الطريقة اللي رسمنا بها القمر فقط جعلنا الوسيط الأول والثاني والثالث في دالة renderSphere

يساوي صفر أي ثابت في محلة ولكن جعلناه يتحرك حركة دورانية بستخدام الدالة

GlRotatef

  Rotate_moon+=0.5;
Rotate_earth+=0.3;

عبارة عن قيمة الوسيط الأول لدالة glRotatef

فإذا كان أكبر من 0 بكثير حدكون سرعة الدوران كبيرة

وكذلك إذا كان أصغر من الصفر بكثير ستكون نفس السرعة ولكن عكس الإتجاه

case VK_LEFT : 
			eyex-=0.3;
			;break;
		case VK_RIGHT : 
			eyex+=0.3;
			;break;
		case VK_UP : 
				eyez-=0.3;
			break;
		case VK_DOWN : 
				eyez+=0.3;
			break;
		case VK_HOME :
 				eyey+=0.3;
 			break;
		case VK_END : 
 				eyey-=0.3;


break;

الكود السابق ضيفه الى دالة WindowProc

وهو لكي تحرك الكامرة عن طريق الكيبورد

void redirectIOToConsole( void )
{
	// Allocate a console so we can output some useful information.
	AllocConsole();

	// Get the handle for STDOUT's file system.
	HANDLE stdOutputHandle = GetStdHandle( STD_OUTPUT_HANDLE );

	// Redirect STDOUT to the new console by associating STDOUT's file 
	// descriptor with an existing operating-system file handle.
	int hConsoleHandle = _open_osfhandle( (intptr_t)stdOutputHandle, _O_TEXT );
	FILE *pFile = _fdopen( hConsoleHandle, "w" );
	*stdout = *pFile;
	setvbuf( stdout, NULL, _IONBF, 0 );

	// This call ensures that iostream and C run-time library operations occur  
	// in the order that they appear in source code.
	ios::sync_with_stdio();
}

ضيف هذه الدالة الى مشروعك إذا كن حاب تضهر Console

ولكن لاتنسى تكتب إستدعائها في الدالة الرئيسيةإكتبها هكذا

redirectIOToConsole();

وأيضا أكتب هذه الأسطر لتعلم المترجم عن المكتبات التي ستستخدمها في برنامجك

#include <iostream>

#include <io.h>
#include <fcntl.h>
using namespace std;

الآن دعونا نضهر شيء في الكونسل

في دالة WindowProc

في حدث الضغط على زر

F1

و

F2

أضف هذا الكود

cout << "Sphere Resolution: " << g_nPrecision << endl;

وهو لإضهار عدد المثلثات

إنتها الدرس ولكن ماذا تحبوا يكون الدرس القادم إن شاء الله

وشكرا

هذا الموضوع مغلق.

مواضيع مشابهة