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