# مباحث متفرقه برنامه نویسی > طراحی و ساخت بازی‌های کامپیوتری >  ترسیم یک مثلث ساده با استفاده از OpenGL

## Vahid_Nasiri

ایجاد یونیت تشریفاتی  برای  برپایی  فرمت نقطه ای  

قبل از هر چیزی  برای  اینکه تکرار مکررات در تمام مثالها صورت نگیرد و همانطور که در مقاله ی  اول نیز ذکر گردید در برنامه نویسی  OpenGL تحت ویندوز اولین کاری  که باید صورت گیرد برپایی  فرمت نقطه ای  می  باشد پس بهتر است یک یونیت تحت همین نام ایجاد نماییم تا به دفعات از آن در سورس های  دیگر نیز استفاده گردد.


سورس کامل این یونیت تشریفاتی  به صورت زیر بوده و این قاعده در تمام برنامه های  ما تکرار و رعایت خواهد گردید و تحت عنوان SPF به برنامه ها الحاق می  گردد:



unit SPF; // setup pixel format

interface
uses	&#123; uses clause &#125;
   Windows ;
var
  hrc&#58; HGLRC;  // Permanent Rendering Context

procedure CleanUp&#40;Handle&#58; HDC&#41;; //Properly Kill The Window
procedure SetDCPixelFormat&#40;Handle&#58; HDC;ColorBits,DepthBufferBits&#58;integer&#41;;

implementation

procedure CleanUp&#40;Handle&#58; HDC&#41;; //Properly Kill The Window
begin
  if hrc&lt;> 0 then       //Is There A Rendering Context?
    begin
       //Are We Able To Release Dc and Rc contexts?
       if &#40;not wglMakeCurrent&#40;handle,0&#41;&#41; then
         MessageBox&#40;0,'Release of DC and RC failed.'
                    ,' Shutdown Error',MB_OK or MB_ICONERROR&#41;;
      //Are We Able To Delete The Rc?
       if &#40;not wglDeleteContext&#40;hRc&#41;&#41; then
        begin
          MessageBox&#40;0,'Release of Rendering Context failed.',
                       ' Shutdown Error',MB_OK or MB_ICONERROR&#41;;
          hRc&#58;=0;   //Set Rc To Null
        end;
    end;
end;

procedure SetDCPixelFormat&#40;Handle&#58; HDC;ColorBits,DepthBufferBits&#58;integer&#41;;
var
  pfd&#58; TPixelFormatDescriptor;
  nPixelFormat&#58; Integer;

begin
  FillChar&#40;pfd, SizeOf&#40;pfd&#41;, 0&#41;;

  with pfd do begin
    nSize     &#58;= sizeof&#40;pfd&#41;;             // Size of this structure
    nVersion  &#58;= 1;                       // Version number
    dwFlags   &#58;= PFD_SUPPORT_OPENGL Or PFD_DRAW_TO_WINDOW
                 Or  PFD_TYPE_RGBA;       // Flags
    iPixelType&#58;= PFD_TYPE_RGBA;           // RGBA pixel values
    cColorBits&#58;= ColorBits;               // 24-bit color
    cDepthBits&#58;= DepthBufferBits;         // 32-bit depth buffer
    iLayerType&#58;= PFD_MAIN_PLANE;          // Layer type
  end;

  nPixelFormat &#58;= ChoosePixelFormat&#40;Handle, @pfd&#41;;
  //Did We Find A Matching Pixelformat?
  if &#40;nPixelFormat=0&#41; then
    begin
    CleanUp&#40;handle&#41;;                          //Reset The Display
      MessageBox&#40;0,'Cant''t Find A Suitable PixelFormat.'
                      ,'Error',MB_OK or MB_ICONEXCLAMATION&#41;;
      Halt&#40;1&#41;; &#123; Halt right here! &#125;
    end;

  //Are We Able To Set The Pixelformat?
  if &#40;not SetPixelFormat&#40;Handle, nPixelFormat, @pfd&#41;&#41; then
    begin
      CleanUp&#40;handle&#41;;                          //Reset The Display
      MessageBox&#40;0,'Cant''t set PixelFormat.'
              ,'Error',MB_OK or MB_ICONEXCLAMATION&#41;;
      Halt&#40;1&#41;; &#123; Halt right here! &#125;
    end;

  hrc &#58;= wglCreateContext&#40;Handle&#41;;
  if &#40;hRc=0&#41; then
    begin
    CleanUp&#40;handle&#41;;                        //Reset The Display
      MessageBox&#40;0,'Cant''t create a GL rendering context.'
                         ,'Error',MB_OK or MB_ICONEXCLAMATION&#41;;
      Halt&#40;1&#41;; &#123; Halt right here! &#125;
    end;

  //Are We Able To Activate The Rendering Context?
  if &#40;not wglMakeCurrent&#40;Handle, hrc&#41;&#41; then
    begin
    CleanUp&#40;handle&#41;;                      //Reset The Display
    MessageBox&#40;0,'Cant''t activate the GL rendering context.'
                          ,'Error',MB_OK or MB_ICONEXCLAMATION&#41;;
      Halt&#40;1&#41;; &#123; Halt right here! &#125;
    end;

end;

end.





ترسیم یک مثلث ساده با استفاده از OpenGL

با توجه به اینکه احتمالا مطالب عنوان شده در قسمت اول کمی  پیچیده بودند در طی  چند مثال که به تدریج ارائه خواهد شد این مطالب بیشتر موشکافی  خواهند گردید.


در این مثال قصد داریم یک مثلث ساده را با استفاده از OpenGL ترسیم کنیم. در اینجا با نحوه ی  استفاده از توابع glViewport – glMatrixMode – glLoadIdentity – gluPerspective- glClear -  gluLookAt – glBegin  - glEnd  بیشتر آشنا خواهیم شد . 


یک پروژه جدید در دلفی  ایجاد نموده و یونیت OpenGL استاندارد دلفی  و یونیت SPF را که قبلا در طی  مقاله ای  راجع به آن برای  پرپایی  فرمت نقطه ای  صحبت گردید را به برنامه الحاق نمایید . 
یعنی  :


unit ex01;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs , OpenGL, SPF;



قاعده ی  دیگری  که در تمام برنامه های  OpenGL مشترک است ایجاد یک متغیر برای  دریافت HDC فرم می  باشد و پاس کردن آن به توابع موجود در یونیت SPF برای  برپایی  فرمت نقطه ای  با استفاده از آن.
یعنی  در ادامه خواهیم داشت :


type
  TForm1 = class&#40;TForm&#41;
    procedure FormCreate&#40;Sender&#58; TObject&#41;;
  private
    &#123; Private declarations &#125;
  public
    &#123; Public declarations &#125;
  end;

var
  Form1&#58; TForm1;

implementation

&#123;$R *.dfm&#125;

var
 f_Hdc &#58; LongInt;




حالا روی  فرم دوبار کلیک نمایید تا کار برپا سازی  فرمت نقطه ای  را در اینجا انجام دهیم . یعنی  :



procedure TForm1.FormCreate&#40;Sender&#58; TObject&#41;;
begin
  // Create a rendering context.
  f_Hdc &#58;= GetDC&#40;handle&#41;;
  SetDCPixelFormat&#40;f_Hdc,16,16&#41;;
  InitGL;
end;



از برگه ی  خواص رخدادهای  مربوط به فرم را انتخاب نمایید و در قسمت OnDestory مربوط به فرم دوبار کلیک کنید تا روال رخداد آن آماده شود . از آن برای  پاکسازی  برنامه استفاده خواهیم کرد (یک تعهد اخلاقی  ! ) . یعنی  :


procedure TForm1.FormDestroy&#40;Sender&#58; TObject&#41;;
begin
  CleanUp&#40;f_Hdc&#41;;// Clean up and terminate.
end;



استاندارد دیگری  که در تمام برنامه های  OpenGL  رعایت می  شود بدین صورت است که یک تابع InitGL تعریف می  گردد تا مقدمات کاربرنامه نویسی  مانند تعریف رنگها و نحوه ی  سایه زدن و امثال اینها که عموما در طی  برنامه تغییر نمی  کنند در آن گنجانده شود . این مورد در روال رخداد FormCreate همانطور که دقت کردید کاربرد دارد.


procedure InitGL;	// All Setup For OpenGL Goes Here
begin
// select clearing color
   glClearColor&#40; 0.0, 0.0, 0.0, 0&#41;;
   glMatrixMode&#40;GL_PROJECTION&#41;;
   glLoadIdentity&#40;&#41;;
end;



قاعده ی  دیگری  که بازهم بصورت استاندارد بکار گرفته می  شود برای  خوانایی  بیشتر کدهای  OpenGL ایجاد تابع DrawGLScene برای  نوشتن تمام دستورالعمل های  ترسیمات برنامه است. در اینجا ما می  خواهیم یک مثلث را ترسیم نماییم . بدین صورت :



procedure DrawGLScene &#40;&#41;;
// Here's Where We Do All The Drawing!!!
begin

// سبب پاک شدن صفحه و عمق بافر می  شود
	glClear&#40;GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT&#41;;	
// ماتریس های  مورد استفاده و در نتیجه صفحه را ریست می  کند
	glLoadIdentity&#40;&#41;;								
// بدین وسیله محل قرار گیری  دوربین را برای  نمایش مثلث مشخص می  کنیم
	gluLookAt&#40;0, 0, 6,     0, 0, 0,     0, 1, 0&#41;;	

	// The position has an X Y and Z.  Right now, we are standing at &#40;0, 0, 6&#41;
	// The view also has an X Y and Z.  We are looking at the center of the axis &#40;0, 0, 0&#41;
	// The up vector is 3D too, so it has an X Y and Z.  We say that up is &#40;0, 1, 0&#41;
	// Unless you are making a game like Descent&#40;TM&#41;, the up vector can stay the same.

//در اینجا ما به اپن جی  ال می  گوییم کی  می  خواهیم ترسیم یک مثلث را شروع کنیم	
	glBegin &#40;GL_TRIANGLES&#41;;					
// در ادامه سه راس مثلث را برای  ترسیم مشخص می  نماییم
	    glVertex3f&#40;0, 1, 0&#41;;
	     glVertex3f&#40;-1, 0, 0&#41;;
	     glVertex3f&#40;1, 0, 0&#41;;	
// و در اینجا خاتمه ترسیم را اعلام می  نماییم
	glEnd&#40;&#41;;										
	// You can have as many points inside the BEGIN and END, but it must be in three's.
	// Try GL_LINES or GL_QUADS.  Lines are done in 2's and Quads done in 4's.

	SwapBuffers&#40;f_Hdc&#41;;									// Swap the backbuffers to the foreground
// استفاده از تابع فوق الزامی  است. در غیر اینصورت چیزی  روی  صفحه ترسیم نخواهد شد
end;




برای  اینکه مطمئن شویم پنجره ی  برنامه ما در هر حالتی  دوباره ترسیم خواهد شد رخداد OnPaint را باید به برنامه اضافه کرد :


procedure TForm1.FormPaint&#40;Sender&#58; TObject&#41;;
begin
  wglMakeCurrent&#40;f_Hdc,hrc&#41;; //activate the RC
  DrawGLScene;// Draw the scene.
end;




با توجه به اینکه فرم برنامه می  تواند به هر اندازه ای  از طرف کاربر تغییر کنید به رخداد OnResize هم نیاز داریم :


procedure TForm1.FormResize&#40;Sender&#58; TObject&#41;;
begin
	if &#40;height=0&#41; then   
// در اینجا از تقسیم شدن ارتفاع صفحه بر صفر که در توابع بعدی  استفاده می  شوند جلوگیری  میشود
  	   height&#58;=1;									

//  در این حالت دریچه ی  دید را کل صفحه انتخاب می  نماییم
	glViewport&#40;0,0,width,height&#41;;	
	// The glViewport takes &#40;x, y, width, height&#41;
	// This basically means, what our our drawing boundries

	glMatrixMode&#40;GL_PROJECTION&#41;;  // Select The Projection Matrix
	glLoadIdentity&#40;&#41;; // Reset The Projection Matrix

												// Calculate The Aspect Ratio Of The Window
	// The parameters are&#58;
	// &#40;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&#41;
	gluPerspective&#40;45.0,width/height, 1 ,150.0&#41;;

	// * Note * - The farthest distance should be at least 1 if you don't want some
	// funny artifacts when dealing with lighting and distance polygons.  This is a special
	// thing that not many people know about.  If it's less than 1 it creates little flashes
	// on far away polygons when lighting is enabled.

	glMatrixMode&#40;GL_MODELVIEW&#41;;	 // Select The Modelview Matrix
	glLoadIdentity&#40;&#41;;  // Reset The Modelview Matrix

end;





در این برنامه با روال معمول ایجاد یک برنامه OpenGL ( در هر سطحی  ) و با رخدادهای  کلیدی  آن آشنا شدید. تمام اینگونه رخدادها در برنامه های  OpenGL تکرار می  شوند و مرور آنها لازم به نظر می  رسید.
باید اعتراف کنم که سرعت دلفی  واقعا به قول خارجی  ها Amazing است !! ( مخصوصا در مقایسه با این وی ژوال استودیوی  جدید مایکروسافت که آدم را روی  یک پنتیوم فور خفه می  کند!! )

فرض کنید که می  خواستیم دو مثلث را ترسیم کنیم . کد حاصل به صورت زیر در می  آمد :


//	glBegin &#40;GL_TRIANGLES&#41;;	
//		glVertex3f&#40;0, 1, 0&#41;;	// Top point			
//		glVertex3f&#40;-1, 0, 0&#41;;	// Bottom right point
//		glVertex3f&#40;1, 0, 0&#41;;    // Bottom left point
//
//		glVertex3f&#40;0, 1, 1&#41;;	// Top point			
//		glVertex3f&#40;-1, 0, 1&#41;;	// Bottom right point
//		glVertex3f&#40;1, 0, 1&#41;;    // Bottom left point
//	glEnd&#40;&#41;;

// دید سه بعدی  در اپن جی  ال به صورت زیر است
//
//				Y
//				|
//				|
//				|________ X
//				 \
//				   \
//					Z
بنابراین مثلث ما به صورت زیر ترسیم می  شود//
//				Y
//				|
//			   /|\
//			  /_|_\______ X
//				 \
//				   \
//					Z
// در مورد ماتریس ها در طی  مقالات آتی  بیشتر توضیح داده خواهد شد
// در هر حال برای  برنامه نویسی  اپن جی  ال نیازی  به دانش بردارها و ماتریسها در حد ابتدایی  است

----------

