Представим, что нам надо нарисовать стол. В этом случае нам надо нарисовать четыре ножки, которые выглядят как кубы. Вроде ничего сложного, но куба среди примитивов OpenGL нет. Поэтому придёться каждый куб рисовать из шести квадратов. И так четыре раза. Не легче ли один раз создать ножку и использовать её в своей проге сколько угодно? Вот именно этому мы и научимся.
Для реализации задуманного я использовал пример из июльского номера. Посмотри на рисунок 1, чтобы освежить его в памяти. Я этот пример сейчас немного изнасилую.
Рис 1. Форма
На событие OnCreate добавим вот такую конструкцию:
Функция glNewList создаёт новый список (объект). Он работает как Begin, а концом для него служит glEndList. Всё, что находиться между этими двумя функциями - это вершины, или стороны создаваемого нового объекта. В моём случае - это простые два квадрата, которые пересекают друг друга (см рисунок 1).
Теперь разберёмся с параметрами функции glNewList. В качестве первого параметра выступает цифра, которая идентифицирует новый объект. Она изменяется от 1 до 255 и по этой цифре мы будем в дальнейшем обращаться к этому объекту. Второй параметр - константа, которая указывает, как будет создаваться объект. Она может быть только GL_COMPILE - создать объект или GL_COMPILE_AND_EXECUTE - создать объект и вывести на экран. Я в своём примере только создаю новый объект.
Теперь корректируем событие OnPaint
procedure TForm1.FormPaint(Sender: TObject);
var
ps:TPaintStruct;
begin
BeginPaint(Handle,ps);
glClearColor(1,0.5,0.5,1);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glRotated(45,1,0,0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glCallList(1);
glTranslatef(2,2,0);
glCallList(1);
glTranslatef(-4,-4,2);
glCallList(1);
glFlush();
swapBuffers(dc);
EndPaint(Handle,ps);
end;
Здесь я теперь вместо рисования пересекающихся квадратов, просто вызываю созданный объект с помощью glCallList . В качестве параметра выступает номер объекта (я создавал объект 1, значит и здесь должна быть 1). Я делаю это три раза в разных позициях, чтобы пример был более наглядным. Пример готов, можно запустить его и посмотреть на результат.
Теперь я хочу немного улучшить наш пример. В разделе private главной формы TForm я объявил несколько переменных:
private
X,Y,Z:real;
RX,RY,RZ:real;
В событии OnCreate я присваиваю всем этим переменным 0. После этого создадим новое событие OnKeyDown и напишем в нём:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key =VK_RIGHT then
X:=X+0.1;
if Key =VK_LEFT then
X:=X-0.1;
if Key =VK_UP then
Y:=Y+0.1;
if Key =VK_DOWN then
Y:=Y-0.1;
if Key =VK_INSERT then
RY:=RY-2;
if Key =VK_DELETE then
RY:=RY+2;
if Key =VK_HOME then
RX:=RX-2;
if Key =VK_END then
RX:=RX+2;
FormPaint(nil);
end;
И наконец событие OnPaint корректирую так:
procedure TForm1.FormPaint(Sender: TObject);
var
ps:TPaintStruct;
begin
BeginPaint(Handle,ps);
glClearColor(1,0.5,0.5,1);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glPushMatrix;
glRotated(45,1,0,0);
glTranslatef(X,Y,Z);
glRotated(RX,1,0,0);
glRotated(RY,0,1,0);
glRotated(RZ,0,0,1);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glCallList(1);
glTranslatef(2,2,0);
glCallList(1);
glTranslatef(-4,-4,2);
glCallList(1);
glPopMatrix;
glFlush();
swapBuffers(dc);
EndPaint(Handle,ps);
end;
Можно запустить программу и посмотреть на результат. Попробуй понажимать на клавиши стрелок, Insert, Home, Delete, End. Если результат удовлетворил, то давай перейдём к рассмотрению новых функций. С функцией glRotated мы уже знакомы, это поворот сцены. GlTranslatef - перемещает сцену по координатам указанным в скобках (X,Y,Z).
На функциях glPushMatrix и glPopMatrix я остановлюсь подольше. Они очень даже интересны и очень полезны. Их назначение - сохранить и восстановить текущую матрицу перемещений.
Найди пример из июльского номера и запусти. Теперь попробуй полностью перекрыть окно и снова показать его. Сцена изменилась. Это произошло из-за того, что снова произошла прорисовка экрана, а мы использовали функцию поворота glRotated. Когда программа запустилась, то вызвалась функция glRotated и сцена повернулась на 45 градусов. После того, как ты перекрыл окно и снова показал, сцена перерисовалась и повернулась ещё на 45 градусов (всего уже 90). Как избавится от этого эффекта? Легко. Заключить все повороты (glRotated), перемещения (glTranslatef) и масштабирование (clScale) и рисование соответствующих объектов между glPushMatrix и glPopMatrix. Первая их них сохраняет текущую матрицу поворотов, перемещений и масштабирование. После этого можно смело рисовать. После вызова glPopMatrix, матрица восстановиться в состояние, которое было до вызова glPushMatrix.
В нашем случае, я сохраняю нулевую матрицу (потому что я ещё не производил никаких действий до вызова glPushMatrix). Потом рисую объекты и в конце восстанавливаю нулевую матрицу. Если перед вызовом glPushMatrix поместить glRotate(45,1,0,0) (поворот на 45 градусов), то сохраниться матрица, в которой есть только этот поворот. После восстановления в матрице опять будет только этот поворот. Все действия между glPushMatrix и glPopMatrix будут отменены.
Вроде всё. Мы сегодня и так поработали слишком хорошо. Продолжим наши занятия в следующий раз, а пока играйся с новым примером. До встречи.