به راهنماي OpenGL خوش آمديد . همانطور که در صفحه فهرست هم گفته شد اين راهنما بر پايه NeHe Production بنا شده و در حقيقت بيشتر يک ترجمه آزاد است که متن برنامه ها به دلفي برگردانده شده است و هر جا که لازم ديده ام مطالبي را به آن افزوده ام . و اما دليل اين امر اين بود که خود من هر چه گشتم راهنماي فارسي براي اين موضوع پيدا نکردم . ( هر چند ترجمه ها به فارسي افتضاح است . ولي براي شروع بد نيست ) به همين دليل تصميم گرفتم که راهنماي NeHe را به فارسي برگردانم تا سايرين مشگل من را نداشته باشند . از پاراگراف زير ترجمه اين متن شروع مي شود که در حقيقت از پاراگراف چهارم درس يک NeHe مي باشد . من اين راهنما را با کد نويسي شروع مي کنم . اولين چيزي که شما لازم داريد ساختن يک پروژه جديد در دلفي است . اگر نمي دانيد چطور اينکار را بايد انجام دهيد لازم است ابتدا دلفي را ياد بگيريد . به خاطر داشته باشيد که ما ( حداقل فعلا ) نيازي به فرم نداريم و با همان فايل dpr کار خواهيم کرد . پس فرم را از پروژه برداريد . | ||
program lesson1;
4 خط زير چيزهايي است که برنامه درس اول ما براي اجرا نياز دارد . | ||
uses
Windows, // بخش ويندوز ( براي تعاريف متغيرها )
Messages, // بخش پيغامها ( براي بخش چک کردن وضعيت برنامه )
OpenGL; // OpenGL بخش اصلي
حالا ما بايد متغيرهايي را که برنامه مان به آنها نياز دارد را تعريف کنيم . اين برنامه يک صفحه خالي مي سازد . پس ما نيازي به تنظيم متغيرهاي زيادي نداريم . ولي همين چند متغيري که در پايين مي بينيد بسيار با اهميت هستند و در تمام برنامه هايي که ما مي نويسيم استفاده خواهند شد . خط دوم يک متغير براي Rendering Context تعريف مي کند . ( مترجم متن !؟! ) . هر برنامه مبتني بر OpenGLبه يک متغير از نوع مترجم متن ( Rendering Context )متصل مي شود . يک Rendering Context تمام مراجعات OpenGL را به يک Device Contextمتصل مي کند . ما اين متغير را h_Rc مي ناميم . براي اينکه برنامه ما در يک پنجره ( window )تمام عمليات ترسيم ( draw )خود را انجام مي دهد ما نياز به يک متغير Device Context داريم که در خط سوم آنرا تعريف کرده ايم . اين متغير h_Dc ناميده شده است . DCپنجره (window) را به GDIيا همان رابط گرافيکي ( Graphics Device Interface) متصل مي کند . RCبخش OpenGLرا به DC متصل مي کند . در خط چهارم متغير h_Wnd دستگيره (handle) ي را که ويندوز به پنجره ما اختصاص داده است نگه مي دارد . | ||
var
h_Rc: HGLRC; // Permanent Rendering Context
h_Dc: HDC; // Private GDI Device Context
h_Wnd:HWND; // Holds Our Window Handle
اولين خط يک آرايه براي نگهداري دکمه هاي فشرده شده بر روي صفحه کليد تعريف مي کند . راههاي زيادي براي اينکه بفهميم چه دکمه هايي از صفحه کليد فشار داده شده اند وجود دارد . ولي راهي که من انجام مي دهم اين است . اين راه قابل اطمينان بوده و همينطور قابليت نگهداري فشرده شدن همزمان چند دکمه را نيز دارد ( يعني اگر در زماني که مشغول انجام کاري هستيم و قابليت آن را نداريم که همان لحظه کار مورد نظر را انجام دهيم مي توانيم کليدها را نگه داشته و سر فرصت يکي يکي پردازش مورد نظر را انجام دهيم ) .متغير activeبراي اين بدرد مي خورد که ببينيم آيا برنامه ما کوچک ( minimize ) شده و به قسمت task bar رفته يا اينکه فعال است . اگر کوچک شده است ما مي توانيم کارهايي براي معوق گذاردن عمليات انجام دهيم که با خروج از برنامه متفاوت است . من اين کار را ترجيح مي دهم زيرا باعث مي شود که برنامه ما در background اجرا نشود . ( در بازيهاي معروف و حتي غير معروف هم اگر با Alt+Tab به صفحه ويندوز برگرديد مشاهده مي کنيد که برنامه suspend شده و عملا از cpu استفاده نمي کند ) متغير FullScreen< هم که واضح است . اگر برنامه در حالت تمام صفحه باشد اين متغير درست (true) بوده و در غير اينصورت نادرست . اين متغير بايد به صورت عمومي (global) باشد تا تمام روالها و زير برنامه ها از اين موضوع مطلع شوند . | ||
keys: array [0..255] of BOOL; // Array Used For The Keyboard Routine
Active:bool; // Window Active Flag
FullScreen:bool; // Fullscreen Flag
کار اين روال اين است که اگر شما در حالتي غير از تمام صفحه (fullscreen) اندازه پنجره را تغيير دهيد چشم انداز OpenGL را تغيير دهد . اگر شما در حالت تمام صفحه هم باشيد اين روال حداقل يکبار در ابتداي کار صدا زده خواهد شد تا نماي پرسپکتيو ما را تنظيم کند . چشم انداز يا نماي OpenGL ما به اندازه پنجره اي خواهد بود که در آن نمايش داده مي شود . | ||
procedure ReSizeGLScene(Width: GLsizei; Height: GLsizei); //Resize And Initialze The GL Window
var
fWidth, fHeight: GLfloat;
begin
if (Height=0) then // Prevent A Divide By Zero If The Window Is Too Small
Height:=1; // By Making The Height One
glViewport(0, 0, Width, Height); // Reset The Current Viewport And Perspective Transformation
خطوط زير صفحه را براي يک نمايش پرسپکتيو تنظيم مي کنند ( دو خط اول به دليل اينست که دلفي و در حقيقت پاسکال از خاصيت تغيير اتوماتيک نوع متغيرها برخلاف سي C برخوردار نيست ) . اجسام فاصله دار کوچک تر خواهند بود . اين کار يک منظره واقعي تر رسم مي کند . پرسپکتيو با يک زاويه 45 درجه که بر پايه طول و عرض صفحه بنا مي شود محاسبه خواهد شد . مختصات 0.1 و 100 (100و0.1) نقطه شروع و نقطه پايان آخرين عمقي است که ما در صفحه مي توانيم رسم کنيم . glMatrixMode(GL_PROJECTION)مشخص مي کند که دو دستور بعدي براي OpenGLبر روي ماتريس ترسيم Projection Matrix ) تاثير مي گذارند . اين ماتريس مسئول اضافه کردن حالت پرسپکتيو به خروجي تصوير ماست . glLoadIdentity() مانند حالت ريست است . و ماتريس مشخص شده را به حالت اصلي (original) خود مي برد . بعد از صدا زدن glLoadIdentity() ما مقادير خودمان را براي ساختن نماي پرسپکتيو تنظيم مي کنيم . glLoadIdentity()مشخص مي کند که تمام تغييرات جديد بر روي ماتريس مدل نمايش (modelview matrix) تاثير مي گذارند .اين ماتريس جايي است که اطلاعات اشياء(object) ما نگهداري مي شوند . در نهايت ما اين ماتريس را ريست مي کنيم . اگر چيزي را اينجا متوجه نشديد زياد هم نگران نباشيد زيرا من آنها را در ساير بخشها دوباره توضيح خواهم داد . فعلاً اينقدر بدانيد که اين اعمال براي اينکه منظره پرسپکتيو خوبي داشته باشيد لازم است . | ||
fWidth := width;
fHeight := height;
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
gluPerspective(45.0,fWidth/fHeight,0.1,100.0);// Calculate The Aspect Ratio Of The Window
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity //Reset The Modelview Matrix
end;
در اين قسمت ما تمام اقدامهايي را که براي تنظيم OpenGL لازم است انجام خواهيم داد . ما رنگي را براي پاک کردن صفحه تنظيم مي کنيم بافر لازم براي عمق بخشي به کارمان را مشخص مي کنيم روش smooth shading را فعال مي کنيم و ... اين بخش صدا زده نخواهد شد مگر زماني که صفحه OpenGL ساخته شده باشد . اين تابع مقداري را برمي گرداند که به دليل اينکه فعلا بخش ارزش دهي آغازين (initialization) ما چندان پيچيده نيست از آن صرف نظر مي کنيم . | ||
function InitGL:bool; // All Setup For OpenGL Goes Here
begin
خط زير حالت smooth shading را فعال مي کند . اين حالت باعث مي شود که رنگهاي دور يک چند ضلعي زيبا تر به نظر برسند و خطوط صاف روشن تر بنظر برسند . راجع به اين حالت بعدا بيشتر صحبت خواهيم کرد . | ||
glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
خط زير رنگي را مشخص مي کند که صفحه با آن پاک خواهد شد . اگر نمي دانيد که ترکيب رنگها چگونه است ، من آنرا شرح مي دهم . محدوده عددي رنگها از صفر تا يک تغيير مي کند . اين عدد بصورت مميز دار است . صفر به معني تيره ترين و يک به معني روشن ترين است . اولين پارامتر بعد از glClearColor رنگ قرمز است . دومي براي سبز و سومي براي آبي ( قرمزته !!! ) . بالاترين عدد يک است بمعني روشن ترين . آخرين عدد مقدار آلفا ناميده مي شود که در هنگام تنظيم رنگ براي پاک کردن صفحه بکار نمي رود . فعلا با مقدار آن کاري نداريم . بعداً آنرا توضيح خواهم داد . شما مي توانيد با ترکيب رنگها از سه رنگ اصلي رنگ جديدي بسازيد . مثلاً glClearColor(0.0f,0.0f,1.0f,0.0f) صفحه را با رنگ آبي روشن پاک مي کند . و glClearColor(0.5f,0.0f,0.0f,0.0f) صفحه را با رنگ قرمز متوسط پاک مي کند . نه روشن ( يک ) و نه پررنگ ( صفر ) براي يک پس زمينه سفيد شما بايد تمام رنگها را به بالاترين حد ممکن يعني يک تنظيم کنيد . و براي مشکي به کمترين حد ممکن يعني صفر . | ||
glClearColor(0.0, 0.0, 0.0, 0.5); // Black Background
سه خط بعدي براي تنظيم بافر عمق بکار مي روند . بافر عمق را مانند لايه ها در نظر بگيريد . اين بافر مشخص مي کند که اشياء روي صفحه چه عمقي دارند . ما در اين مثال از اين بافر استفاده نخواهيم کرد ولي هر برنامه OpenGLکه بر روي صفحه اشياء سه بعدي رسم مي کند از اين بافر استفاده مي کند . اين بافر ترتيب کشيده شدن اشياء را بر روي صفحه مشخص مي کند که به عنوان مثال دايره جلوتر است يا مربع و گوشه هاي مربع بايد مشخص باشد يا نه . اين بافر يکي از پر اهميت ترين بخشهاي OpenGL است . | ||
glClearDepth(1.0); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LESS); // The Type Of Depth Test To Do
در خط زير ما به OpenGL مي گوييم که بهترين تصحيح سازي را براي پرسپکتيو مي خواهيم . اينکار سرعت عملکرد (performance) ما را کمي پايين مي آورد ولي نتيجه تصويري بهتري را به ارمغان مي آورد . | ||
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);//Realy Nice perspective calculations
در نهايت مقدار درست (true) را بر مي گردانيم تا اگر خواستيم در بخش مقدار دهي اوليه آنرا چک کنيم بتوايم . ما در اين مثال جايي را براي خطا نداشتيم ولي شما مي توانيد اين بخش را به برنامه خود اضافه کنيد . فعلا اين بخش براي ما اهميتي ندارد . | ||
initGL:=true; // Everything went fine
end;
اين بخش شامل تمام چيزهايي مي شود که شما مي خواهيد بر روي صفحه
رسم کنيد . هر چيزي که مي خواهيد برروي صفحه رسم کنيد در اين بخش از کد قرار مي
گيرد . هر درس بعد از اين درس بخش و يا بخشهايي را به اين قسمت اضافه مي کند . اگر
از OpenGL سر در مي آوريد ( و يا
آورديد ) مي توانيد اشياء مورد نظر خود را بعد از
glLoadIdentity()
و قبل از Result:=true; رسم کنيد .
ولي اگر تازه OpenGL را شروع کرده
ايد تا درس بعدي صبر کنيد . الان ما صفحه را با رنگي که قبلا مشخص کرده بوديم پاک
مي کنيم ، بافر عمق و منظره را نيز پاک مي کنيم . ما اينجا چيزي رسم نمي کنيم .
| ||
function DrawGLScene():bool; // Here's Where We Do All The Drawing
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity(); // Reset The View
Result:=true; // Everything Went OK
end;
اين بخش از برنامه قبل از خروج از برنامه يکبار صدا زده مي شود . کار KillGLWindow() اين است که Rendering Contextو Device Context و در نهايت هندل صفحه را آزاد کند . اگر در هر مرحله اي برنامه نتواند يکي از اين کارها را انجام دهد با يک پيغام اين خطا را به شما اعلام مي کند تا ايراد يابي راحت تر انجام گيرد . | ||
procedure KillGLWindow; //Properly Kill The Window
begin
اولين کاري که انجام مي دهيم اينست که ببينيم آيا در حالت تمام صفحه هستيم يا خير . اگر بوديم بايد به صفحه desktop برگرديم . ما مي توانيم پنجره را نابود کنيم (destroy) قبل از اينکه حالت تمام صفحه را غير فعال کنيم ولي بر روي بعضي از کارتهاي گرافيکي اين امر منجر به خراب شدن desktop خواهد شد . پس ما اول حالت تمام صفحه را غير فعال مي کنيم . اينکار جلوي خراب شدن desktop را مي گيرد و بر روي کارتهاي nvidia و 3dfx کار خواهد کرد . ( بر روي سيستم من با کارت گرافيک Radeon 9000 Pro IIو ويندوز 2000 سرور با سرويس پک 4 باز هم کار نمي کند !!! ) | ||
if FullScreen then //Are We In Fullscreen Mode?
begin
ما از ChangeDisplaySettings(devmode(nil^),0); براي باز گشت به desktop اصلي استفاده مي کنيم . ارسال nil به عنوان پارامتر اول و صفر به عنوان پارامتر دوم به ويندوز مي گويد که از مقادير ثبت شده در رجيستري براي تنظيم desktop استفاده کند ( براي رزولوشن ، فرکانس ، تعداد رنگ و ... ) . سپس ما کرسر (cursor) را مجددا نمايش مي دهيم . | ||
ChangeDisplaySettings(devmode(nil^),0); //Switch Back To The Desktop
showcursor(true); //Show The Mouse Pointer
end;
کد زير ابتدا چک مي کند که آيا ما از Rendering Context استفاده مي کنيم يا نه . اگر استفاده نمي کنيم به قسمت پايين تر براي چک کردن اينکه آيا از Device Context استفاده مي کنيم يا نه مي رود . | ||
if h_rc<>0 then //Is There A Rendering Context?
Begin
اگر يک Rendering Context داريم کد زير چک مي کند تا ببيند که آيا مي توانيم آنرا از بين ببريم (release) يا نه ( جدا کردن h_rc از h_dc ) . به روشي که من براي چک کردن خطا به کار برده ام توجه کنيد . ابتدا با دستور wglMakeCurrent(h_Dc,0) به برنامه مي گويم که سعي در بستن RC کند . سپس چک مي کنم که آيا اينکار بدرستي انجام شده است يا نه . ترکيب چند خط کد در يک خط . | ||
if (not wglMakeCurrent(h_Dc,0)) then //Are We Able To Release Dc and Rc contexts?
اگر ما نتوانيم که
RC
و DC
را از بين ببريم با يک پيغام خطا برنامه اعلام خطا مي کند . براي راهنماي MessageBox
لطف کرده و در محيط دلفي روي اين دستور دکمه F1
را بزنيد | ||
MessageBox(0,'Release of DC and RC failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
>حالا ما سعي مي کنيم که Rendering Context را از بين ببريم . اگر به خطايي برخورديم پيغامي نمايش داده خواهد شد . | ||
if (not wglDeleteContext(h_Rc)) then //Are We Able To Delete The Rc?
begin
اگر ما نتوانيم Rendering Context را از بين ببريم کد زير پيغام خطايي نمايش خواهد داد و به h_Rcمقدار صفر خواهد داد . | ||
MessageBox(0,'Release of Rendering Context failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
h_Rc:=0; //Set Rc To Null
end;
end;
حالا ما چک مي کنيم که آيا برنامه ما از Device Context استفاده مي کند يا نه و سپس سعي در بستن آن مي کنيم . اگر نتوانيم زير پيغام خطايي نمايش خواهيم داد و به h_Dcمقدار صفر خواهيم داد . | ||
if (h_Dc=1) and (releaseDC(h_Wnd,h_Dc)<>0) then //Are We Able To Release The Dc?
Begin
MessageBox(0,'Release of Device Context failed.',' Shutdown Error',MB_OK or MB_ICONERROR);
h_Dc:=0; //Set Dc To Null
end;
حالا همان کارها را در مورد هندل پنجره انجام مي دهيم . | ||
if (h_Wnd<>0) and (not destroywindow(h_Wnd))then //Are We Able To Destroy The Window?
begin
MessageBox(0,'Could not release hWnd.',' Shutdown Error',MB_OK or MB_ICONERROR);
h_Wnd:=0; //Set hWnd To Null
end;
<بخش بعدي کد صفحه
OpenGL
را براي ما مي سازد . من مدتي در اين فکر بودم که من بايد يک صفحه ثابت تمام صفحه
را بسازم که مقداري کد اضافه نخواهد و يا اينکه يک صفحه کاربر پسند (user friendly)
با مقداري کد نويسي بيشتر . من به اين نتيجه رسيدم که گزينه دوم بهترين انتخاب است
. من هميشه با اين سوال در email
مواجه بودم که براي اينکه يک صفحه غير از تمام صفحه بخواهم چه بايد بکنم . چگونه
caption
فرم ( صفحه ) را تغيير بدهم . چگونه رزولوشن يا تعداد رنگها را مشخص کنم . کد زير
پاسخي براي تمام اينهاست .
| ||
function CreateGlWindow(title:Pchar; width,height,bits:integer;FullScreenflag:bool):boolean stdcall;
وقتي ما از ويندوز مي خواهيم که فرمت پيکسلي را که ما مي خواهيم مطابقت دهد ، تعداد مودها (mode)يي که ويندوز براي ما پيدا کرده است در متغير PixelFormatذخيره مي شود . | ||
var
Pixelformat: GLuint; //Holds The Result After Searching For A Match
Wcبراي نگه داشتن ساختار کلاس پنجره ما استفاده مي شود که اطلاعات مربوط به پنجره ما در آن نگه داري مي شود . با تغيير دادن فيلدهاي مختلف اين کلاس ما مي توانيم شکل و رفتار پنجره را تغيير دهيم . هر پنجره به يک کلاس پنجره تعلق دارد . شما بايد يک کلاس براي پنجره رجيستر کنيد . | ||
wc:TWndclass; //Windows Class Structure
dwExStyleو dwStyleدر حالتهاي پيشرفته و معمولي اطلاعات حالت (style)پنجره ما را نگه مي دارند . من از متغيرها براي ذخيره کردن اين اطلاعات استفاده مي کنم تا بتوانم حالت پنجره را بسته به نياز تغيير دهم . ( براي حالت popupيا تمام صفحه يا داراي حاشيه براي حالت غير تمام صفحه ) . | ||
dwExStyle:dword; //Extended Window Style
dwStyle:dword; //Window Style
متغير pfd نوع و حالت نقاط را نگه مي دارد . dmScreenSettings حالت صفحه و h_Instance شماره instance برنامه را | ||
pfd: pixelformatdescriptor; //Tells Windows How We Want Things To Be
dmScreenSettings: Devmode; //Device Mode
h_Instance:hinst; // Holds The Instance Of The Application
دو خط بعدي ابتداinstance برنامه را پيدا کرده و سپس متغير عمومي FullScreen را مساوي fullscreenflag قرار مي دهيم . | ||
begin
h_instance:=getmodulehandle(nil); //Grab An Instance For Our Window
FullScreen:=FullScreenflag; //Set The Global Fullscreen Flag
حالا ما کلاس پنجره خود را تعريف مي کنيم . حالت CS_HREDRAW و CS_VREDRAW پنجره را وادار مي کنند که هر بار که تغيير اندازه داد دوباره رسم شود . CS_OWNDCيک DC اختصاصي براي پنجره مي سازد . يعني اينکهDC بين برنامه ها مشترک نيست و براي هر برنامه يک عدد اختصاصي و يکتا (unique) است . WndProc تابعي است که پيغامها را در برنامه گرفته و پردازش مي کند . ساير اطلاعات اضافه استفاده نمي شود . پس دو متغير را مقدار صفر مي دهيم . سپس مقدار instance را تنظيم مي کنيم . بعد ما مقدار آيکون را صفر قرار مي دهيم زيرا از آيکون براي برنامه مان استفاده نمي کنيم ، و براي ماوس از حالت استاندارد استفاده مي کنيم . رنگ پس زمينه مهم نيست زيرا آنرا توسط OpenGL تعريف کرده ايم . ما از منو استفاده نخواهيم کرد ، پس مقدار آنرا nil قرار مي دهيم . و در نهايت نام کلاس آنرا OpenGL (براي راحتي کار) قرار مي دهيم . | ||
with wc do
begin
style:=CS_HREDRAW or CS_VREDRAW or CS_OWNDC; //Redraw On Size -- Own DC For Window
lpfnWndProc:=@WndProc; //WndProc Handles The Messages
cbClsExtra:=0; //No Extra Window Data
cbWndExtra:=0; //No Extra Window Data
hInstance:=h_Instance; //Set The Instance
hIcon:=LoadIcon(0,IDI_WINLOGO); //Load The Default Icon
hCursor:=LoadCursor(0,IDC_ARROW); //Load The Arrow Pointer
hbrBackground:=0; //No BackGround Required For OpenGL
lpszMenuName:=nil; //We Don't Want A Menu
lpszClassName:='OpenGl'; //Set The CLass Name
end;
حالا بايد کلاس خود را رجيستر کنيم . اگر خطايي رخ دهد پيغام به نمايش درآمده و از برنامه خارج مي شويم . | ||
if RegisterClass(wc)=0 then //Attempt To Register The Window Class
begin
MessageBox(0,'Failed To Register The Window Class.','Error',MB_OK or MB_ICONERROR);
CreateGLwindow:=false; //Return False
exit; //Exit
end;
حالا ما چک مي کنيم که آيا در حالت تمام صفحه قرار داريم يا خير . | ||
if FullScreen then //Attempt Fullscreen Mode
begin
اين قسمت از کد جايي است که اکثر کاربران با آن مشگل دارند . چند چيز خيلي مهم وجود دارد که شما بايد در حالت تمام صفحه به آن توجه کنيد . بايد مطمئن شويد که عرض و ارتفاع حالت تمام صفحه با عرض و ارتفاع صفحه شما يکي است و مهمترين چيز آنست که ابتدا به حابت تمام صفحه سوئيچ کنيد قبل از آنکه پنجره خود را بسازيد . در اين کد شما نبايد نگران اين موضوع باشيد ، اندازه حالت تمام صفحه و پنجره شما يکي است . | ||
ZeroMemory( @dmScreenSettings, sizeof(dmScreenSettings) ); //Makes Sure Memory's Available
with dmScreensettings do
begin
dmSize := sizeof(dmScreenSettings); //Size Of The Devmode Structure
dmPelsWidth := width; //Selected Screen Width
dmPelsHeight := height; //Selected Screen Height
dmBitsPerPel := bits; //Selected Bits Per Pixel
dmFields := DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
end;
در کد بالا ما فضاي لازم براي تنظيمات خود را اختصاص مي دهيم . ما عرض ، ارتفاع و تعداد رنگها را به مقداري که براي پنجره خود مي خواهيم تنظيم مي کنيم . در کد زير ما سعي مي کنيم که تنظيمات خود را اعمال کنيم . ما تمام اين اطلاعات را در dmScreenSettings ذخيره مي کنيم . در کد پايين دستور ChangeDisplaySettingsسعي مي کند که صفحه را به حالتي که توسط dmScreenSettings مشخص شده ببرد . من از پارامتر CDS_FULLSCREEN براي تغيير مود استفاده کردهيم ، زيرا اين حالت دکمه start و taskbar را مخفي ميکند بدون اينکه اندازه ساير پنجره هاي باز را تغيير دهد يا مکان آنها را عوض کند وقتي که به حالت تمام صفحه مي رويد و يا به حالت desktop بر مي گرديد . | ||
//Try To Set The Selected Mode And Get Results. CDS_FullScreen Gets Rid Of The Start Bar
if (ChangeDisplaySettings(dmScreenSettings, CDS_FullScreen))<>DISP_CHANGE_SUCCESSFUL THEN
Begin
اگر نتوانيم به حالت درخواست شده سوئيچ کنيم کد زير اجرا مي شود . اگر حالت تمام صفحه مورد نظر وجود نداشته باشد کد زير پيغامي با دو گزينه نمايش مي دهد . يکي براي اجرا در حالت پنجره اي و ديگري براي خروج از برنامه . | ||
if MessageBox(0,'This FullScreen Mode Is Not Supported. Use Windowed Mode Instead?'
,'NeHe GL',MB_YESNO or MB_ICONEXCLAMATION)= IDYES then
اگر کاربر تصميم بگيرد که از حالت پنجره اي استفاده کند متغير fullscreen به مقدار نادرست تغيير داده مي شود و اجراي برنامه ادامه مي يابد . | ||
FullScreen:=false //Select The Windowed Mode
else
begin
اگر کاربر تصميم به خروج بگيرد يک پيغام به کاربر مي گويد که برنامه بسته خواهد شد . مقدار نادرست برگردانده خواهد شد که به برنامه مي گويد پنجره ساخته نشده است و برنامه بسته مي شود . | ||
//Popup A Message Box Letting The User Know The Program Is Closing
MessageBox(0,'Program Will Now Close.','Error',MB_OK or MB_ICONERROR);
CreateGLWindow:=false; //Return False
end;
end;
end;
چون حالت تمام صفحه ممکن است به خظا برخورده باشد و کاربر تصميم به اجراي برنامه در حالت پنجره اي گرفته باشد ، ما بايد دوباره متغير fullscreen را چک کنيم . | ||
if FullScreen then //Check If We're Still In Fullscreen Mode
begin
اگر ما حالت تمام صفحه را داشته باشيم ما بايد حالت پيشرفته WS_EX_APPWINDOW را انتخاب کنيم که ساير پنجره هاي سطح بالا را به taskbar ببرد وقتي که پنجره ما قابل رويت (visible) مي شود . در حالت پنجره اي ما حالت WS_POPUP را انتخاب مي کنيم که يک پنجره بدون حاشيه (border) مي سازد که براي حالت تمام صفحه هم مناسب است . در نهايت ما نشانگر ماوس را هم غير فعال مي کنيم . اگر برنامه از ماوس استفاده نمي کند اين کار ايده خوبي است . به هر حال تصميم با شماست . | ||
dwExStyle:=WS_EX_APPWINDOW; //Extended Window Style
dwStyle:=WS_popup or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; //Window Style
Showcursor(false); //Hide Mouse Pointer
end
else
begin
اگر ما از حالت پنجره اي به جاي تمام صفحه استفاده مي کنيم ، بايد حالت WS_EX_WINDOWEDGE را به حالت پيشرفته اضافه مي کنيم . اين کار حالت سه بعدي بهتري به پنجره مي دهد . براي style ما از WS_OVERLAPPEDWINDOW به جاي WS_POPUP استفاده مي کنيم . WS_OVERLAPPEDWINDOW يک پنجره با title bar ، قاب قابل تغيير اندازه ، منوي اصلي و دکمه minimize/maximize مي سازد . | ||
dwExStyle:=WS_EX_APPWINDOW or WS_EX_WINDOWEDGE; //Extended Window Style
dwStyle:=WS_OVERLAPPEDWINDOW or WS_CLIPSIBLINGS or WS_CLIPCHILDREN; //Windows Style
end;
در بخش بعدي ما
براي ساختن پنجره خود اقدام مي کنيم و چک مي کنيم که آيا درست مطابق خواسته ما
ساخته شده است يا نه . ما تمام مقاديري را که
CreateWindowEx()
نياز دارد را برايش مي فرستيم . حالت پيشرفته اي را که تصميم به استفاده از آن
گرفته ايم . نام کلاس ( همان نامي که در موقع رجيستر کردن مشخص کرديم ) . نوشته
بالاي صفحه ، حالت پنجره ، مختصات بالا – سمت چپ پنجره ( 0و0 بهترين حالت است ) ،
عرض و ارتفاع پنجره ، ما يک پنجره والد (parent)
نمي خواهيم و همينطور منو پس اين دو پارامتر را تهي (nil)
مي فرستيم . ما instance
پنجره را و در نهايت هم يک تهي ديگر به عنوان پارامتر مي فرستيم . | ||
H_wnd:=CreateWindowEx(dwExStyle, //Extende Style For The Window
'OpenGl', //Class Name
Title, //Window Title
dwStyle, //Window Style
0,0, //Window Position
width,height, //Selected Width and Height
0, //No Parent Window
0, //No Menu
hinstance, //Instance
nil); //Don't Pass Anything To WM_CREATE
حالا ما تست مي کنيم تا ببينيم آيا پنجره ما بدرستي ساخته شده است يا خير . اگر پنجره ما ساخته شده باشد h_Wnd شامل اشاره گري (handle)به صفحه ما خواهد بود . اگر پنجره بدرستي ساخته نشود کد زير يک پيغام خطا نمايش مي دهد و از برنامه خارج مي شود . | ||
if h_Wnd=0 then //If The Window Creation Failed
begin
KillGlWindow(); //Reset The Display
MessageBox(0,'Window creation error.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
کد زير يک فرمت براي نقاط (pixel format) تعريف مي کند . ما فرمتي را انتخاب مي کنيم که از OpenGL و همينطور از بافر دوگانه را پشتيباني کند ، و RGBA را (Red,Green,Blue,Alpha channel) . ما فرمتي را انتخاب مي کنيم که تعداد رنگهاي آن با مقداري که قبلاً تصميم گرفته بوديم يکي باشد (16bit,24bit,32bit) . در نهايت ما يک Z-Buffer 16 بيتي تعريف مي کنيم . ساير مقادير يا استفاده نمي شوند و يا مهم نيستند . | ||
with pfd do //Tells Windows How We Want Things To Be
begin
nSize:= SizeOf( PIXELFORMATDESCRIPTOR ); // Size Of This Pixel Format Descriptor
nVersion:= 1; // Version Number (?)
dwFlags:= PFD_DRAW_TO_WINDOW // Format Must Support Window
or PFD_SUPPORT_OPENGL // Format Must Support OpenGL
or PFD_DOUBLEBUFFER; // Must Support Double Buffering
iPixelType:= PFD_TYPE_RGBA; // Request An RGBA Format
cColorBits:= bits; // Select Our Color Depth
cRedBits:= 0; // Color Bits Ignored
cRedShift:= 0;
cGreenBits:= 0;
cBlueBits:= 0;
cBlueShift:= 0;
cAlphaBits:= 0; // No Alpha Buffer
cAlphaShift:= 0; // Shift Bit Ignored
cAccumBits:= 0; // No Accumulation Buffer
cAccumRedBits:= 0; // Accumulation Bits Ignored
cAccumGreenBits:= 0;
cAccumBlueBits:= 0;
cAccumAlphaBits:= 0;
cDepthBits:= 16; // 16Bit Z-Buffer (Depth Buffer)
cStencilBits:= 0; // No Stencil Buffer
cAuxBuffers:= 0; // No Auxiliary Buffer
iLayerType:= PFD_MAIN_PLANE; // Main Drawing Layer
bReserved:= 0; // Reserved
dwLayerMask:= 0; // Layer Masks Ignored
dwVisibleMask:= 0;
dwDamageMask:= 0;
end;
اگر در زمان ساختن صفحه خطايي رخ ندهد ما سعي در گرفتن يک Device Context براي OpenGL مي کنيم . اگر ما نتوانيم يک DC بگيريم خطايي نمايش داده شده و از برنامه خارج مي شويم . | ||
h_Dc := GetDC(h_Wnd); // Try Getting A Device Context
if h_Dc=0 then // Did We Get Device Context For The Window?
begin
KillGLWindow(); //Reset The Display
MessageBox(0,'Cant''t create a GL device context.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
اگر توانستيم يک DC براي OpenGL در نظر بگيريم حالا بايد به دنبال يک pixel format که با مال ما يکي باشد بگرديم . اگر ويندوز نتواند چنين حالتي را پيدا کند پيغام خطايي نمايش داده شده و از برنامه خارج مي شويم . | ||
PixelFormat := ChoosePixelFormat(h_Dc, @pfd);// Finds The Closest Match To The Pixel Format We Set Above
if (PixelFormat=0) then //Did We Find A Matching Pixelformat?
begin
KillGLWindow(); //Reset The Display
MessageBox(0,'Cant''t Find A Suitable PixelFormat.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
اگر ويندوز توانست حالت نقاط مورد نظر را پيدا کند ما سعي مي کنيم که آنرا به کار بگيريم . اگر نتوانيم پيغام خطايي نمايش داده شده و از برنامه خارج مي شويم . | ||
if (not SetPixelFormat(h_Dc,PixelFormat,@pfd)) then //Are We Able To Set The Pixelformat?
begin
KillGLWindow(); //Reset The Display
MessageBox(0,'Cant''t set PixelFormat.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
اگر حالت نقاط به کار گرفته شد ما سعي در بدست آوردن يک Rendering Context مي کنيم . اگر نتوانيم پيغام خطايي نمايش داده شده و از برنامه خارج مي شويم . | ||
h_Rc := wglCreateContext(h_Dc); //Are We Able To Get A Rendering Context?
if (h_Rc=0) then
begin
KillGLWindow(); //Reset The Display
MessageBox(0,'Cant''t create a GL rendering context.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
اگر به هيچ خطايي برنخورده باشيم و توانسته باشيم که DC و RC مورد نظر را بسازيم تنها چيزي که بافي مي ماند فعال ساختن RC است . اگر نتوانيم آنرا فعال بسازيم پيغام خطايي نمايش داده شده و از برنامه خارج مي شويم . | ||
if (not wglMakeCurrent(h_Dc, h_Rc)) then //Are We Able To Activate The Rendering Context?
begin
KillGLWindow(); //Reset The Display
MessageBox(0,'Cant''t activate the GL rendering context.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
اگر همه چيز بخوبي پيش رفته باشد و پنجره OpenGL ما ساخته شده باشد ما پنجره خود را به نمايش در مي آوريم ، آنرا به صورت جلو زمينه (foreground)در مي آوريم ( تا از priority بالاتري استفاده کند ) و سپس کنترل را به صفحه خود منتقل مي کنيم . سپس با صدا زدن ReSizeGLSceneبا پارامترهاي عرض و ارتفاع صفحه حالت پرسپکتيو OpenGL را تنظيم مي کنيم . | ||
ShowWindow(h_Wnd,SW_SHOW); //Show The Window
SetForegroundWindow(h_Wnd); //Slightly Higher Priority
SetFOcus(h_Wnd); //Set Keyboard Focus To The Window
ReSizeGLScene(width,height); //Set Up Our Perspective Gl Screen
در نهايت روال InitGL() صدا زده مي شود تا نورها texture و هر آن چه لازم داريم تنطيم شود . شما مي توانيد خطاهاي مختلف را در InitGL چک کنيد و درست را بفرستيد اگر همه چيز بخوبي پيش رفت و گرنه نادرست را برگردانيد تا بتوانيد برنامه را متوقف کنيد . اگر شما نادرست را برگردانيد خطوط زير پيغام خطايي برگردانده و از برنامه خارج مي شويم . | ||
if (not InitGl()) then //Can we Initialize The Newley Created GL Window
begin
KillGLWindow(); //Reset The Display
MessageBox(0,'initialization failed.','Error',MB_OK or MB_ICONEXCLAMATION);
CreateGLWindow:=false; //Return False
exit;
end;
حالا که همه چيز بخوبي پيش رفته و به خطايي برنخورده ايم مقدار درست را بر مي گردانيم تا به WinMain() هم اين موضوع را اعلام کنيم که از برنامه خارج نشود . | ||
CreateGLWindow:=true; //Succes
end;
اينجا جايي است که ما تمام پيغامهايي را که به پنجره ما مي رسد پردازش مي کينم . وقتي ما کلاس پنجره خود را رجيستر کرديم ما مشخص کرديم که براي پيغامها به اين بخش از برنامه ما مراجعه شود . | ||
function WndProc(hWnd: HWND; //Handle For The Window
message: UINT; //Message For This Window
wParam: WPARAM; //Additional Message Information
lParam: LPARAM): //Additional Message Information
LRESULT; stdcall;
begin
ابتدا حالتي را که محافظ صفحه نمايش (sceern saver) مي خواهد شروع شود و يا مانيتور مي خواهد به حالت خاموش برود (power save) چک کرده و جلوي اين حالات را مي گيريم . | ||
if message=WM_SYSCOMMAND then //Intercept System Commands
begin
case wParam of //Check System Calls
SC_SCREENSAVE,SC_MONITORPOWER: //Screensaver Trying To Start, Monitor Trying To Enter Powersave?
begin
result:=0; //Prevent This From Happening
exit; //Exit
end;
end;
end;
حالا بر اساس نوع پيغامها تصميم مي گيريم . | ||
case message of // Tells Windows We Want To Check The Message
ما حالت WM_ACTIVATE را چک مي کنيم تا ببينيم آيا پنجره ما همچنان فعال است يا نه . اگر پنجره ما کوچک شده بود (minimize)مقدار متغير active را "نادرست" کرده و در غير اينصورت "درست" مقدار دهي مي کنيم . | ||
WM_ACTIVATE:
begin
if (Hiword(wParam)=0) then //Check Minimization State
active:=true //Program Is Active
else
active:=false; //Program Is No Longer Active
Result:=0; //Return To The Message Loop
end;
در حالت WM_CLOSEپنجره ما بسته شده است . ما يک پيغام خروج مي فرستيم تا از حلقه اصلي خارج شويم . متغير done به "درست" مقدار دهي مي شود تا از حلقه اصلي در WinMain() خارج شويم و برنامه بسته شود . | ||
WM_CLOSE: //Did We Get A Close Message
Begin
PostQuitMessage(0); //Send A Quit Message
result:=0 //Return To The Message Loop
end;
اگر کليدي در حالت فشرده شده باشد ما مي توانيم آنرا با چک کردن wParam بدست آْوريم . سپس ما آنرا در آرايه Keys به حالت "درست" در مي آوريم . در اين روش ما بعداً مي توانيم اين آرايه را چک کنيم و دکمه هاي فشرده شده را بدست آوريم . اين روش همچنين به ما اين امکان را مي دهد که فشرده شدن همزمان چند کليد را بفهميم . | ||
WM_KEYDOWN: //Is A Key Being Held Down?
begin
keys[wParam] := TRUE; //If So, Mark It As True
result:=0; //Return To The Message Loop
end;
اگر کليدي رها شود ما آنرا با چک کردن wParam بدست مي آوريم . سپس وضعيت آنرا در آرايه Keys به نادرست تغيير مي دهيم . در اين روش ما مي توانيم بفهميم که آيا دکمه همچنان فشرده شده يا رها شده است . هر کليد بر روي صفحه کليد عددي بين صفر تا 255 بر مي گرداند . به عنوان مثال وقتي دکمه اي با کد 40 فشار داده مي شود ما keys[40] را "درست" مقدار دهي مي کنيم و وقتي رها مي شود دوباره به "نادرست" برمي گردانيم . اين روشي است که من براي ذخيره کليدها بکار مي برم . | ||
WM_KEYUP: //Is A Key Being Released?
begin
keys[wParam] := FALSE; //If So, Mark It As False
result:=0; //Return To The Message Loop
end;
وقتي پنجره اندازه اش تغيير مي کند message با مقدار WM_SIZE پر مي شود . ما مقادير LOWORD و HIWORDاز lParam را براي عرض و ارتفاع جديد مي خوانيم و صفحه OpenGL را با مقادير جديد مقدار دهي مي کنيم . | ||
WM_SIZe: //Resize The GL Window
begin
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); //Loword=Width, Highword=Height
result:=0; //Return To The Message Loop
end
else
ساير پيغامها را نيز به DefWindowProc مي فرستيم تا ويندوز کارهاي مربوط به آنها را انجام دهد . | ||
//Pass All Unhandled Messages To DefWinProc
begin
Result := DefWindowProc(hWnd, message, wParam, lParam);
end;
end;
end;
اين بخش مدخل برنامه ما است . اين جايي است که ما روتين ساختن پنجره را صدا مي زنيم و به عملکرد کاربر واکنش نشان مي دهيم . | ||
function WinMain(hInstance: HINST; //Instance
hPrevInstance: HINST; //Previous Instance
lpCmdLine: PChar; //Command Line Parameters
nCmdShow: integer): //Window Show State
integer; stdcall;
ما دو متغير تعريف مي کنيم . msg براي اين استفاده مي شود که ببينيم آيا پيغامي براي واکنش نشان دادن وجود دارد يا نه . متغير done را هم به "نادرست" تنظيم مي کنيم . اين بدان مهناست که تا زماني که همين مقدار را دارد برنامه ما به کار خود ادامه مي دهد . و وقتي که "درست" شد ، از برنامه خارح مي شويم . | ||
var
msg: TMsg; // Windows Message Structure
done: Bool; // Variable To Exit The Loop
begin
done:=false;
اين بخش از کد اختياري است . از کاربر مي پرسد که آيا مايل است برنامه در حالت تمام صفحه اجرا شود يا خير و اگر نه متغير fullscreen را به حالت "نادرست" مي برد تا برنامه در حالت پنجره اي به جاي تمام صفحه اجرا شود . | ||
//Ask The User Which Screenmode They Prefer
if MessageBox(0,'Would You Like To Run In FullScreen Mode?','Start FullScreen',
MB_YESNO or MB_ICONQUESTION)=IDNO then
FullScreen:=false //Windowed Mode
else
FullScreen:=true; //Fullscreen Mode
اينجا جايي است که ما دستور ساختن پنجره OpenGL خومان را مي دهيم . ما مقادير نوشته ي بالاي صفحه ، عرض ، ارتفاع ، عمق رنگ و مقدار تمام صفحه بودن يا نبودن را مي فرستيم . دقت کنيد که اين بخش از کد تا چه حدي زيبا و کارآمد است . اگر پنجره بدرستي ساخته نشود مقدار "نادرست" برگردانده شده و ما از برنامه خارح مي شويم . | ||
if not CreateGLWindow('NeHe''s OpenGL Framework',640,480,16,FullScreen) then //Could We Create The OpenGl Window?
begin
Result := 0; //Quit If The Window Wasn't Created
exit;
end;
اين جايي است که حلقه ما شروع مي شود . تا وقتي متغير done "نادرست" است حلقه اجرا مي شود و در غير اينصورت از حلقه خارج مي شويم . | ||
while not done do //Loop That Keeps The Program Running
begin
اولين چيزي که بايد چک کنيم اين است که آيا پيغامي جهت پردازش وجود دارد يا خير . با استفاده از PeekMessage به جاي GetMessageيک حالت پيشرفته تري به برنامه مان مي دهيم . اولي اين مزيت را دارد که اگر پيغامي در کار نباشد به کار ادامه مي دهد ولي GetMessage تا زماني که پيغامي نرسد اجراي برنامه ما را متوقف مي کند . ( اين پيغام لزوماً پيغامهايي که در بالا ديديد نيست . بلکه مي تواند پيغام رسم (paint) و يا هر پيغام ديگري نيز باشد ) | ||
if (PeekMessage(msg, 0, 0, 0, PM_REMOVE)) then //Is There A Message Running
begin
در اين بخش ما چک مي کنيم که آيا پيغامي براي بستن برنامه دريافت کرده ايم يا خير . اگر آري متغير done را "درست" مقدار دهي کرده تا از حلقه و سپس از برنامه خارج شويم . | ||
if msg.message=WM_QUIT then //Have We Received A Quit Message?
done:=true //If So, Done=True
else
begin
اگر نه ما پيغام را ترجمه کرده و سپس آنرا ارسال مي کنيم تا WndProc و يا ويندوز آنرا دريافت کنند . | ||
TranslateMessage(msg); //Translate The Message
DispatchMessage(msg); //Dispatch The Message
end;
end
else
begin
اگر پيغامي در کار نبود ما منظره مورد نطر را در OpenGL رسم مي کنيم . اولين خط چک مي کند که آيا برنامه فعال است يا نه ؟ اگر ESC زده شده باشد ما بايد از برنامه خارج شويم و احتياجي به رسم اشياء نيست . | ||
//Draw The GL Scene. Watch For ESC Key Aned Quit Messages from DrawGLScene()
if (active and not(DrawGLScene()) or keys[VK_ESCAPE]) then
done:=true //ESC PRessed Or DrawGLScene Signalle A Quit
else
اگر برنامه فعال بود و ESC هم زده نشده بود و همينطور توانسته بوديم اشياء را رسم کنيم بافرها را تعويض مي کنيم . ( با استفاده از دو بافر ما از پرش تصوير جلوگيري مي کنيم ) با استفاده از دو بافر ما مي توانيم عمليات ترسيم اشياء را در صفحه دوم ( و مخفي از نظر کاربر ) انجام دهيم و سپس وقتي جاي دو بافر را عوض مي کنيم تصوير به يکباره ظاهر مي شود و حالت رسم از ديد کاربر مخفي مي ماند . | ||
SwapBuffers(h_Dc); //Not Time TO Quit Yet, Update The Screen
end;
end;
اگر متغير done "نادرست" نباشد بايد از برنامه خارج شويم . يعني بايد پنجره OpenGL را ببنديم و دستور خروج را به برنامه بدهيم . | ||
killGLwindow(); //Shutdown
result:=msg.wParam;
end;
اين بخش هم که روتين اصلي برنامه ما است . | ||
begin
active:=true; //The Active Variable Is Set To True By Default
WinMain( hInstance, hPrevInst, CmdLine, CmdShow ); //This Starts the program
end.
من براي ترجمه و اضافه کردن بخشها(ي معدود) حدود 2 تا 3 روز کاري وقت گذاشتم .همينطور درست کردن صفحات سايت و تنظيم آنها حدود 2 روز طول کشيد .اگر جايي ايرادي مي بينيد و يا فکر مي کنيد با اضافه و يا کم کردن بخشهايي به خوانايي و يا افزايش عملکرد برنامه و يا سطح آموزش اضافه مي شود حتماً با من در ميان بگذاريد
|
| دريافت ليست دلفي نوشته شده توسط Peter De Jaegher
| ||
|
درس دوم -> |
| |