Основы программирования 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
Ну вот пока и всё.
30 ноября 2003 (Обновление: 4 фев 2011)