Войти
ПрограммированиеСтатьиГрафика

Основы программирования OpenGL в Borland С++Builder и Delphi. (Часть 3)

Автор:

Полезные мелочи

Списки

В прошлой статье для рисования параллелепипеда мы использовали процедуру. В OpenGL есть свой метод хранения последовательности рисования примитивов – через специальные списки.

Для создания списка нужно вызвать процедуру glNewList(list, mode). Параметр list – беззнаковое целое – число, которое используется для идентификации списка (я обычно пользуюсь глобальной константой, которая своим именем подсказывает, что рисует этот список). А mode – это режим «сборки», который может принимать следующие значения:
· GL_COMPILE – команды просто запоминаются без выполнения
· GL_COMPILE_AND_EXECUTE  – команды сначала выполняются, а потом заносятся в список

Все команды, расположенные после glNewList заносятся в список, до тех пор, пока не встретится glEndList().

Для вызова списка используется команда glCallList(list), где list – беззнаковое целое – имя списка.
Если у вас существует несколько списков, то их можно вызвать одновременно. Для этого используется команда glCallLists(n, type, lists). Здесь n – количество списков, lists – указатель на массив смещений, а type – тип значений в списке.

Перед вызовом glCallLists можно вызвать процедуру glListBase(base), которая добавляет постоянное целочисленное смещение base к каждому имени списка из массива lists. Если в результате получится имя несуществующего списка, то ничего не выполняется.

Разрешены вложенные вызовы glCallList и glCallLists из других списков, но уровень вложенности не превышает 64 (конкретное значение зависит от реализации).

Приведу простенький примерчик. В FormCreate или в FormActivate (или ещё где-нибудь) напишем следующее

  glNewList(1,GL_COMPILE);
    glColor3f(0, 1, 1);
    glBegin(GL_TRIANGLES);
      glVertex3i(-4,0, 1);
      glVertex3i(1, 3, 3);
      glVertex3i(3,-3, 2);
    glEnd();
  glEndList();
 
  glNewList(2,GL_COMPILE);
    glColor3f(0, 1, 0);
    glBegin(GL_TRIANGLES);
      glVertex3i(4,0, 1);
      glVertex3i(-1, 3, 3);
      glVertex3i(-3,-3, 2);
    glEnd();
  glEndList();

Теперь эти списки нужно вызвать. Сделать это, как вы сами понимаете, можно двумя способами: при помощи glCallList или glCallLists.

  glCallList(1);
  glCallList(2);

Или так.

void TForm1::Draw()
{
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
 
  Byte p[] = {1,2};
  glCallLists(2, GL_BYTE, p);
 
  SwapBuffers(ghDC);
}

procedure TForm1.Draw;
var
  p: array[0..1] of GLbyte;
begin
  glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT);
 
  p[0]:=1;
  p[1]:=2;
  glCallLists(2, GL_BYTE, @p);
 
  SwapBuffers(ghDC);
end;

Результат в обоих случаях будет один и тот же:

Изображение

Прозрачность через Alpha Test.

В OpenGL цвет можно задавать двумя способами: явным указанием значений RGBA или указанием индекса цвета в палитре. Так вот, с прозрачностью можно работать только в режиме RGBA, т.е. указав конкретные значения RGB и прозрачности (альфа). При альфа равном 1 фрагмент считается полностью непрозрачным, а при 0 – полностью прозрачным.

Тест на прозрачность разрешается и запрещается как и всё остальное командами glEnable и glDisable с параметром GL_ALPHA_TEST. Если тест не разрешён, считается, что он прошёл успешно.

Процедура glAlphaFunc(func, ref) указывает библиотеке, как проверять, проходит фрагмент альфа тест или нет. Параметр ref – значение из диапазона [0, 1] , с которым сравнивается поступающее значение альфа, а параметр func задаёт функцию сравнения значений альфа:
· GL_NEVER - тест всегда завершается неудачно
· GL_EQUAL - тест завершается положительно, если поступающее значение равно ref
· GL_NOTEQUAL - тест завершается положительно, если поступающее значение не равно ref
· GL_LESS - тест завершается положительно, если поступающее значение меньше чем ref
· GL_LEQUAL - тест завершается положительно, если поступающее значение меньше или равно ref
· GL_GREATER - тест завершается положительно, если поступающее значение больше чем ref
· GL_GEQUAL - тест завершается положительно, если поступающее значение больше или равно ref
· GL_ALWAYS - тест всегда завершается положительно

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

Смешивание (blending).

Смешивание – это комбинирование поступающих значений цвета в формате RGBA из текстурного комбайнера и значений цвета, хранящихся в буфере кадра. Каким образом OpenGL будет комбинировать цвета, задаёт процедура glBlendFunc(sfactor, dfactor). Параметр sfactor задаёт влияние источника (source), т.е. поступающих значений из комбайнера на результирующий цвет. А параметр dfactor задаёт влияние значений цвета, хранящихся в буфере кадра (destination).

Все возможные методы смешения цветов описаны в таблице ниже, где Rs, Gs, Bs, As – цвет источника, Rd, Gd, Bd, Ad – цвет приёмника. (Обзозначение для значения цветов примем в диапазоне от 0 до 1).

ЗначениеНовые значения RGBA
GL_ZERO(0,0,0,0)
GL_ONE(1,1,1,1)
GL_SRC_COLOR(Rs, Gs, Bs, As) - т.е. цвет источника
GL_ONE_MINUS_SRC_COLOR(1,1,1,1) - (Rs, Gs, Bs, As)
GL_DST_COLOR(Rd, Gd, Bd, Ad) - т.е. цвет приёмника
GL_ONE_MINUS_DST_COLOR(1,1,1,1) - (Rd, Gd, Bd, Ad)
GL_SRC_ALPHA(As, As, As, As)
GL_ONE_MINUS_SRC_ALPHA(1,1,1,1) - (As, As, As, As)
GL_DST_ALPHA(Ad, Ad, Ad, Ad)
GL_ONE_MINUS_DST_ALPHA(1,1,1,1) - (Ad, Ad, Ad, Ad)
GL_SRC_ALPHA_SATURATE(i, i, i, 1), где i = min(As, 1 - Ad)

Изображение
Не самый красивый рисунок, но суть отражает.

Прямоугольник отсечения

В OpenGL есть возможность, позволяющая выводить изображение не на всё окно, а только внутри указанного прямоугольника. Такой прямоугольник называется прямоугольником отсечения. Задаётся он процедурой glScissor(x, y, width, height), где x и y – координаты левого нижнего угла, а width и height – ширина и высота. По умолчанию прямоугольник отсечения совпадает с рабочей областью окна. Остаётся только сказать, что началом координат считается левый нижний угол окна.

Пример

В примере к прозрачности в функции Draw разрешите GL_SCISSOR_TEST и удалите команду SwapBuffers(ghDC). А содержимое процедуры OnTimer измените на следующее:

glScissor(70,90, 130,100);
Draw();
glScissor(220,140, 100,100);
Draw();
SwapBuffers(ghDC);

Вот, что получится:

Изображение

Плоскость отсечения.

OpenGL предоставляет вам возможность «отрезать» часть объекта плоскостью, задаваемой уравнением A*x + B*y + C*z + D = 0. Для тех, кто не знаком с геометрией, поясню: вектор n={A,B,C} задаёт нормаль к плоскости, а D – это расстояние от начала координат до плоскости. В OpenGL определено 6 плоскостей отсечения, каждая из которых задаётся процедурой glClipPlane(plane, equation). Параметр plane указывает номер плоскости отсечения и может принимать значения GL_CLIP_PLANE0 … GL_CLIP_PLANE5. Второй параметр – это указатель на массив из четырёх элементов – коэффициентов уравнения плоскости (A, B, C, D). Осталось только разрешить использовать плоскость отсечения процедурой glEnable(GL_CLIP_PLANE0).

Изображение

Исходник к статье: 20031130.zip

Ну вот пока и всё.

#C++, #Delphi, #OpenGL

30 ноября 2003 (Обновление: 4 фев. 2011)

Комментарии [10]