OpenGL в Delphi

       

Выбор элементов


Задача выбора определенных элементов на экране является очень распространенной для многих интерактивных приложений, поэтому достойна подробного рассмотрения. Решений ее несколько. Одно из самых простых состоит в том, что элементы раскрашиваются в уникальные цвета, и для выбора элемента просто определяется цвет пиксела в нужной точке.
Рассмотрим проект из подкаталога Ex01. Это плоскостная картинка, содержащая изображения двух треугольников: красного и синего. При нажатии кнопки мыши определяем цвет пиксела под курсором и выделяем его составляющие, по которым вычисляем выбранный элемент. Для красного треугольника код, например, может быть следующим:

ColorToGL (Canvas.Pixels [X,Y], R, G, B) ;
If (R о 0) and (B = 0) then
ShowMessage ('Выбран красный треугольник');

Здесь для определения составляющих цвета пиксела вызывается пользовательская процедура, знакомая по главе 1.

glRotatef (AngleXYZ [1], 1, 0, 0);
glRotatef (AngleXYZ [2], 0, 1, 0);
glRotatef (AngleXYZ [3], 0, 0, 1);
If flgSquare then glCallList (1); // рисуем площадку (плоскость узла)
If flgOc then OcXYZ; // рисуем оси
If flgLight then begin // рисуем источник света
glTranslatef (PLPosition^ [1], PLPosition^ [2], PLPosition^ [3]);
gluSphere (ObjSphere, 0.01, 5, 5);
glTranslatef (-PLPosition^ [1], -PLPosition^ [2], -PLPosition^ [3]);
end;
glScalef (CoeffX, CoeffY, CoeffZ);
glTranslatef (0.0, 0.0, SmallB);
glCallList (3); // пружина
glCallList (10); // дырки в плите под болты
glCallList (5); // плита
glRotatef (AngleX, 1.0, 0.0, 0.0);
glRotatef (AngleY, 0.0, 1.0, 0.0);
glTranslatef (0.0, 0.0, Smallh);
glCallList (4); // диск
glCallList (8); // первый болт
glCallList (9); // второй болт
glRotatef (AngleZ, 0.0, 0.0, 1.0);
glCallList (2); // шпильковерт со шпинделем
glCallList (6); // патрон
glCallList (7); //деталь
glPopMatrix;
// конец работы SwapBuffers(DC);
end;

Конечно, вызываемые подряд списки можно также объединить, и после этого код вообще уложится в десяток строк.
В примере для определения цвета пиксела под курсором используются средства Delphi - прямое обращение к цвету пиксела формы.
В проекте из подкаталога Ех02 делается все то же самое, но цвет пиксела определяется с помощью команды OpenGL:

var
wrk : Array [0..2] of GLUbyte; begin
glReadPixels (X, Y, 1, 1, GL_RGB, GLJJNSIGNED_BYTE, @wrk);
If (wrk [0] о 0) and (wrk [2] = 0) then
ShowMessage ('Выбран красный треугольник')
else
If (wrk [0] = 0) and (wrk [2] <> 0) then
ShowMessage ('Выбран синий треугольник')
else
ShowMessage ('Ничего не выбрано');
end;

Выбор объектов, основанный на определении цвета пиксела под курсором, немногим отличается от простого анализа координат точки выбора. Достоинства такого метода - простота и высокая скорость. Но есть и недостатки:

  • метод неприменим при включенном источнике света; в общем случае мы не можем предусмотреть возможные цветовые оттенки, только если объекты не разнятся в цветах кардинально;
  • можно выбрать лишь элемент, нарисованный последним и лежащий сверху.

Следующий метод выбора похож на предыдущий: элементы, раскрашенные в уникальные цвета, рисуются в заднем буфере кадра, без вызова команды SwapBuffers. Для выбора элементов анализируем цвет заднего буфера в позиции, в которой осуществляется выбор. В заднем буфере элементы рисуются без текстуры и при выключенном источнике света, чтобы не портить чистые цвета.
Обратимся для ясности к проекту из подкаталога Ех03, где рисуются два треугольника одинакового цвета, при нажатии кнопки сообщается, какой треугольник выбран, левый или правый.
В процедуре перерисовки окна после команды SwapBuffers код рисования треугольников повторяется, однако каждый треугольник раскрашивается в уникальные цвета. Цвет фона для простоты дальнейших манипуляций задается черным.
Обработка нажатия кнопки начинается так:

wglMakeCurrent(Canvas.Handle, hrc);
glReadPixels(X, ClientHeight - Y, I, I, GL_RGB, GL_UNSIGNED_BYTE, @Pixel);
If (Pixel [0] <> 0) and (Pixel [2] = 0)
then ShowMessage ('Выбран левый треугольник');

To есть считываем пиксел в текущем, заднем, буфере кадра в позиции курсора, для чего необходимо предварительно установить контекст воспроизведения. Массив, хранящий цвета пиксела - массив трех элементов типа GLbyte.
Для разнообразия в этом примере я не занимаю контекст воспроизведения один раз на все время работы приложения, а делаю это дважды: один раз при перерисовке окна для воспроизведения сцены и второй раз при нажатии кнопки мыши для того, чтобы воспользоваться командами OpenGL чтения пиксела. Каждый раз после работы контекст, конечно, освобождается.
Чтобы не сложилось впечатление, что такой метод применим только для плоскостных построений, предлагаю посмотреть пример из подкаталога Ех04, где рисуются сфера и конус из одного материала и осуществляется выбор между ними. Можете перенести вызов команды SwapBuffers в конец кода перерисовки окна, чтобы увидеть, как сцена выглядит в заднем буфере.



Содержание раздела