PDA

View Full Version : ترسیم یک مثلث ساده با استفاده از OpenGL



Vahid_Nasiri
پنج شنبه 12 تیر 1382, 00:55 صبح
ایجاد یونیت تشریفاتی برای برپایی فرمت نقطه ای

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


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




unit SPF; // setup pixel format

interface
uses { uses clause }
Windows ;
var
hrc: HGLRC; // Permanent Rendering Context

procedure CleanUp(Handle: HDC); //Properly Kill The Window
procedure SetDCPixelFormat(Handle: HDC;ColorBits,DepthBufferBits:integer);

implementation

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

procedure SetDCPixelFormat(Handle: HDC;ColorBits,DepthBufferBits:integer);
var
pfd: TPixelFormatDescriptor;
nPixelFormat: Integer;

begin
FillChar(pfd, SizeOf(pfd), 0);

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

nPixelFormat := ChoosePixelFormat(Handle, @pfd);
//Did We Find A Matching Pixelformat?
if (nPixelFormat=0) then
begin
CleanUp(handle); //Reset The Display
MessageBox(0,'Cant''t Find A Suitable PixelFormat.'
,'Error',MB_OK or MB_ICONEXCLAMATION);
Halt(1); { Halt right here! }
end;

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

hrc := wglCreateContext(Handle);
if (hRc=0) then
begin
CleanUp(handle); //Reset The Display
MessageBox(0,'Cant''t create a GL rendering context.'
,'Error',MB_OK or MB_ICONEXCLAMATION);
Halt(1); { Halt right here! }
end;

//Are We Able To Activate The Rendering Context?
if (not wglMakeCurrent(Handle, hrc)) then
begin
CleanUp(handle); //Reset The Display
MessageBox(0,'Cant''t activate the GL rendering context.'
,'Error',MB_OK or MB_ICONEXCLAMATION);
Halt(1); { Halt right here! }
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(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

var
f_Hdc : LongInt;




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




procedure TForm1.FormCreate(Sender: TObject);
begin
// Create a rendering context.
f_Hdc := GetDC(handle);
SetDCPixelFormat(f_Hdc,16,16);
InitGL;
end;



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



procedure TForm1.FormDestroy(Sender: TObject);
begin
CleanUp(f_Hdc);// Clean up and terminate.
end;



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



procedure InitGL; // All Setup For OpenGL Goes Here
begin
// select clearing color
glClearColor( 0.0, 0.0, 0.0, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
end;



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




procedure DrawGLScene ();
// Here's Where We Do All The Drawing!!!
begin

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

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

//در اینجا ما به اپن جی ال می گوییم کی می خواهیم ترسیم یک مثلث را شروع کنیم
glBegin (GL_TRIANGLES);
// در ادامه سه راس مثلث را برای ترسیم مشخص می نماییم
glVertex3f(0, 1, 0);
glVertex3f(-1, 0, 0);
glVertex3f(1, 0, 0);
// و در اینجا خاتمه ترسیم را اعلام می نماییم
glEnd();
// 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(f_Hdc); // Swap the backbuffers to the foreground
// استفاده از تابع فوق الزامی است. در غیر اینصورت چیزی روی صفحه ترسیم نخواهد شد
end;




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



procedure TForm1.FormPaint(Sender: TObject);
begin
wglMakeCurrent(f_Hdc,hrc); //activate the RC
DrawGLScene;// Draw the scene.
end;




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



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

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

glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix

// Calculate The Aspect Ratio Of The Window
// The parameters are:
// (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(45.0,width/height, 1 ,150.0);

// * 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(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix

end;





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

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



// glBegin (GL_TRIANGLES);
// glVertex3f(0, 1, 0); // Top point
// glVertex3f(-1, 0, 0); // Bottom right point
// glVertex3f(1, 0, 0); // Bottom left point
//
// glVertex3f(0, 1, 1); // Top point
// glVertex3f(-1, 0, 1); // Bottom right point
// glVertex3f(1, 0, 1); // Bottom left point
// glEnd();

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