OpenGL и Delphi на практике Издательский Дом "КОМИЗДАТ" Любая теория хороша, если она может быть реализована на Delphi :-). Поэтому предлагаю не откладывая в долгий ящик написать первую программу на OpenGL - а потом, окрылившись успехом, вернуться к теории и как следует проштудировать все книги и сайты по сабжу, чтобы уж стать настоящими монстрами трехмерного моделирования. Для начала придется проделать подготовительную работу: настроить формат пикселей с учетом отображаемой информации; создать контекст OpenGL и подготовить сам движок OpenGL к работе. Формат пикселей удобно вынести в отдельную процедуру, которую мы оформим следующим образом: procedure SetDCPixelFormat (dc: HDC); var pfd: TPixelFormatDescriptor; nPixelFormat: Integer; begin FillChar (pfd, SizeOf (pfd),0); with pfd do begin nSize:= sizeof (pfd); nVersion:= 1; dwFlags:= PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER; iPixelType:= PFD_TYPE_RGBA; cColorBits:= 16; cDepthBits:= 64; iLayerType:= PFD_MAIN_PLANE; end; nPixelFormat:=ChoosePixelFormat (DC,@pfd); SetPixelFormat (DC, nPixelFormat,@pfd); end; Здесь при заполнении структуры TPixelFormatDescriptor мы задаем параметры будущего графического отображения, в том числе количество цветовых бит, а также тип пикселей (iPixelType). Мы также задаем флаги, которые, как видно из названия, указывают, что наша программа будет поддерживать OpenGL, а также что мы будем рисовать в окне и использовать двойную буферизацию (параметр, необходимый для воспроизведения движущихся объектов). Далее посредством вызова ChoosePixelFormat система выбирает подходящий формат пикселя - и мы присваиваем его (через SetPixelFormat) нашему окну. Теперь нужно инициализировать контекст самого OpenGL посредством функций, содержащихся в модуле Windows, и произвести дополнительную настройку движка: procedure TForm1.FormCreate (Sender: TObject); begin H:=Handle; DC:=GetDC (H); SetDCPixelFormat (DC); RC:=wglCreateContext (DC); wglMakeCurrent (DC, RC); glClearColor (0.6,0.6,0.6,1.0); glMatrixMode (GL_PROJECTION); glLoadIdentity; glFrustum (-1,1,-1,1,2,20); glMatrixMode (GL_MODELVIEW); glLoadIdentity; glTranslatef (0.0,-1.0,-6.0); BeginPaint; end; Как видим, сначала мы задали для нашей графики необходимый формат пикселей. Теперь при помощи функции wglCreateContext создаем OpenGL-контекст, а впоследствии делаем его текущим контекстом. Далее, используя уже универсальные функции**, произведем настройку "мира", который будем создавать. Для этого через glClearColor очистим контекст и заполним ее 60-процентным черным цветом. Далее выберем матрицу проекций, которая определяет, как будут проецироваться трехмерные объекты на плоскость экрана (в оконные координаты) и через glLoadIdentity установим единичную матрицу и зададим границы плана в "мировых координатах" при помощи вызова glFrustum. После чего загрузим модельно видовую матрицу и произведем ее смещение (glTranslatef). Что будем рисовать Конечно, можно было нарисовать простую пирамиду или же куб. Но мы сделаем большее - нарисуем "признание в любви"** (рис. 1). Специально для этого методом "научного перебора" была разработана модель, описывающая соответствующую кривую: Остается только перевести ее с языка математики на нормальный человеческий. Прорисовка сцены Подготовку сцены начнем с подключения разных дополнительных функций, без которых дальнейшая работа невозможна. Эти функции прописаны в методе BeginPaint, а также в методе FormResize (чтобы при изменении размера формы соответственно менялся размер объекта). Для этого используем функцию glEnable с соответствующими параметрами. Далее в FormPaint используем подготовленные заранее методы DrawFace и DrawElement (см. листинг ниже) для отрисовки упомянутого объекта. А для придания ему еще большей "жары" используем возможности OpenGL по освещению сцены. Итог С точки зрения сложности освоения OpenGL сопоставим с другими подобными библиотеками. Так что с одной стороны нет разницы, в чем разбираться и что изучать. Но с точки зрения разумного подхода любой проект трехмерной графики должен как минимум поддерживать OpenGL в качестве одной из опций. Ведь серьезные вещи считаются и визуализируются, как правило, под Unix/IRIX/Linux/FreeBSD, и в то же время было бы неправильно игнорировать пользователей Windows. Так что OpenGL как раз и является тем универсальным языком и общим знаменателем, позволяющим вашим приложениям свободно мигрировать с одной платформы на другую. Листинг программы Листинг ======== unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs,OpenGL, StdCtrls, ExtCtrls; type TForm1 = class(TForm) Timer1: TTimer; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormPaint(Sender: TObject); procedure FormResize(Sender: TObject); private RC:HGLRC; DC:HDC; H:THandle; procedure BeginPaint; { Private declarations } public { Public declarations } end; var Form1: TForm1; const mat1_dif:Array[0..2] of Single = (0.8,0.8,0.0); const mat1_amb:Array[0..2] of Single = (0.2,0.2,0.2); const mat1_spec:Array[0..2] of Single = (0.6,0.6,0.6); const mat1_shininess = 0.5*128; procedure DrawElement(A,b,R0,r1:Single); procedure DrawFace(A,R:Single;Normal:Boolean); implementation procedure SetDCPixelFormat(dc:HDC); var pfd:TPixelFormatDescriptor; nPixelFormat:Integer; begin FillChar(pfd,SizeOf(pfd),0); with pfd do begin nSize := sizeof(pfd); nVersion := 1; dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER; iPixelType:= PFD_TYPE_RGBA; cColorBits:= 16; cDepthBits:= 64; iLayerType:= PFD_MAIN_PLANE; end; nPixelFormat:=ChoosePixelFormat(DC,@pfd); SetPixelFormat(DC,nPixelFormat,@pfd); end; procedure TForm1.BeginPaint; begin glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); timer1.enabled:=true; end; {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin H:=Handle; DC:=GetDC(H); SetDCPixelFormat(DC); RC:=wglCreateContext(DC); wglMakeCurrent(DC,RC); glClearColor(0.6,0.6,0.6,1.0); glMatrixMode(GL_PROJECTION); glLoadIdentity; glFrustum(-1,1,-1,1,2,20); glMatrixMode(GL_MODELVIEW); glLoadIdentity; glTranslatef(0.0,-1.0,-6.0); BeginPaint; end; procedure TForm1.FormDestroy(Sender: TObject); begin wglMakeCurrent(0,0); wglDeleteContext(RC); ReleaseDC(H,DC); DeleteDC(DC); end; procedure TForm1.Timer1Timer(Sender: TObject); begin glRotatef(4.0,0.0,1.0,0.0); SwapBuffers(DC); InvalidateRect(H,nil,False); end; procedure DrawElement(a,b,r0,r1:Single); var x1b,y1b:Single; x1e,y1e:Single; x0b,y0b:Single; x0e,y0e:Single; t0,t1:Single; dt:single; begin t0:=-3;t1:=3; dt:=0.06; while t0<=t1 do begin x0b:=a*sin(t0)*sin(t0)*sin(t0)*sin(t0)*cos(t0); y0b:=a*abs(sin(t0)*cos(t0)); x0e:=a*sin(t0+dt)*sin(t0+dt)*sin(t0+dt)*sin(t0+dt)*cos(t0+dt); y0e:=a*abs(sin(t0+dt)*cos(t0+dt)); x1b:=b*sin(t0)*sin(t0)*sin(t0)*sin(t0)*cos(t0); y1b:=b*abs(sin(t0)*cos(t0)); x1e:=b*sin(t0+dt)*sin(t0+dt)*sin(t0+dt)*sin(t0+dt)*cos(t0+dt); y1e:=b*abs(sin(t0+dt)*cos(t0+dt)); glBegin(GL_TRIANGLE_STRIP); glNormal((x0b+x1e)/2,(y0b+y1e)/2,(r1+r0)/2); glVertex3f(x0b,y0b,r0); glVertex3f(x0e,y0e,r0); glVertex3f(x1e,y1e,r1); glVertex3f(x1b,y1b,r1); glEnd; t0:=t0+dt; end; end; procedure DrawFace(A,R:Single;Normal:Boolean); var x,y:single; t0,t1,dt:Single; begin t0:=-3;t1:=3; dt:=0.06; glBegin(GL_POLYGON); while t0<=t1 do begin x:=a*sin(t0)*sin(t0)*sin(t0)*sin(t0)*cos(t0); y:=a*abs(sin(t0)*cos(t0)); glVertex3F(x,y,r); t0:=t0+dt; end; t0:=0; x:=a*sin(t0)*sin(t0)*sin(t0)*sin(t0)*cos(t0); y:=a*abs(sin(t0)*cos(t0)); if Normal then glNormal3f(x,y,-r) else glNormal3f(x,y,r); glEnd; end; procedure TForm1.FormPaint(Sender: TObject); var m,n:single;dm:Single;a:Single;df:Single; begin a:=25; df:=10; glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); glColor(1.0,0.0,0.0,0.0); glMaterialfv(GL_FRONT,GL_AMBIENT,@mat1_amb); glMaterialfv(GL_FRONT,GL_DIFFUSE,@mat1_dif); glMaterialfv(GL_FRONT,GL_SPECULAR,@mat1_spec); glMaterialf(GL_FRONT,GL_SHININESS,mat1_shininess); m:=-1;n:=1;dm:=0.5; while m<=n do begin DrawElement(Sqrt(a-m*m),Sqrt(a-(m+dm)*(m+dm)),m/df,(m+dm)/df); m:=m+dm; end; DrawFace(Sqrt(a-(m)*(m)),(m)/df,True); m:=-1; DrawFace(Sqrt(a-(m)*(m)),(m)/df,True); end; procedure TForm1.FormResize(Sender: TObject); const lm:Array[0..3] of Single = (0.5,0.5,0.5,1.0); const light_ambient:array[0..3] of glfloat = (0.0,0.0,0.0,1.0); light_diffuse:array[0..3] of glfloat = (1.0,1.0,1.0,1.0); light_specular:array[0..3] of glfloat = (2.0,2.0,2.0,1.0); light_position:array[0..3] of glfloat = (2.0,1.0,3.0,1.0); light_emission:array[0..3] of glfloat = (1.0,1.0,1.0,1.0); light_spotdirection:array[0..3] of glfloat = (1.0,1.0,1.0,1.0); begin wglMakeCurrent(0,0); wglDeleteContext(RC); ReleaseDC(H,DC); DC:=GetDC(H); SetDCPixelFormat(DC); RC:=wglCreateContext(DC); wglMakeCurrent(DC,RC); glClearColor(0.6,0.6,0.6,0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity; glFrustum(-1,1,-1,1,2,20); glMatrixMode(GL_MODELVIEW); glLoadIdentity; glTranslatef(0.0,-1.0,-6.0); glLightModel(GL_LIGHT_MODEL_LOCAL_VIEWER,Ord(True)); glLightModelfv(GL_LIGHT_MODEL_AMBIENT,@lm); glLightfv(GL_LIGHT0,GL_AMBIENT,@light_ambient); glLightfv(GL_LIGHT0,GL_DIFFUSE,@light_diffuse); glLightfv(GL_LIGHT0,GL_SPECULAR,@light_specular); glLightfv(GL_LIGHT0,GL_POSITION,@light_position); glLightf(GL_LIGHT0,GL_SPOT_EXPONENT,8); glLightf(GL_LIGHT0,GL_SPOT_CUTOFF,170); glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,@light_spotdirection); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); end; end. Список литературы Для подготовки данной работы были использованы материалы с сайта http://www.citforum.ru/



Яндекс.Метрика