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

تعلم خطوه بخطوه بناء 3D engine

مغلق
بدأه nono00700 في 25 يوليو 2007 · 16 رد · 8,168 مشاهدة · في OpenGL
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

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

أولا مرحبا بالجميع

سنبدأ بإذن الله تعالى تعلم كيفية بناء محرك ثلاثى الابعاد أو بالأخص 3d engine وليس محرك العاب كامل .

طبعا فكره مجنونه كما اخبرتني لجنة التقيم بمشروع التخرج وتجاوز للمعاير النسبيه ولكن وجدت نفسي اما تحدا اخر في حياتي وحبيت انقل لكم ما أكتسبته من خبرات قليله لعلها تنفع بعض المبرجين .

ولكن لماذا نشرع في بناء محرك العاب ( ماسوق مليان ياعم )؟؟؟

نعم السوق مليان بس تفتكر كدا الي بيجيب محرك جاهز ويشتغل عليه هل هو حقا مصمم العاب؟؟؟؟؟؟؟؟؟؟

هناك فرق بين من تعلم اصول الرسم على الورق والالوان ثم بدأ التطبيق على الكمبيوتر ومن بدأ من الكمبيوتر من الصفر والفرق شااااااااااسع بينهما.

ثانيا وده الاهم دائما محرات الالعاب الي مفروضه علينا في السوق محدوده بإمكانيتها او ما وفرته الشركات المنتجه لهذه المرحكات من أدوات ومكتبات

وكلمتين من الاخر كدا عايز تكون مبرمج محترف فكر انك تبني محرك اللعاب ولو على سبيل التعليم ( ليــــــــــــــــــــــــه يعم )؟

علشان حا تتعلم مبدأ اساسي من مبادء البرمجه هو ( تحسين أداء الكوود ) في حجز الذاكره وفي سرعة الاجابه لتنفيذ الوظائف ( Functions )

حتى انك في بعض الاحيان حاتستبدل (function) بأخرى ولن يكون هدفك هو الحصول على وظيفه فقط ولكن الحصول عليها وتنفيذها بأسرع ما يمكن ايضا وسيأتي التفضيل هنا والمقارنه ( وبعدين تتجنن وتعد تكلم نفسك في الاخر ) :)

المهم تعالو نتعرف على مكونات ( 3d engine ) ممكن تكون زي ايه :

1-Real time render system

أ- Object loader

ب-Texture manager

جـ-Particle system

د-Water sysrem

هـ-lightning system

و-shadow system

ز-sprite system

2-camera system

3-Math Engine

4-sound system

على فكره التصنيف ده مش علمي اوي او مش موثق ده من عندي وعلى فكره كمان مفيش تصميم / تخطيط ثابت لمحركات اللاعاب فقط تبني ما ينسبك

او على حسب احتيجاتك ولكن لن يخلو محرك ثلاثي الابعاد مما ذكرت...

طيب فاضل ايه من الاسسيات علشان نبني محرك العاب كاااامل زي الي في السوق؟؟؟

فاضـــــــــــــــــــــــل :

*AI Engine

*Physics Engine

*Collision detection system

*World manager system

*Load / save system

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

لو ربنا احيانا واحياكم حا نتكلم بالتفصيل على الجزء الخاص بـ ( 3d engine ) ونفهم كل system داخله بيتصمم الزاي ...

نصيحـــــــــــــــــــــــــه للي حابب يكمل الدوره:

أولا: أذا كنت مش بتحب الرياضيات / مابتفهمهاش علاقتك بيها مش كويسه انسى الموضوع علشان ميجلكش مرض نفسي ...

ثانيا: مش معقوله حد يفكر يعمل محرك ثلاثي الابعاد من غير ما يكون يعرف على سبيل المثال (dirctx / opengl) طبعا بما اني عرضت الموضوع بقسم (opengl) يبقى حا نشتغل بيها لو مش فاهمها عليك وعلى الكتب والحمد الله أكتر من الهم على القلب والقسم مليييييييييييييييييييييييييان بمشاركات الشباب الله يعطيهم الصحه يااارب انا حاشرح طبعا وظائف (function) بس مش بستفاضه حاركز اكتر على الموضوع العام.

ثالثا : مدام قررت تشتغل (opengl) يبقى حا تشتغل ++ c ا+ ( api ) و من وجهة نظري الشخصية افضل بيئه برمجيه ممكن تبني فيها محرك ألعاب كامل هي ++c لانك ببساطه لازم تشتغل ( api ) وأحسن لغه تدعم ( api ) ا ++c وايضا (opengl) أصلا مكتوبه بـ ( api ) .

ولنا لقااااااء قريب إن شاء الله

#2
اقتباس
هناك فرق بين من تعلم اصول الرسم على الورق والالوان ثم بدأ التطبيق على الكمبيوتر ومن بدأ من الكمبيوتر من الصفر والفرق شااااااااااسع بينهما.

ثانيا وده الاهم دائما محرات الالعاب الي مفروضه علينا في السوق محدوده بإمكانيتها او ما وفرته الشركات المنتجه لهذه المرحكات من أدوات ومكتبات

وكلمتين من الاخر كدا عايز تكون مبرمج محترف فكر انك تبني محرك اللعاب ولو على سبيل التعليم ( ليــــــــــــــــــــــــه يعم )؟

كلام سليم جدا ..

وشكرا على المبادرة .

لكن هل تريد الموضوع .. كموضوع تعليمي .. أو أنه مشروع .. من العنوان اللي فاهمه أنه موضوع تعليمي .. :) .

أيضا ماهو شكل المحرك الذي تنوي طرحه .. لابد من وضع إطار عام .. عشان نمشي عليه .. بمعنى .. تضع أهم خصائص المحرك الذي تنوي طرحه . .

حقيقة الموضوع كبير .. ويحتاج جهد وعمل لاتمامه .. أدعو لك بالتوفيق ,

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#3

السلام عليكم

مشكووور أخي الشمري على تفاعلك السريع

وكما ذكرت فإن شاء الله موضوع تعليمي .. :lol:

بس ياريت لو كلنا نشارك ولي عندو معلومه يقولها علشان نستفيد جميعا إن شاء الله

اما بالنسبه للإطار العام :unsure:

كما ذكرت انه محرك ثلاثي الابعاد وليس محرك ألعاب كامل

محتوياته العامه هي :

1-Real time render system

أ- Object loader

ب-Texture manager

جـ-Particle system

د-Water sysrem

هـ-lightning system

و-sprite system

2-camera system

3-Math Engine

4-sound system

وإن شاء الله تزيد على حسب رغبة العضاء والتوفيق من عند الله

#4

السلام عليكم

مرحبا بالجميع

سنبدأ اليوم بإعدادت ++c مع opengl ولكن في ظل full screen mode ..

علشان نضمن ان مفيش حاجه حا تشغل المعالج ولا كرت الشاشه غير المحرك خاصتنا

وده طبعا يستلزم ان نبني الوندو بتاعتنا بستخدام api

ياريت الناس كلها تعمل مشروع ++c فـــــــــــــــــــارغ سواء .net / vs6 ويكون من نوع win32 application ا ( windows application )

دلوقت المفروض في vs في مشروع فااارغ بدون اي ملفااات.

ونبدأ نضيف ثلاثه ملفات

اول واحد نسميه main.cpp

الثاني init.cpp

الثالث main.h

يعني مشوار الألف ميل بيبدأ بخطوه :lol:

دلوقت بقى عندنا الملفات فارغه ..

ماحدش يقلق مش حا خليك تكتب من اول ما اخترعو الكمبيوتر هههههه

علشان نعمل وندو بستخدام (api) في ++c

عندنا ثلاثه دوال رئيسيه هما:

أولا:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow)

وهنا main الرئيسيه للمشروع This function handles registering and creating the window

ثانيا :

WPARAM MainLoop()

ودي main loop هنا بقى بالعربي كدا المكان الي بيمسك اي حدث مممكن يحصل على الوندو زي مثلا الموس / الكيبورد / رسم الويندو نفسو

وبعدين بيرسلو لأخر داله وهي :

LRESULT CALLBACK WinProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)

حيث نقرر حانعمل ايه لما الحدث ده يحصل

ده كدا تعريف سريع للموضوع ومكوناته...

طبعا دي الدوال الاساسيه لو حاعمل ويندو عاديه :unsure:

ايوه يعني احنا حا نزود شوية دوووال كماااان علشان الموضوع ما يكنش مكلكع معلش مفيش حاجه ببلاش :lol:

نبدأ بالـ main.h

ونكتب مايلي :

#ifndef _MAIN_H
#define _MAIN_H

علشان يكون الملف ده معرف عند compiler ومايعدش يناديه كل شويه ويعملو compiler ايوه حا ننادي الملف ده كتيررررررر في ملافات تانيه بس قدام شويه لان هنا حانعرف كل ما هو عااام بالنسبه للمشروع نضيف ايضا

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fstream>
#include <string>
#include <gl\gl.h>													
//	Header File For The OpenGL32 Library
#include <gl\glut.h>									
//	 Header File For The GLu32 Library
#include <gl\glaux.h>

على فكره أنا حاطتة مكتبات opengl مع مكتبات ++c عااادي بس داخل مجلد اخر ومسميه gl علشان كدا عامل gl\gl.h

يعني داخل مجلد include عامل مجلد تااني ومسيمه gl وحاطة فيه gl.h , glut.h , glu

تماما كدا

ونضيف أيضا :

#define SCREEN_WIDTH 800	
#define SCREEN_HEIGHT 600								
#define SCREEN_DEPTH 32

طبعا واضح من الاسم انه الرزليوشن / حجم الشاشه بالبكسل الي حا نشتغل عليه وطبعا لازم نختار حاجه مناسبه لكرت الشاشه بتعنا

أما SCREEN_DEPTH يعني كام بيت لكل بكسيل علشان نعرف كمية الالواااان ممكن يكون 16 / 24 / 32

متهيقلي كلنا جربنا الموضوع ده

ونضيف ايضا :

extern HWND  g_hWnd;									
// This is the handle for the window
extern RECT  g_rRect;									
// This holds the window dimensions
extern HDC   g_hDC;									
// General HDC - (handle to device context)
extern HGLRC g_hRC;									
// General OpenGL_DC - Our Rendering Context for OpenGL
extern HINSTANCE g_hInstance;								
// This holds our window hInstance

طبعا انا استعملت extern علشان compiler يعرف ان المتغير ده كنت معرفو قبل كدا بس في ملف تاني

المهم نضيف بقى الدوااال

// This is our MAIN() for windows
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow);

// The window proc which handles all of window's messages.
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

// This controls our main program loop
WPARAM MainLoop();

// This changes the screen to full screen mode
void ChangeToFullScreen();

// This is our own function that makes creating a window modular and easy
HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle, HINSTANCE hInstance);

// This allows us to configure our window for OpenGL and backbuffered
bool bSetupPixelFormat(HDC hdc);

// This inits our screen translations and projections
void SizeOpenGLScreen(int width, int height);

// This sets up OpenGL
void InitializeOpenGL(int width, int height);

// This initializes the whole program
void Init(HWND hWnd);

// This draws everything to the screen
void RenderScene();

// This frees all our memory in our program
void DeInit();

#endif

اخر حاجه نقفل بيها الملف هي endif#

وبكدا الحمد الله خلصنا اول header

طبعا ممكن يكون غريب شويه ان اكتب () main بتاعت المشروع هنا بس اصلا هي مش حيكون فيها حاجه وده ملف Init للمشروع

الشغل كلو حايكون في main.cpp

نبدأ نكتب في init.cpp

#include "main.h"

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{	

	HWND hWnd;
	// Create our window with our function we create that passes in the:
	// Name, width, height, any flags for the window and the hInstance
	hWnd = CreateMyWindow("My_3DEngine", SCREEN_WIDTH, SCREEN_HEIGHT, 0, hInstance);
	// If we never got a valid window handle, quit the program
	if(hWnd == NULL) return true;
	// INIT OpenGL
	Init(hWnd);									
	// Run our message loop and after it's done, return the result
	return MainLoop();						
}

كدا main خلصت عملنا ايه ؟؟؟

ندينا على CreateMyWindow ودي لسه حا نبنيها وتأكدنا انها ما رجعتش حاجه فاضيه وبعدين ندينا على Init ودي المسئوله عن اعدادات Opengl وفي النهايه خالص ندينا على mainloop وحاتفضل شغاله لحد ما نقفل الوندو بتاعتنا..

تعالو نشوف حا نكتب ايه دااخل CreateMyWindow

HWND CreateMyWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle, HINSTANCE hInstance)
{
	HWND hWnd;
	WNDCLASS wndclass;

memset(&wndclass, 0, sizeof(WNDCLASS));			// Init the size of the class
wndclass.style = CS_HREDRAW | CS_VREDRAW;		// Regular drawing capabilities
wndclass.lpfnWndProc = WinProc;				// Pass our function pointer as the window procedure
wndclass.hInstance = hInstance;							// Assign our hInstance
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);								// An arrow for the cursor
wndclass.hbrBackground = (HBRUSH) (1);						// A white window
wndclass.lpszClassName = "My_3dENgine";									// Assign the class name

RegisterClass(&wndclass);								// Register the class

لحد هنا الكوود عادي مجرد Register لل WNDCLASS يعني كأني بالملى properties بتاعتو عااادي

وبعدين نضيف فب نفس الداله

								  // Set the window properties for full screen mode
dwStyle = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
ChangeToFullScreen();							// Go to full screen
ShowCursor(FALSE);							// Hide the cursor

غيرنا في dwStyle ونادينا على ChangeToFullScreen

وباااق الكوود عااادي جدا

g_hInstance = hInstance;			// Assign our global hInstance to the window's hInstance

RECT rWindow;
rWindow.left	= 0;			   				   // Set Left Value To 0
rWindow.right	= width;							   // Set Right Value To Requested Width
rWindow.top		= 0;													   // Set Top Value To 0
rWindow.bottom	= height;		 					   // Set Bottom Value To Requested Height

AdjustWindowRect( &rWindow, dwStyle, false);		// Adjust Window To True Requested Size


// Create the window
hWnd = CreateWindow("My_3dENgine", strWindowName, dwStyle, 0, 0,
rWindow.right  - rWindow.left, rWindow.bottom - rWindow.top, 
NULL, NULL, hInstance, NULL);

	if(!hWnd) return NULL;								
// If we could get a handle, return NULL

ShowWindow(hWnd, SW_SHOWNORMAL);					// Show the window

			  UpdateWindow(hWnd);																					//Draw the window

	SetFocus(hWnd);										// Sets Keyboard Focus To The Window	

	return hWnd;
}

حد فاااهم حااجه :unsure:

عموما الي مش فاهم يقول واحنا نشرح الي مش مفهوم مع انه api و ++c بس :huh:

#5

نكمل باقي الدوال ولازلنا داخل init.cpp

void ChangeToFullScreen()
{
	DEVMODE dmSettings;									// Device Mode variable

	memset(&dmSettings,0,sizeof(dmSettings));			// Makes Sure Memory's Cleared

	// Get current settings -- This function fills our the settings
	// This makes sure NT and Win98 machines change correctly
	if(!EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dmSettings))
	{
		// Display error message if we couldn't get display settings
		MessageBox(NULL, "Could Not Enum Display Settings", "Error", MB_OK);
		return;
	}

	dmSettings.dmPelsWidth	= SCREEN_WIDTH;		// Selected Screen Width
	dmSettings.dmPelsHeight	= SCREEN_HEIGHT;		// Selected Screen Height

لحد هنا الموضوع سهل مجرد تعريف DEVMODE وتأكدنا من انه حا يشتغل على NT and Win98

وأعطيناله طول وعرض الويندو

ونضيف في نفس الداله

int result = ChangeDisplaySettings(&dmSettings,CDS_FULLSCREEN);	

	// Check if we didn't recieved a good return message From the function
	if(result != DISP_CHANGE_SUCCESSFUL)
	{
		// Display the error message and quit the program
		MessageBox(NULL, "Display Mode Not Compatible", "Error", MB_OK);
		PostQuitMessage(0);
	}
 }

زي ما احنا شيفين الموضوع كلو معتمد على ChangeDisplaySettings

وبكدا خلصنا دي كمان

ندخل على حاجه تانيه

bool bSetupPixelFormat(HDC hdc) 
{ 
	PIXELFORMATDESCRIPTOR pfd; 
	int pixelformat; 

	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);			// Set the size of the structure
	pfd.nVersion = 1;						// Always set this to 1
												 																				// Pass in the appropriate OpenGL flags
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;   
	pfd.dwLayerMask = PFD_MAIN_PLANE;			   // We want the standard mask (this is ignored anyway)
	pfd.iPixelType = PFD_TYPE_RGBA;			   // We want RGB and Alpha pixel type
	pfd.cColorBits = SCREEN_DEPTH;			   // Here we use our #define for the color bits
	pfd.cDepthBits = SCREEN_DEPTH; 			   // Depthbits is ignored for RGBA, but we do it anyway
	pfd.cAccumBits = 0;					   // No special bitplanes needed
	pfd.cStencilBits = 0;					   // We desire no stencil bits

	// This gets us a pixel format that best matches the one passed in from the device
	if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == FALSE ) 
	{ 
		MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); 
		return FALSE; 
	} 

	// This sets the pixel format that we extracted from above
	if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE) 
	{ 
		MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); 
		return FALSE; 
	} 

	return TRUE;										
	// Return a success!
}

طبعا الداله دي هي المسئوله بتعريف Pixel انها حاتشتغل تحت ظروف Opengl وبتشغل مجموعه خصائص زي Alpha pixel مثلا

لو محدش فاهم حاجه يقول ولو انها بردو api

الداله دي حا نستعملها في InitializeOpenGL

void InitializeOpenGL(int width, int height) 
{  
	g_hDC = GetDC(g_hWnd);								

// This sets our global HDC
											 														  // We don't free this hdc until the end of our program
	if (!bSetupPixelFormat(g_hDC))			// This sets our pixel format/information
		 PostQuitMessage (0);				// If there's an error, quit
	g_hRC = wglCreateContext(g_hDC);		// This creates a rendering context from our hdc
	wglMakeCurrent(g_hDC, g_hRC);			
// This makes the rendering context we just created the one we want to use
SizeOpenGLScreen(width, height);					
// Setup the screen translations and viewport
}

بالنسبه لاختراع الي اسمه hWnd ده اعتبره عنوان الوندو الي شغلين فيها تمام :unsure:

وبعدين في g_hDC وده اداه علشان ترسم اي حاجه في الوندو وزي ما احنا شيفين عرفناه بنوع Pixel الي عملناها قبل كدا :blink:

الاخ بقى g_hRC ده بيتعامل مع opengl بيتكب فيها بياناته الخاصه Model View و Projection View , Texture matrices الي بيترسمم على الشاشه حاليا.. :wacko:

=================================================================

عامة محدش يقلق لان الي انكتب ده كله ثاااااابت مفهوش حرف بيتغير لحد اخر المشروع إن شاء الله

و نراااااااااااااااكم قريبا إن شاء الله

تصبحو على خيررر -_-

#6

السلام عليكم جميعا

عدنا من جديد ومرحبا بالجميع

كنت عاوز اقول حاجه :

هو محدش علق ليه ؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟ :(

ما هو يا أما الناس فهما تقول ننتقل على صلب الموضوع يا أما الناس مش فهمه ومش عايزه تقول ؟؟؟

عااامة نستكمل وعلى فكره كل ده اعدادات علشان نربط opengl بـ ++c ونشتغل fullscreen mode

مراجعه بسيطه نفتكر عملنا ايه وفاضل ايه طبعا الكود الي انكتب لسه مش كامل فا لوحد جرب اكيد حا يطلع اخطاااء

سريعا كان في ثلاثة ملفات

main.h ودي خلاص خلصناها وحطين header للدوال الرئيسيه العامة

init.cpp ودي حطينا فيها الـ main وذكرت اني وضعتها هناك علشان مش حيكون فيها تغيير لحد نهاية المشروع فخليها بعيده عن مساحة العمل الاساسيه والي حاتكون main.cpp .

قبل ما انتقل الي الكوود

احنا في init.cpp عملنا

WinMain :

ودي كانت الـ main الاساسيه .

CreateMyWindow:

وهي الداله المسؤله عن بناء النافذه في ( fullscreen mode ) حتى ظهورها على الشاشه .

ChangeToFullScreen :

هنا قمنا بتعديل mode من الحاله العاديه وهي النافذه الي fullscreen .

bSetupPixelFormat :

وهذه الداله تقوم بالاعدادات الازمه للـ Pixel .

InitializeOpenGL :

وتقوم هذه الداله بإعداد أدوات في النظام للرسم في النافذه .

SizeOpenGLScreen :

هذه داله جديده وهي المسئوله عن Viewport ويمكننا اعتباره حجم النافذه التى ننظر من خلالها على العالم الافتراضي / ثلاثي الابعاد

بمعنى لو أردنا ان نستعمل نافذه بحجم 600 × 800 وهي نافذه النظام او المساحه الكليه للرسم عند استعمالنا Viewport سوف نخصص المساحه هنا الفعليه للرسم وماحولها سيكون ملون بلون الخلفيه طيب الفايده ايه؟؟؟

ممكن نبني القوائم بستعمال النظام حول Viewport .. ا :blink:

محتويات الداله :

void SizeOpenGLScreen(int width, int height)	
if (height==0)											{

	height=1;									
				}

	glViewport(0,0,width,height);

في حالتنا سوف نجعل النافذه تسواي مساحة الرسم الكاليه

ثم نخبر opengl بهذه الاعدادات

glMatrixMode(GL_PROJECTION);					
glLoadIdentity();

نحدد الان نظام الكاميره المستخدمه :

// (view angle, aspect ration of the width to the height, 
//  The closest distance to the camera before it clips, 
 // FOV		
// Ratio				
//  The farthest distance before it stops drawing)
gluPerspective(25.0f,((GLfloat)width/(GLfloat)height), 4 ,5000.0f);	

glMatrixMode(GL_MODELVIEW);							
glLoadIdentity();

طبعا opengl في ثلاثة دوال تقوم بتحديد شكل VIEW

بلغة الاعلاب عند صناعة لعبه من نوع first man shot زي doom3 مثلا او اذا اردنا وضع الكاميره خلف الشخصيه فسوف نستعمل منظور اسمه

Perspective وكأن الاعب يحمل معه الكاميره ويتحرك بها

اما اذا كانت اللعبه من النوع الاستراتجي فغالبا ما تستعمل الكاميره المعلقه وكأنك تنظر من طائره مثلا فيكون المنظور من نوع

orthographics وهو منظور عمودي .

ولكن ماهي حدود الكاميره فكما نعلم ان الكاميره تشكل شكل مخروطي / هرمي رأسه هي مكان الكاميره كما في الشكل

view.gif

لو حد يفتكر need for speed عااااالم كبير جدا ولكن وأنت تتحرك بالعربه وتنظر لنهاية الطريق ترى ما يسمى بخط الافق او ألتحام السماء بالارض

وسرعان ما تظهر مباني اخرى كانت مختفيه وأنت تتحرك..??

وايضا وانت تتحرك بالشخصيه في doom3 لماذا لا نرى الثلاث جدردان للغرفه مثلا لماذ يكون الاجدار الامامي وجزء بسيط من الجدار الايسر او الايمن.. ??

هذا هو مفهوووم Frustum

وسوف نتحدث عنه بالتفصيل إن شاء الله عندما نتحدث عن worlde manager ..

كدا فاضل DeInit :

وطبعا دي علشان نخلي المصادر الي استخدمناها في الرسم ونقفل النافذه

void DeInit()
{
if (g_hRC)											
				{
// This frees our rendering memory and sets everything back to normal
wglMakeCurrent(NULL, NULL);						
// Delete our OpenGL Rendering Context	
wglDeleteContext(g_hRC);						
	}

if (g_hDC) 
// Release our HDC from memory
ReleaseDC(g_hWnd, g_hDC);						

// Free the window class
UnregisterClass("My_3dENgine", g_hInstance);		
// Post a QUIT message to the window
PostQuitMessage (0);	

}

كدا أحنا خلصنا init.cpp الحمد الله :)

----------------------------------------------------------------------------------------------------------

نبدأ العمل في main.cpp

حيكون في أربع دوال هي :

Init :

هنا نقوم بأي أعدادت او بناء اساسي يخص المحرك مع العلم انها خارج mainloop

RenderScene :

هنا بقى حا نرسم الي عيزينه اي حاجه عايز ترسمها حيكون هنا

MainLoop :

وكما ذكرت سلفا هي المسئوله عن events تجمعها وترسل بها الي

WinProc :

حيث نقرر ما سنفعله عند حدوث اي حدث معين.

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

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")

#include "main.h"

ثم ببعض المتغيرات

HWND  g_hWnd;										
// This is the handle for the window

RECT  g_rRect;										
// 0This holds the window dimensions

HDC   g_hDC;										
// General HDC - (handle to device context)

HGLRC g_hRC;										
// General OpenGL_DC - Our Rendering Context for OpenGL

HINSTANCE g_hInstance;									
// This holds the global hInstance for UnregisterClass() in DeInit()

لو تتذكرو دي نفس المتغيرات الي عملنها extern في الـ main.h

ثم نبدأ

void Init(HWND hWnd)
{
// Assign the window handle to a global window handle
g_hWnd = hWnd;										

//Assign the windows rectangle to a global RECT
GetClientRect(g_hWnd, &g_rRect);					

// Init OpenGL with the global rect
InitializeOpenGL(g_rRect.right, g_rRect.bottom);	

//Black Background
glClearColor(0.0, 0.0, 0.0,1.0f);				   
}

لو تفتكرم init كانت تاني داله تشتغل في main

hWnd :

ده العنوان الي رجعته CreateMyWindow عنوان الوندو الي احنا شغلين عليها حطناه في متغير عاااام

GetClientRect :

فاكرين CreateMyWindow كان من ضمن البرامتر ( paramter ) بتعها SCREEN_WIDTH + SCREEN_HEIGHT

الداله دي حاترجع نفس القيم بس بعد ما نتأكد انها اعتمدت وموجوده فعلا .

InitializeOpenGL :

أخيرا نادينا على حاجه كتبناها :lol:

وأخدت g_rRect.right + g_rRect.bottom ودي طبعا بعد ما تحملت بالقيم من GetClientRect

وهي أقصى مسافه لليمين وأقصى مسافه للأسفل

glClearColor :

واخيرا مسحنا الشاشه بالون اسود غير مهما هنا ولكن يمكننا هنا تحميل صوره مثلا كا خلفيه للعبه عند التحميل .

نبدأ مع MainLoop

WPARAM MainLoop()
{
	MSG msg;

// Do our infinate loop
while(true)										
{										
// Check if there was a message
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
			   { 
// If the message wasnt to quit
if(msg.message == WM_QUIT)									break;

// Find out what the message does
 TranslateMessage(&msg);				

// Execute the message
DispatchMessage(&msg);			
			  }
		else											// if there wasn't a message
		{ 
			RenderScene();
						  }

	DeInit();											// Clean up and free all allocated memory

	return(msg.wParam);								
}

عرفنا حاجه من نوع msg وبعدين ابتدينا نعمل trace او تحليل msg للتعرف على وجود اي حدث او events

أذا كان WM_QUIT يبقى كدا حا نخرج برا loop ونغلق البرنامج وطبعا ننادي DeInit علشان تخلي المصادر قبل الاغلاق

ماعدا ذلك قوم بترجمة الرساله / events وأرساله winproc

واذا كان لايوجد اي events او بعد ترجمتها كلها يبدا ينفذ ويرسم RenderScene .

نبدأ مع WinProc

LRESULT CALLBACK WinProc(HWND hWnd,UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LONG	lRet = 0; 
   PAINTSTRUCT	ps;

	switch (uMsg)
	{ 
 	case WM_PAINT:											BeginPaint(hWnd, &ps);							
	EndPaint(hWnd, &ps);						
	break;

			 	// Escape - Quits
	// If we pressed a key

		switch(wParam) {								

			case VK_ESCAPE:								// If we hit the escape key
	PostQuitMessage(0);			// Send a QUIT message to the window
 					 break;	

	default:											// Return by default
		lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); 
		break; 
	} 

	return lRet;	
}

طبعا كل الي كان يخصنا هو WM_PAINT وهي تدل على الحاجه للرسم وأيضا VK_ESCAPE لغلق الوندو بأي وقت عند الضغط عليه

كدا خلصنا الحمد الله

RenderScene

ضع بها ما تشاء من كود الرسم

void RenderScene() 
{

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glColor3f(1,0,0); 

	glBegin(GL_TRIANGLES);

	glVertex3f(-1.0f, -0.5f, -4.0f);	// lower left vertex
	glVertex3f( 1.0f, -0.5f, -4.0f);	// lower right vertex
	glVertex3f( 0.0f,  0.5f, -4.0f);	// upper vertex

	glEnd();

	 glFlush();
	 SwapBuffers(g_hDC);	

}

تم بحمد الله أذا شغلت الكود كدا إن شاء الله يشتغل عامة انا حارفع الملفات بالدوال مجمعه وإن شاء الله المره القادمه نشرح camera وشوية math

والي لقااااء قريبا ... :D

fullscreen_mode1.rar

تم تعديل هذه المشاركة بواسطة nono00700 في 30 يوليو 2007 في 19:36

#7
اقتباس
ما هو يا أما الناس فهما تقول ننتقل على صلب الموضوع يا أما الناس مش فهمه ومش عايزه تقول ؟؟؟

أنا انتظرك تدخل في صلب الموضوع :D . . لان أول خطوة كانت مجرد APIs عادية .. وسهلة ..

أعجبني كثيرا شرحك لمفهوم الFrustum و Perspective .

لكن لم أفهم فكرة استخدام Perspective لانشاء الكاميرا ( يعني تستخدم مصفوفة الProjection ).. هل ستغير بزاوية الرؤية مثلا , ما اعرفه هو ان تستخدم مصفوفة الModelView للتحكم بالكاميرا ,,

على العموم لا نستعجل ,,سنترقب الأحداث :) .

اذا كنت تحب تسمع رأيي عن الموضوع حتى الان ... فأقول أسلوبك في الموضوع ممتاز .. لمن يملك معلومات سابقة عن هذه الAPIs ..

أما المبتدئ .. فبلا شك .. يحتاج قراءة المراجع ليفهم بعض الAPIs ,

بالنسبة لماتبقى من المحرك .. هل ستكمله بالبرمجة الاجرائية Functions أو بالOOP .. مجرد سؤال ! .

أكمل وفقك الله .. أتمنى من كل قلبي أن تصل الى ماتريد .. وأن لايصيبك الملل مع مرور الوقت لأي سبب ...

تم تعديل هذه المشاركة بواسطة الشمري في 31 يوليو 2007 في 02:08

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#8

السلام عليكم

مرحبا أخي الشمري واســــــــــــــــــــــــــــــــف جدا جدا جدا على التأخير

بس مشاغل بقى وكنت بحضر الدرس :unsure:

بالنسبة لموضوع Perspective :

Perspective عباره عن نوع الكاميرا الي بتستخدمها يعني المنظور بيكون أفقي وبنستخدم مصفوفة Projection لتمثيل المنظور ولا يعني هذا انها لا تستعمل مع orthgraphic او المنظور العامودي فا مصفوفة Projection تأتي دائما أذا أردنا تغير ( View port ) او نافذه الرئيه وأيضا يأتي بعد المصفوفه دائما نوع المنظور أذا كان orthgraphic او Perspective.

أما أذا أردنا تحريك الكاميره وتغير اتجاها ودورانها فعلينا اخبار مصفوفة MODELVIEW دائما بالتغيير فهي من تحمل اتجاه ومكان الكاميرا

في تأتي دائما قبل gluLookAt وبعد تحديد المنظور ( orthgraphic او Perspective ) فأذا غيرنا مكان الكاميره او اتجاهاا فعلينا دائما مناداه مصفوفة MODELVIEW وعلى فكره gluLookAt بتغير مباشرة بقيم مصفوفة MODELVIEW .

ارجو ان يكون الشرح وافي ..

عامة إن شاء الله بالكود يوضح أكتر .. أما بالنسبة لأستكمال المحرك فإن شاء الله بيكونOOP لانه في رأي لولا وجدو OOP لما ظهرة المحركات والألعاب بهذا الشكل والقوة .

وشكرا لكم جميعا..

#9

عـــدنـــا مــــ

#10

عـــدنـــا مــــــــــع الـــــــدرس

مرحبا بكم جميعا أخيرا حضرت الدرس

بعد ما حضرنا الكود وشغلنا opengl فاضل لنا نشغل الـ Camera يعني النهارده حا نتعلم فكرة عمل الكاميرا الزاي زي ما هي موجوده في الاعاب الي في السوق ..

وحاضيف كمان أرض البناءى للمحرك وهي عباره عن wire plane وليها فوائد كثيرا جدا جدا جدا انا بس حأضع كود البناء والرسم ونستكمل المره الجايه ..

طيب ما هي المواد الازمه لبناء كاميرا في عالم ثلاثي الابعاد ؟؟؟؟؟

الاجاااااابه سهله جدا لو كنت تعرف القليل عن الـ OPengl علشان تنفذ الي في دماخك حاليا زي كاميرة need for speed مثلا او general , command والقائمه طويله ..

فقط عليك تحديد نوع المنظور أفقي ولا عامودي وكلها دوال داخل opengl ومن ثم ضع احداثيات الكاميرا وأتجاهها وبكدا تكون انتهت أعدادات الكاميرا بس للأسف كاميرا ثابته :angry:

الصبر جميل ..

يعني لو المفروض نطور الكود يبقى نفكر الزاي نحرك الكاميرا مش الزاي نعملها او نبنيها لان opengl فيه كاميرا اصلا الفكره انك تشغلها بس , طيب نتعرف سريعا على كيفية تشغيل الكاميرا في عالم ثلاثي الابعاد

اولا :

حدد View port وهو اقصى نقطه لليمين واقصى نقطه للأسفل وعلى فكره ده مش معناه ان هو ده حدود العالم بتعنا العالم ممكن يكون اكبر من كدا بكتيرررررررررررر ولكن ده النافذه الي بننظر منها على العالم الثلاثي الابعاد .

glViewport(0,0,width,height);

ثانيا :

علينا اخبار OPengl بهذا View port كما نعلم فهي تقوم بتحويل العالم الأفتراضي الي مصفوفه وايضا تجهيزا لنوع المنظور ( افقي او عامودي ) فقط عليا استدعاء

glMatrixMode(GL_PROJECTION);	
glLoadIdentity();

ثالثا :

نحدد نوع المنظور وهناك ثلاث دوال تحدد هذا المنظور

gluPerspective , glFrustum , glOrtho

glOrtho : تقوم بعمل منظور عامودي

gluPerspective : تقوم بعمل منظور أفقي

glFrustum : تقوم بعمل منظور أفقي مع خاصية Frustum

أختار واحده :

glOrtho(-600,600,-600,600,20,10000);
glFrustum(-20 , 20 , -15  ,15  ,40,1500);
gluPerspective(25.0f,((GLfloat)width/(GLfloat)height), 4 ,5000.0f);

والان علينا أستدعاء الكاميرا او تشغيلها ولكي نقوم بذلك علينا اولا استدعاء مصفوفة MODELVIEW ومن ثم نقوم بإعداد الكاميرا أذا مصفوفة MODELVIEW يتواجد بها احداثيات الكاميرا بأستمرار واذا قمنا بتغير مكانها او تحريكها فا MODELVIEW تتغير قيمه ايضا .

لأعداد الكاميرا فقط علينا استدعاء gluLookAt واعطائها مكان الكاميرا و وجهتها او الي اين تنظر والمتجه العامودي عليها وغالبا ما تكون قيمه ثابته .

 glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

gluLookAt(50,10,10, 0,10,0, 0,1,0);

كدا ده الكود الطبيعي الي مممكن تشغل بيه الكاميرا بس احنا طمعانين في اكتر من كدا :rolleyes:

عوزين نحرك الكاميرا ونمشي بيها ونلف بيها ...

علشان نعمل كدا احنا محتاجين نتعامل مع Vector او المتجهات وتعرف ايضا بالمركبات الجبريه

عامة علشان الناس ما تتعبش انت مش مطلوب منك غير انك تشغل class لو كنت بقى من الناس الي بتحب تعرف كل حاجه جاات منين وتفكر وتطور يبقى ركز معايا شويه انا عموما مش حا حول الدرس الي رياضيات بس حاذكر بعض المفاهيم علشان تبقى فاااهم الموضوع.

مبدئيا كنت عاوز اقول ان محرك الالعاب مش عباره عن مكتبة جرافيكس (opengl / dirctx) او ايا كانت الموضوع اكبر من كدا والا كان بقى عامل زي البرامج الي في السوق عارفين ان ميزانيات الاعاب برا من 10 مليون لحد 100 مليون دولار يعني الموضوع فعلا ضخم وكبير وده بيرجه بسبب فريق العمل الكبير المكون من فنانين وأدبيين وخبراء رياضيات لعمل الخوارزميات والتحاليل الرياضيه ويأتي في النهايه دور المصميين و المبرمجين يعني احنا اخر الحالقة :lol:

المهم خلينا في المحرك بتعنا

يبقى اتفقنا ان احنا حا نستعمل المتجهات او المركبه الثلاثيه ( x,y,z ) و ايضا حا نتستعمل مركبه رباعيه في حالت خاصه زي تحريك الشخصيات ...

طيب تعالو نتعرف على خصائص الصف CVector3 وهو عباره عن الصف الرئيسي في المحرك خاصتنا الذي يدير العمليات الرياضيه واحزرو بين الرياضيات والفزياء .

بأعتبار ان كل نقطه لدينا سوف تمثل بـ عنصر من CVector3 اذا فهو data type تسمح بتعريف ثلاثه معاملات من نوع float هي ( x,y,z ) وبعدين قمنا بعمل overload على opreator جميعا من جمع وطرح وقسمه وضرب ومزايده وغيرها علشان نتعامل معها بحريه مطلقه تماما وقمنا ايضا بتعريف دوال او function خاصه به للتعامل معه على هيئة مركبه جبريه زي مفهوم Normalize وهو حساب قيمة متجه الوحده محصور بين -1 و +1

وايضا المتجه العمودي على متجهين CrossProduct وايضا حساب القيمة المعيايره للمتجه magnitude

والمتجه العكسي Reverse

يعني خصائص بدائيه جدا ولكن لا تخلى لعبه منها

قبل ما ادخل على صف الكاميرا حابب نتعرف احنا عوزين نعمل ايه في الكاميرا

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

يعني ملخص للعمليات الرياضيه حاتكون يا حركه او دوران

الحركه حا تحلها CVector3

فاضل الدوران حا نستعمل مفهوم Quaternion ودي تعتبر مركبه رباعيه وماخدش يتنح :blink:

Quaternion احسن واقوى طريقة لعمل rotation في جميع الاتجاهات فهي تستعمل في orintation او التوجيه في الفزياء وعلشان نعمل كود موحد في طريقة العمل والتنفيذ حا نستعملها في عمل الدوران للكاميرا و توجيهها

لو عايز تعرف اكتر عن Quaternion دي لنكات بتشرح المفهوم ده لو عند حد سؤال فيها انا إن شاء الله مستعد اجاوب

من هناااا

ومن هنا ايضا

وده كمااان حلو جدا

يبقى هو مفهوم رياضي بحت وبيستعمل ايضا في الاقمار الصناعيه وتوجيهاها :wacko:

كدا بقى ندخل نشوف حا نحرك الكاميرا الزاي.....

#11

بأفتراض انك عندك خلاص كلا من class CQuaternion و class CVector3

حانبني class CCamera وهو المسؤل الاول والاخير عن تحريك ودوران الكاميرا

حا نعمل في CCamera.h :

class CCamera
{
   public:
  // Constructor.
	  CCamera();									
 // Set the camera's position.
	void SetCamera(float x, float y, float z,	  
					 float xv, float yv, float zv,
					 float xu, float yu, float zu);

   	// This strafes the camera left or right depending on the speed (+/-) 
	void StrafeCamera(float speed);

	// This will move the camera forward or backward depending on the speed
	void MoveCamera(float speed);

	// This checks for keyboard movement
	void CheckForMovement();

	// This updates the camera's view and other data (Should be called each frame)
	void Update();
// Rotate the camera around the view.
	  void RotateCamera(float AngleDir, float xSpeed, 
float ySpeed, float zSpeed);
// Rotate the camera around a point.
	  void RotateCamera(CVector3 point, float AngleDir,
						float xSpeed, float ySpeed, float zSpeed);
	  void RotateByMouse();		 // Rotate camera by mouse.
	  void MoveByMouse();		   // Rotate camera by mouse.

	  CVector3 Pos;								   // Camera position.
	  CVector3 View;								  // Look at position.
	  CVector3 Up;									// Up direction.
	  CVector3 Strafe;								// Strafe direction.


	  float m_YAngle;

// Keeps us from going too far up or down.
	  float currentRotationAngle;					 
	  float frameInterval; 

};

دي كا كل الاجراءات الي ممكن نتعرض ليها في الوقت الحالي في الكاميرا

الموضوع إن شاء الله سهل

انا حا شرح الفكره الاساسيه في الدوال الاساسيه وحا سبكم مع الكود اي استفسار انا موجود

void StrafeCamera :

تقوم بتحريك الكاميرا الي اليمين والي اليسار ولكن احزر هي تتحرك الي اليمين بالنسبه لمحاورها هي وليس بالنسبه للعالم

يعني احنا عاوزين ننقل محاور الكاميرا الي اليمين او الي اليسار

ببساطه حانحسب اولا المتجه الذي يمثل اتجاه الكاميرا وهو عبارا عن (View - Pos) حيث ان ops هو مكان الكاميرا View هو المكان الذي تنظر اليه وناتج عملية الطرح هذه ينتج متجه يمثل اتجاه الكاميرا.

ثم نقوم بعد ذلك بحساب المتجه العامودي على ( متجه يمثل اتجاه الكاميرا ) و( Up ) اخر قيم بدخلها في glulookat

الي هو اصلا عمودي على View يعني المعادله حاتكون

Strafe.CrossProduct(View - Pos, Up);

وتخزن القيمه في Strafe إن شاء الله سهله

ونحول Strafe الي متجه وحده اي قيم تكون بين -1 و +1 وهي دي حا تكون القيمه الي حا نزايد بيها على View و Pos

ممكن نضرب القيمه دي في سرعه معينه ونزايد بيها يعني الكود على بعضه حا يكون كدا

 Strafe.CrossProduct(View - Pos, Up);
	Strafe.Normalize(); 

	// Add the strafe vector to our position
	Pos.x += Strafe.x * speed;
	Pos.z += Strafe.z * speed;

	// Add the strafe vector to our view
	View.x += Strafe.x * speed;
	View.z += Strafe.z * speed;

بس كدا كل الي عليك انك تستدعي الداله دي وتديها السرعه الي عايزها

الفكره سهسله

طيب وعلشان نتحرك للأمام والي الخلف

MoveCamera :

ودي اسهل بس حا تحسب ( متجه يمثل اتجاه الكاميرا ) وتقوم بتحويله لمتجه الوحده وتزايد بيه مش حا نحسب العامودي عليه

يعني حا يبقى الكود كدا

CVector3 Dir_tank = View - Pos;
Dir_tank.Normalize();
Pos.x += (Dir_tank.x * speed);
Pos.z += (Dir_tank.z * speed);

 View.x += (Dir_tank.x * speed );
 View.z += (Dir_tank.z * speed );

كدا احنا عدلنا قيم pos & view علشان تتحرك في جميع الاتجاهات

فاضل الدوران وزي ما قلت حا نستعمل Quaternion وعلى فكره احنا حانعمل الدوران للـ View

ببساطه جدا مفهوم الدوران في Quaternion يمثل بعمل دوران على محور معين بمقدار الزاويه الي عندي

qRotation.Rotatef(AngleDir, xSpeed, ySpeed, zSpeed);

حيث qRotation معامل من نوع Quaternion وعلى فكره مفهوش قيم فاااارغ تمام .؟؟؟؟

هو ببساطه حايرجع قيمة الدوران حول احد المحاور xSpeed, ySpeed, zSpeed وهي قيمة ثابته وعلى فكره ممكن يكون الدوران في جميع هذه المحاور او في اثنين منها او واحد وهذا ببساطه يمكن التعبيره عنه بـ

qRotation.Rotatef(45, 0, 1, 0);

ساهل طبعا :unsure:

لسه بردو ما خلصناش كدا بقى عندنا قيمة الدوران لهذا المحور وليكن yAxis

نحسب ( متجه يمثل اتجاه الكاميرا ) معروف بقى !!

وبعدين حا نضرب القيمه qRotation في ( متجه يمثل اتجاه الكاميرا ) الي عيزين نعمله الدوران وحاصل الضرب نضربه تاني في معكوس qRotation يعني بعد ما نخلي القيم سالبه علشان تلف مع عقارب الساعه وبعدين نزايد على View.

يعني المعادله النهائيه حا تبقى

 qNewView = ((qRotation * qView) * qRotation.Conjugate());

الداله كامله حا تبقى كدا

void CCamera::RotateCamera(float AngleDir, float xSpeed, float ySpeed, float zSpeed)
{
	CQuaternion qRotation, qView, qNewView;

   // Create the rotation quaternion based on the axis we are rotating on.
   qRotation.Rotatef(AngleDir, xSpeed, ySpeed, zSpeed);

   // Create the view quaternion.  This will be the direction of the view and position.
   qView.x = View.x - Pos.x;
   qView.y = View.y - Pos.y;
   qView.z = View.z - Pos.z;
   qView.w = 0;

   // Create the resulting quaternion by multiplying the rotation quat by the view quat
   // then multiplying that by the conjugate of the rotation quat.
   qNewView = ((qRotation * qView) * qRotation.Conjugate());

   // Update the view information by adding the position to the resulting quaternion.
   View.x = Pos.x + qNewView.x;
   View.y = Pos.y + qNewView.y;
   View.z = Pos.z + qNewView.z;
}

ساهل إن شاء الله

طيب لو حبينا مثلا نعمل دوران حلو نقطه معينه ؟؟ يعني على سبيل المثال في معظم الالعاب اما بتحرك الفأره بيحصل دوران بس للكامييرا مش للـ View الكاميرا بدور حولين View وبيكون قيمة View ثابته بس كدا اتجاه الرؤيه بيتغير

حاول تخيلها :wacko:

التغيير مش كتير ودي وظيفة نفس الداله بس عملنا overload عليها

void CCamera::RotateCamera(CVector3 point, float AngleDir, float xSpeed, float ySpeed, float zSpeed)
{
	CQuaternion qRotation, qPos, qNewPos;

   // Create the rotation quaternion based on the axis we are rotating on.
   qRotation.Rotatef(AngleDir, xSpeed, ySpeed, zSpeed);

   // Create the pos quaternion.  This will be the direction of the center point and position.
   qPos.x = Pos.x - point.x;
   qPos.y = Pos.y - point.y;
   qPos.z = Pos.z - point.z;
   qPos.w = 0;

   // Create the resulting quaternion by multiplying the rotation quat by the pos quat
   // then multiplying that by the conjugate of the rotation quat.
   qNewPos = ((qRotation * qPos) * qRotation.Conjugate());

   // Update the position information by adding the position to the resulting quaternion.
   Pos.x = View.x + qNewPos.x;
   Pos.y = View.y + qNewPos.y;
   Pos.z = View.z + qNewPos.z;

}

واضح اننا تعاملنا مع مكان الكاميرا Pos وقمنا بالمزايده عليه وباقي الكوود لم يتغير ...

تمام كدا حلينا مفهوم الدوران والحركه..

بس اوضح حاجه تانيه لما نحب نغلي الفأره هي المسئوله عن الدوران

RotateByMouse

اولا عليك بحساب منتصف الشاشه ولا تنسي ان الموس عباره عن x,y أذا نحسب نقطة المنتصف

int middleX = SCREEN_WIDTH  >> 1;				
// This is a binary shift to get half the width

int middleY = SCREEN_HEIGHT >> 1;				
// This is a binary shift to get half the height

ثم نستعلم عن أحداثيات الفأره؟؟

// Get the mouse's current X,Y position
	GetCursorPos(&mousePos);

وبعدين نضع الفأره في نقطه المنتصف , ولا تقلق فا ستتحرك ولكن سرعان ما تعود مره اخرى الي المنتصف

SetCursorPos(middleX, middleY);

دلوقت بقى الفأره عندنا حاتعمل دوران حولين اي محاور ؟؟؟

الشائع هو zAxis , yAxis

// Get the direction the mouse moved in, but bring the number down to a reasonable amount
	angleY = (float)( (middleX - mousePos.x) ) / 5.0f;		
	angleZ = (float)( (middleY - mousePos.y) ) / 5.0f;

اولا :

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

ثانيا :

عند تحريك الفأره الي اليمين او اليسار سنفترض ان اللعاب سيدور برأٍه او يحركه الي اليمين او اليسار وبمعنى اخر هو دوران حول المحور yAxis

اما اذا تحركة الي العلى او الاسفل فهو ايضا يقوم بذلك ينظر الي اعلى او الي اسفل وهو دوران حول محور zAxis .

كما نرى( middleX - mousePos.x ) لتحديد يمينا ويسارا اما موضوع القسمه فو لتحديد مقدار الزاويه زعد عليها او انقص لا تخف ولكن تذكر ان مع كل حركة للفأره سوف تقوم بعمل دوران بهذه القيمه...

علشان ماحدش يتفاجأ انا عامل شوية قيود على الدوران حول zAxis لان مش معقوله الشخصيه تنظر من اعلى السقف مثلا

فيجب ان يكون الزاويه مقدار لا تزيد عنه .

كدا بقى الباقي سهل وإن شاء الله مفهوم بمجدر النظر له حا تفهم

بس هو كان في داله خارج الموضوع بتحسب الوقت بين الحركه الاولى والثانيه FrameInterval علشان يكون الحركه سلسه شويه يعني زي فكره timer في .net مفهاش حاجه معقده إن شاء الله .

كنت عاوز اشرح سريعا الصف CNode وهو يقوم برسم wire plane

كل الموضوع انك بتحدد ارتفاع وطول وعرض هذه الارضيه وعدد الحلاقات التي بها وهو يقوم برسم هذه الحلقات مجتمعه لتمثل هذه الارضيه

بس طبعا الغرض مش رسم أرض او plane الغرض استعمال هذه الحلقات لمعرفة اذا ما كانت تنظر اليه الكاميرا اما لا ؟؟؟

ثم اذا كانت تنظر الي عدد حلاقات معين ستعرف مكان الحلقه وبتالي سوف تسأل اي شيء مبني على هذه الارضيه اذا ما كان في هذه الحلقات التي تراها الكاميرا ام لا ؟؟؟؟

ثم تخبر مدير المهام في اللمحرك بأن يرسم هذا المبنى ام لاء ؟؟؟

يعني الكاميرا مش بس بنستفيد منها في ان احنا نشوف العالم الافتراضي وبس دي كمان بتفيد المحرك لمعرفة المستخدم الي ماذا ينظر ودي خااااااصيه خطيره خطيره خطيره جدا جدا جدا

علشان العالم ثالاثي الابعااد مممكن يكون كبير جداجداجدا وانا مش عايز ارسم غير الجزء الي بينظر اليه المستخدم للعبه وبالطريقة دي حيكون عندي تفاصيل اكتر عن المباني والشخصيات في الرسم لان كرت الشاشه مش حايرسم غير جزء من العالم فقط .........

وعلى فكره الي اتكلمت عنه ده هو مفهوم الـ Culling + Furstom وهي من اخطر خصائص محركات الاعاب ؟؟؟

كدا الحمد الله درس النهارده خلص

المرفقات فيها التعديل والصفوف الجديده يااااااريت الناس تجرب وتقول النتيجه ايه واي استفسار او سؤااال انا موجود

المره الجايه إن شاء الله حانشرح مدير الخامات و صندوق السماااء

أرااااكم على خير

والسلام عليكم

camera_in_full_screen_mode.rar

#12

Thank you >>>>

#13

متابع معك ..

- أنا مهتم أكثر بطريقتك في تحريك الكاميرا ... هو الكود اللي يهمني كثيرا ,

- أما Quaternion فهي كلمة تخيفني بصراحة .. لاني لم ادرسها كدراسة أكاديمية .. ولا اعرف بماذا تفيدني ..

قرأت الدروس عنها .. يقولون انها تحل بعض المشاكل التي تظهر مع Rotate العادية .. حقيقة .. لم اواجه اي مشاكل حتى الان ..

أنا متابع معك .. ومتشوق لرؤية النهاية المكتملة ان شاء الله .. استمر ,

logo1.png تطبيق طمأنينة ، نسخة بيتا على أندرويد

عبدالله الشمّري - Al-Shammari

CodingAlone.com

twitter @abshammeri

abshammeri AT gmail.com

github : abshammeri

#14

السلام عليكم

شكرا جزيلا moon land على تعقيبك .. B)

وشكرا عزيز الشمري على تفاعلك معانا

وااااااسف جدا جدا جدا على التأخير كنت مريض شويه معلش :(

اما بالنسبه لموضوع Quaternion فلاتقلق منها مجرد معادله رياضيه بديله لعمل الدوران انا فوجئة زيك كدا ما عرفت عنها بس لو حبيت تعمل دوران على ثلاثة محاور حايحصل معاك مشكله معروفه جد ومشهوره وكان الحل Quaternion

عامة لا تزعج نفسك بها كثير لان

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

واسف مره تانيه على التأخير عليكم بس الظروف بقى...

إن شاء الله ارفعلكم الدرس اليوم ..

والسلام عليكم

#15

السلام عليكم

مرحبا بالجميع .. أخيرا الدرس خلص :lol:

ومشكور لكل من عقب والي لم يعقب بس ياريت يكون في حد بيستفيد بس !!

اولا حبيت اسأل لو في حد عندو مشاكل او ما اشتغل معاه الكود ؟؟

ياريت نتابع ويخبروني ....!

نرجع لموضوعنا

على فكره احنا شغلين كويس يعني لما نكمل الدرس النهارده حانحس بالانجاز الي حاصل لان اهم شيء البنيه التحتيه للمحرك واحنا شغلينا فيها كويس الحمد الله .

درس اليوم زي ما اتفقنا حيكون عن مدير الخامات او مدير الاكساء ( TextureManager) وعن صندوق السماء ( SkyBox ) وعملت تطبيق بسيط على ( TextureManager) يعني علشان تعرف نستفيد من خواصه الزاي ..

بالمناسبه انا حبيت اقول حاجه انه الغرض من مشاركتي التعليم ثم التعليم ثم التعليم يعني انا بفتح سكك و وجهات نظر ليس إلا بس ياااريت الناس تشارك وتتفاعل :angry:

ماذا يعني مدير الاكساء وما وظيفته :

مدير الاكساء عباره عن class زيه زي باقي الشغل بس يفضل او قيمة الاساسيه له عندما لا نستنسخ منه اكثر من مستنسخ واحد وينفرد هو بكل حق الاداره طيب ليه ؟؟

لان الوظيفه الاساسيه ليه او الاداره الي بيمارسها هي توفير وتحسين اداء الذاكره بمعنى

مدير الاكساء نرسل له صوره عاديه وهو يقوم بتحويلها لماده قابله للأكساء او يعني يهيئها انه يضعها على كائن او موديل معين ثم يقوم بتعريف رقم لهذه الماده لكي نستعمله لمناداتها طبعا سهل .. طيب الاداره بقى هنا فين ؟ في محركات الالعاب يكون عندنا عدد كبير صور بالمآت لتحويلها لماده أكسائيه وممكن نستعملها علي دبابه مثلا وعربه طيب المفروض عند انشاء العربه استعمال نفس الخامه في الدبابه يعني مش المفروض اقوم بأنشاء ماده أكسائيه لنفس الصوره بتاعة الدبايه ولكن بأستعمالها مباشرة ودي هي الاداره بتاعة الاستاذ ( TextureManager) ببساطه كدا يضمن لي عدم تكرار انشاء مادة اكسائيه لنفس الصوره وبالتالي التوفير في حجم الزاكره :huh:

طيب خواصه ؟؟

الخواص كثيره تأتي من التعديل والتحوير للصوره الي وضعها على الجسم المراد أكسائه

في مدير الكساء الي حانشرحه حيكون عندنا :

1- تحميل صوره من نوع BMP ( يفضل ان تكون الصوره بهذه الصيغه الانها تحافظ على الالوان كماهي في الرسم عند الحفظ ) ويكون حجمها ( بالبيكسل ) مضاعفات 2 في الطول والعرض .

2- تحويل الصوره الى ماده شفافه .

3- انتزاع لون معين من الصوره .

4- اكساء الجسم بالصوره .

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

نبدأ مع الكود :

عملنا calss TextureManager

#ifndef _TEXTUREMANAGER_H
#define _TEXTUREMANAGER_H

#define MAX_TEX 30

#include"main.h"

class TextureManager{

  int  m_LastTexturID;

  int m_TexturIDIndex[MAX_TEX];
  char tname[MAX_TEX][20];

		 void GetTexture3ds(UINT textureArray[], LPSTR strFileName, int textureID);
	   void loadTextureRGBA( UINT textureArray[], LPSTR strFileName, int textureID,int g_keyColor[3] );	

 public:

	   TextureManager();

	   int GetTexture(char *filename);
	   int GetTextureRGBA(char *filename,int g_keyColor[3]);

	   void LoadTexture(int TexturID);
	   void LoadTexture_Trance(int TexturID);   

	   ~TextureManager();
};


#endif

عندنا GetTexture :

تقوم بتحميل الصوره وتحويلها لـ Texture وتعطينا رقم تعريفها و يفضل حفظ هذا الرقم مع الكائن او الجسم المراده أكسائه وايضا تقوم الداله بعملية الاداره لضمان ان الصوره غيره موجوده مسبقا ... ( سهله عندنا قائمه بأسمها الصور كلها وارقام تعريفها نبحث فيها ذا كان اسم الصوره موجود نرسل رقم تعريفه ولا نقوم بأنشاءه من جديد واذا لم يكن موجود ننشأه ونستكمل الكود عادي )

ومن خلالها نستدعي GetTexture3ds :

وهي الداله الحقيقيه التي تحول الصوره لـ Texture عن طريق طبعا Opengl وترسل رقم تعريفها ( كلها OpenGl ) ا :angry:

على فكره في كذا طريقه لتحويله الصوره لـ Texture احنا استعملنا AUX_ لتحميل الصوره وبياناتها.

عندنا ايضا GetTextureRGBA :

تقوم بنفس وظيفة الداله السابقه بلأضافه انها تأخذ رقم اللون المراد نزعه من الصوره

ومن خلالها نستدعي loadTextureRGBA :

وطبعا فكرة نزع لون من الصوره معروفه وشرحة مسبقا نقوم بالبحث في ألوان الصوره على رقم اللون المطلوب ونجعل قيمة الوضوح له او قناة ألفا بصفر والباقي بأعلى قيمه 255 وباقي الكود زي ما هو OPengl لاعدادت Texture عاادي.

على فكره انا عامل رنج او مدى للون الصوره يعني لو طلبت مثلا رقم اللون 10 انا بنزع كل الالوان الي اعلى من الرقم 10 وصولا مثلا 20 والاقل من 10 وصولا صفر يعني 10 درجات بعده و 10 درجات قبله ... لان في خلفيات بتكون لونها مش درجه واحده زايد شويه او اقل شويه لانها كانت محفوظ بصيغ غير BMP . وممكن بتعديل صغير بالداله تنزع اكثر من لون في نفس الوقت :)

باقي الدوال تقوم بعمل الاكساء على الجسم نعطيها رقم تعريف الصوره وهي تقوم بالاستدلال عليه وتكسو الجسم بها

LoadTexture :

وهي الداله العاديه لاي عملية اكساء, فقط استدعيها واعطيها رقم التعريف .

LoadTexture_Trance :

وهي تقوم بنفس عمل الداله السابقه وايضا بعمل شفافيه للصوره عند الاكساء .

ملاحظه :

انا اقوم بحفظ الصوره بعد إزالة اللون المطلوب حفظها بالذاكره وعند اكسائها فقط نستدعي الداله العاديه LoadTexture بس في أن بالموضوع هههههههه لو عايز تستخدم نفس الصوره ولكن بدون نزع ألوان منها ؟؟؟؟؟؟ حد عارف !

كدا احنا خلصنا هنا , الكوود مفهوش حاجه تتشرح لانه كله OPenGL بس لو حد عندو سؤال يقوووووول ...

#16

نستكمل مع SkyBox او صندوق السماء

على فكره في بردو كرة السماء او SkySpher بس هنا بقى لازم تكون صوره واحده 360 درجه يعني مش صورة مقطعه زي في صندوق السماء

عموما الغرض الاساي من SkyBox / SkySpher هو امتداد النظر بيعطي مساحه او بعد نظري للعالم لي بنيته مهما كان العالم الافترادي محدود وفي حجات كتير ممكن اللاعب ما يستعملهاش بس هي موجوده لأستكمال الموضوع او استكمال العالم الافترادي وتعطي منظر جمالي ايضا لحد فتره قريبه تقريبا من 5 سنوات كان بيستعمل SkyBox ايضا لتمثيل السماء واعتماد كلي عليها دلوقت ممكن نشوف سماء وبها سحب بتتحرك ويتخللها كمان اشعة الشمس وايضا تتغير السماء مع مرور للوقت لتمثل الليل والنهار.. الدنيا اتطوت واحنا بردو اعدين هههههههههههههه

طبعا موضوع MultiTexture حل وفتح حجات كتير وفي فكره تانيه جرب تجيب صوره كبيره للسماء وحطها على شكل مسطح وقوم بتحريكها حاتعطيك منظر حلو :)

عموما SkyBox بتعنا بسيط جدا عباره عن 6 صور نضعها على مكعب بس الـ 6 صور لازم يعبرو عن موضوع واحد او صوره 360 درجه يعني تصور نفسك في غرفتك وقمة بتصوير الاربع حوائط والسقف والارض ضعها على المكعب وستحس بجو غرفتك نوعا ما ...

يبقى الفكره قلنا مكعب بس مش حانستعمل المكعب بتاع OPengl حا نرسم احنا 6 اوجه من خلال داله نعطيها نقطة الاصل او المنتصف والارتفاع والطول وعرض , سهله يعني إن شاء الله ..

نروح نشوف الكود :

عملنا class Environment

#pragma once

#include "TextureManager.h"

extern TextureManager GameTextureManager;

class Environment
{

	int SkyBoxTEXURE[6];
	int TreeTEX;
	int PlanTEX;

public:
	Environment(void);

	void InitSkyBox();
	void InitTree();
	void InitPlan();
	void Setup_Light_Enviroment();
	void CreateSkyBox(float x, float y, float z, 
					  float width, float height, float length);

	void CreatTree(float x,float y,float z,float ScaleTree);
	void CreatPlan(float x,float y,float z,float PlanTEX);
	//void DrawSkyBox();

public:
	~Environment(void);
};

يعني وعملنا شوية شغل فيه هههههههههه

زي ما شفتم قمنا بتعريف TextureManager GameTextureManager بس خلناه extern لان الاساي موجود في main اوك !

InitSkyBox :

وهنا بنقوم بتحميل الصور الخاصه به ( ملاحظه لايمكن تخليق Texture في constractors ) انا جربت ومنفعش شوفو كدا وخبروني !!

void Environment::InitSkyBox()
{
	SkyBoxTEXURE[0]=GameTextureManager.GetTexture("Back.BMP"); 
	SkyBoxTEXURE[1]=GameTextureManager.GetTexture("FRONT.bmp"); 
	SkyBoxTEXURE[2]=GameTextureManager.GetTexture("BOTTOM.bmp"); 
	SkyBoxTEXURE[3]=GameTextureManager.GetTexture("TOP.bmp"); 
	SkyBoxTEXURE[4]=GameTextureManager.GetTexture("LEFT.bmp"); 
	SkyBoxTEXURE[5]=GameTextureManager.GetTexture("RIGHT.bmp"); 
}

CreateSkyBox :

هنا بنرسم SkyBox بناء على النقطه الاصل والابعاد الي اعطينهاله

بنرسم كل وجه عادي وقبل ما نرسم الوحه بنستدعي GameTextureManager.loadTexure لعمل الاكساء دائما بيكون قبل الرسم

وطبعا كلنا عرفين glTexCoord2f حانستعملها هنا بس لتحديد الصوره حا تترسم من الشمال لليمين / من فوق لأسفل او العكس

وبنرسم الوجه بستعمال GL_QUADS .

void Environment::CreateSkyBox(float x, float y, float z, float width, float height, float length)
{

	//Bind the FRONT texture of the sky map to the FRONT side of the cube

	GameTextureManager.LoadTexture(SkyBoxTEXURE[1]);

	// Since we want the sky box to be centered around X, Y, and Z for ease,
	// we do a little math to accomplish this.  We just change the X, Y and Z
	// to perform this task.  If we just minus half the width, height and length
	// from x, y and z it will give us the desired result.  Now when we create the
	// box it will center it around (x, y, z)

	// This centers the sky box around (x, y, z)
	x = x - width  / 2;
	y = y - height / 2;
	z = z - length / 2;

		// Start drawing the side as a QUAD
	glBegin(GL_QUADS);		

		// Assign the texture coordinates and vertices for the FRONT Side
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,			z);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z); 
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height, z);
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x,			y,			z);

	glEnd();

	// Bind the FRONT texture of the sky map to the BACK side of the box

	GameTextureManager.LoadTexture(SkyBoxTEXURE[0]);

	// Start drawing the side as a QUAD
	glBegin(GL_QUADS);	

		// Assign the texture coordinates and vertices for the FRONT Side
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z + length);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height, z + length);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z + length); 
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z + length);
	glEnd();

	// Bind the BOTTOM texture of the sky map to the BOTTOM side of the box

	GameTextureManager.LoadTexture(SkyBoxTEXURE[2]);

	// Start drawing the side as a QUAD
	glBegin(GL_QUADS);		

		// Assign the texture coordinates and vertices for the BOTTOM Side
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y,			z + length);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y,			z + length); 
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z);
	glEnd();

	// Bind the TOP texture of the sky map to the TOP side of the box

	GameTextureManager.LoadTexture(SkyBoxTEXURE[3]);

	// Start drawing the side as a QUAD
	glBegin(GL_QUADS);		

		// Assign the texture coordinates and vertices for the TOP Side
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z);
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y + height, z + length); 
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y + height,	z + length);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height,	z);

	glEnd();

	// Bind the LEFT texture of the sky map to the LEFT side of the box

	GameTextureManager.LoadTexture(SkyBoxTEXURE[4]);

	// Start drawing the side as a QUAD
	glBegin(GL_QUADS);		

		// Assign the texture coordinates and vertices for the LEFT Side
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x,			y + height,	z);	
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x,			y + height,	z + length); 
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x,			y,			z + length);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x,			y,			z);		

	glEnd();

	// Bind the RIGHT texture of the sky map to the RIGHT side of the box

	GameTextureManager.LoadTexture(SkyBoxTEXURE[5]);

	// Start drawing the side as a QUAD
	glBegin(GL_QUADS);		
		// Assign the texture coordinates and vertices for the RIGHT Side
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,			z);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,			z + length);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height,	z + length); 
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height,	z);
	glEnd();
}

void Environment::InitPlan()
{
	PlanTEX = GameTextureManager.GetTexture("BOTTOM.bmp");  
}

void Environment::CreatPlan(float x, float y, float z, float ScalePlan) 
{
	GameTextureManager.LoadTexture(PlanTEX);

	// Start drawing the side as a QUAD
	glBegin(GL_QUADS);		

		// Assign the texture coordinates and vertices for the BOTTOM Side
		glTexCoord2f(1.0f, 0.0f); glVertex3f(x - ScalePlan, y,	z - ScalePlan);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(x - ScalePlan,	y,	z + ScalePlan);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(x + ScalePlan, y,	z + ScalePlan); 
		glTexCoord2f(0.0f, 0.0f); glVertex3f(x + ScalePlan, y,	z - ScalePlan);
	glEnd();

}

ساهل يعني إن شاء الله وبسيط ...

عندك InitPlan :

دي بتحمل الصوره لأرض مسطحه

وبردو CreatPlan :

هنا الرسم غالبا لا يتم الاعتماد على أرض SkyBox

يعني كلها فكره واحده بتعتمد على رسم Plan او مستطيل سواء كان افقى او عمودي ونقوم بأكسائه الخاصيه دي ممكن تخلينا نعمل ايه ؟؟؟؟

رسم النباتات مثلا !!

معظم الالعاب تكون النباتات مجرد شكل لا يقترب منها اللاعب واذا اقترب لا يكون هناك تأثير عليها يعني مش بيقطع شجره مثلا او بيحفر فيها مجرد شكل تكميلي او جمالي

ومنهنا جه فكره رسم النباتات على هيئة صور صورة شجره عاااديه جدا بخلفيه ثابته ( لون واحد ) نقوم بنزعه ونكسو الـ Plan بها فيصبح لدينا شجره

بعيدا عن تعقيدات الرسم وكمان النباتات من اكثر الاشياء استهلاك ( Processing ) لان فيها مئات النقات وكمان تأثير الضوء على كل نقطه شغلانه

عامة معظم محركات الاعاب بتستعمل هذه الطريقه .

InitTree :

تحميل الصوره .

CreatTree :

رسم الصوره :

بس بردو الموضوع في أن ههههههه تاني

سايب انان كتير انا اليوم كويس خلي الناس تتفاعل

الي حايجرب الكود ويشتغل معاه إن شاء الله , حايلاحظ حاجه مش كويسه في النباتات

نفس المشكله دي موجوده مع Sprite لان كلهم نفس الفكره Plan وبينحط عليه صوره .. ياريت الناس تفكر في حل وتطرحه حتى لو مجرد فكره مش شرط الكووود

وان شاء الله المره الجايه نشرح Sprite ولو محدش قال حل اقول انا وأمري لله :rolleyes:

انا الصور الي مستعملها والخامات حرفعها طبعا علشان ماتشفش الدنيا بيضه بس لازم الوان هههههههه

بس حاحاول اضغطها لانها BMP حاحولها Jpg وانتم بعد ما تنزلوها حولوها تاني BMP معلش حا تتعبو شويه

والكوود طبعا والاضافات.. التعديل في main محصور بين :

/* ******************* New code ********************************** */
/* ****************************************************************** */

يلا اشوفكم على خير

السلام عليكم

SkyBox_Tree_Texture_M.rar

#17

مجهود جبار تسلم عليه يا مبدعنا nono00700

انا متابع الموضوع وأتنمي حضرتك تكمل

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

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