OpenGL в Delphi

       

Буфер выбора


В режиме выбора OpenGL возвращает так называемые записи нажатия (hit records), которые содержат информацию о выбранном элементе. Для идентификации элементов они должны быть поименованы, именование. Элементов осуществляется С помощью Команд glLoadName или glPushName. Имя объекта в OpenGL - любое целое число, которое позволяет уникально идентифицировать каждый выбранный элемент. OpenGL хранит имена в стеке имен.
Для включения режима выбора необходимо вызвать команду glRenderMode с аргументом GL_SELECTION. Однако прежде чем сделать это, требуется определить буфер вывода, куда будут помещаться записи нажатия. При нахождении в режиме выбора содержимое заднего буфера кадра закрыто и не может быть изменено.
Библиотека OpenGL будет возвращать запись нажатия для каждого объекта, находящегося в отображаемом объеме. Для выбора только среди объектов, находящихся под курсором, необходимо изменить отображаемый объем. Библиотека glu содержит команду, позволяющую это сделать - gluPickMatrix, которая создает небольшой отображаемый объем около координат курсора, передаваемых в команду в качестве параметров. После задания области вывода можно только выбирать объекты. Напоминаю, что перед рисованием. Объектов вызываются команды glLoadName ИЛИ glPushName.
После осуществления выбора необходимо выйти из режима выбора вызовом команды glRenderMode с аргументом GL_RENDER. С этого момента команда будет возвращать число записей нажатия, и буфер выбора может быть проанализирован. Буфер выбора представляет собой массив, где каждая запись нажатия содержит следующие элементы:
  • число имен в стеке имен на момент нажатия;
  • минимум и максимум оконной координаты Z примитивов в отображаемом объеме на последнее нажатие; эти значения лежат в пределах от нуля до единицы, но перед выводом в буфер выбора умножаются на 232-1;
  • фактическое содержание буфера имен на момент нажатия, начиная с верхнего.

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

  • они входят в стандартную часть OpenGL и не требуют дополнительного кодирования;
  • буфер выбора предоставляет гибкий путь для выбора группированных или иерархических элементов

Однако есть и недостатки:



  • необходимый размер буфера выбора должен быть известен до входа в режим выбора для определения размера требуемой памяти;
  • глубина стека имен ограничена, поэтому очень сложные иерархические модели будут определяться как nil, т. e. не определяться вообще.

Обратимся к простому примеру из подкаталога Ex05, чтобы уяснить использование техники выбора в OpenGL. Ha экране рисуются два треугольника - синий и красный. При нажатии кнопки мыши на поверхности окна выдается сообщение, на каком треугольнике сделан выбор, либо выводится фраза "Пустое место", если под курсором нет ни одного треугольника.
Само построение треугольников вынесено в процедуру, начало описания которой выглядит так:

procedure Render (mode : GLenum); // параметр - режим (выбора/рисования)
begin
// красный треугольник
If mode = GL_SELECT then glLoadName (1); // называем именем 1
glColor3f (1.0, 0.0, 0.0);

Процедурой Render будем пользоваться для собственно построения изображения и для выбора объекта, режим передается в качестве параметра В случае, если действует режим выбора, перед рисованием объекта загружаем его
имя командой glLoadName, аргумент которой - целое число, задаваемое разработчиком. В примере для первого, красного треугольника, загружаем единицу, для второго треугольника загружаем двойку.
При нажатии кнопки мыши координаты курсора передаются в функцию DoSelect. Это важная часть программы, поэтому приведем описание функции целиком.

function DoSelect(x : GLint; у : GLint) : GLint;
var hits : GLint;
Begin
glRenderMode(GL_SELECT); // включаем режим выбора
// режим выбора нужен для работы следующих команд
glInitNames; // инициализация стека имен
glPushName(0); // помещение имени в стек имен
glLoadIdentity;
gluPickMatrix(x, windH - у, 2, 2, @vp);
Render(GL_SELECT); // рисуем объекты с именованием объектов
hits := glRenderMode(GL_SELECT);
if hits <= 0
then Result := -1
else Result := SelectBuf [(hits - 1) * 4 + 3];
end;

Функция начинается с включения режима выбора. Команда glInitNames очищает стек имен, команда glPushName помещает аргумент в стек имен. Значение вершины стека заменяется потом на аргумент команды glLoadName.
Команда gluPickMatrix задает область выбора. Первые два аргумента - центр области выбора, в них передаем координаты курсора. Следующие два аргумента задают размер области в пикселах, здесь задаем размер области 2x2. Последний аргумент - указатель на массив, хранящий текущую матрицу. Ее запоминаем в обработчике события WM_SIZE при каждом изменении размеров окна:

glGetIntegerv(GL_VIEWPORT, @vp);

После этих приготовлений воспроизводим картинку параллельно с загрузкой имен в стек.
Последние строки - собственно выбор: снова задаем режим выбора и анализируем возвращаемый результат. Здесь SelectBuf - массив буфера выборa, подготовленный в начале работы приложения:

glSelectBuffer{MaxSelect, @SelectBuf); // создание буфера выбора

Первый аргумент - размер буфера выбора, в примере задан равным 4 - ровно под один объект, поскольку в проекте выбирается и перекрашивается один, самый верхний (ближайший к наблюдателю) объект.
Более элегантный код для этого действия записывается так:

glSelectBuffer(SizeOf(SelectBuf), @SelectBuf);

Выражение для извлечения имени элемента ((hits - 1> * 4 + 3) в случае, если нам нужно получить имя только последнего, верхнего объекта, можно заменить на просто 3, тем более что в данном примере больше одного элемента в буфер не помещается. Как следует из документации и из вводной части этого раздела, номер выбранного элемента располагается четвертым в буфере выбора, поэтому, если требуется извлечь имя только одного, самого верхнего объекта, можно использовать явное выражение для получения номера выбранного элемента:

Result := SelectBuf [3];

Замечание
В этом примере для определения имени выбранного элемента команда glRenderMode вызывается с аргументом GL_SELECT Согласно документации, в этом случае команда возвращает количество записей нажатия, помещенных в буфер выбора, а при вызове с аргументом GL_RENDER эта команда возвращает всегда ноль. Однако в примере на выбор элемента из пакета SDK при выборе элемента эта команда вызывается именно с аргументом GL_RENDER В нашем примере не будет разницы в результате, если аргументом glRenderMode брать любую из этих двух констант, однако некоторые последующие проекты будут корректно работать только при значении аргумента GL_RENDER Это следует понимать как возвращение обычного режима воспроизведения - воспроизведения в буфер кадра.

Рассмотрим пример из подкаталога Ех06. На экране рисуется двадцать треугольников со случайными координатами и цветом, при нажатии кнопки мыши на каком-либо из них треугольник перекрашивается (Рисунок 6.1).



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