
C++Builder. Учебный курс
.pdfПример 11.7
g lB e g in (GL_POLYGON);
f o r ( in t i = 0 ; i<=6; i++)
{
glColor3f(Random(), Random() , Random()); g l V e r t e x 2 f ( 0 . 7*cos(2*M_PI*i/6) ,
0 . 7*sin(2*M_PI*i/6) ) ;
}
g lE n d ();
Обратите внимание, что в отличие от предыдущих реализаций этой задачи вершины шестиугольника последовательно соединя ются не с центром окна, а с крайней правой вершиной, указанной самой первой. Это становится хорошо заметным, если менять цвет для каждой вершины, как это и сделано в примере. Важно запом нить: базовые команды OpenGL предназначены для построения только выпуклых фигур, поэтому сложные фигуры чаще всего ри суются этапами, по частям.
Замечание. Для воспроизведения треугольников и четырех угольников лучше не использовать примитив GL_P0LYG0N, в та ких случаях оптимальным будет использование примитивов, спе циально предназначенных для этих фигур.
11.3.Преобразования
11.3.1.Масштабирование
Мы уже знаем, что границы области вывода лежат в пределах от - 1 до 1. Это может привести к неудобству при подготовке изо бражений. К счастью, OpenGL предоставляет удобное средство на этот случай - масштабирование. Для изменения масштаба исполь зуется команда g l S c a l e f с тремя аргументами, представляющи ми собой масштабные множители для каждой из осей:
glScalef(m x, my, mz)/
Например, если перед командными скобками вставим строку
g l S c a l e f ( 0 . 5 , 0 . 5 , 1 . 0 ) ;
то будет нарисована уменьшенная в два раза фигура. После ко манд рисования необходимо восстановить нормальный масштаб, т.е. в данном случае добавить строку:
g l S c a l e f ( 2 . 0 , 2 . 0 , 1 . 0 ) ;
Есть и другой способ запоминания/восстановления текущего масштаба, но о нем мы поговорим позднее. Восстанавливать мас штаб необходимо для того, чтобы каждое последующее обращение к обработчику перерисовки экрана не приводило бы к последова тельному уменыпению/увеличению изображения. Масштабные множители могут иметь отрицательные значения, при этом изо бражение переворачивается по соответствующей оси. При дву мерных построениях значение коэффициента по оси Z безразлич но, единица взята без особых соображений.
11.3.2. Поворот
Для поворота системы координат используется команда g l R o t a t e f с четырьмя аргументами: угол поворота в градусах и вектор поворота (три вещественных числа). Для двумерных по строений наиболее нагляден поворот вокруг оси Z. Реализуйте в C++Builder следующий пример:
Пример 11.8
- В обработчик события O n P a in t формы добавьте следую щие строки:
g l R o t a t e f (10, 0 . 0 , 0 . 0 , 1 . 0 ) ; glBegin(GL_QUADS);
g l V e r t e x 2 f ( 0 . 0 , 0 . 0 ) ; g lV e r te x 2 f ( 0 . 8 , 0 . 0 ) ; g l V e r t e x 2 f ( 0 . 6 , 0 . 5 ) ; g l V e r t e x 2 f ( 0 . 0 , 0 . 5 ) ;
g l E n d ( ) ;
- Создайте обработчик события O nK eypress с единственной командой I n v a l i d a t e ()
Теперь при нажатии любой клавиши окно перерисовывается, при этом каждый раз прямоугольник поворачивается на 10 граду сов вокруг оси Z. Здесь надо обратить внимание на следующие моменты:
-при положительном значении компоненты вектора поворот осуществляется против часовой стрелки;
-важно не само значение компоненты вектора, а ее знак и равенство/неравенство ее нулю.
-Измените код программы, чтобы поворот осуществлялся вокруг оси X или Y.
Точно так же, как было с масштабом, поворот действует на все последующие команды воспроизведения, так что при необхо димости текущее состояние восстанавливается обратным поворо том.
-Чтобы вы могли оценить преимущества использования «низкоуровневых» приемов программирования измените обработ чик события O n K ey P ress формы.
void __fastcall T F o rm l: : FormKeyPress (TObject * S e n d e r, c h a r &Key)
{
switch (Key)
{
case VK_SPACE:
I n v a l i d a t e () ; break;
case VK_RETURN:
I n v a l i d a t e R e c t (H andle, NULL, f a ls e ) ;
}
Окно перерисовывается в этих случаях по-разному: в первом случае хорошо заметно мерцание формы, во втором случае проис
ходит только перерисовка изображения и мерцание формы неза метно.
11.3.3. Перенос
Перенос системы координат осуществляется командой g l T r a n s l a t e f с тремя аргументами - величинами переноса для каждой из осей:
g l T r a n s l a t e f ( d x , dy, |
d z ) ; |
|
|
|
Ниже |
приведен |
пример |
использования |
команды |
g l T r a n s l a t e f .
Пример 11.9
g l T r a n s l a t e f ( 0 . 2 , 0 . 3 , 0 . 0 ) ; glBegin(GL_QUADS);
g l V e r t e x 2 f (0 . 0 , 0 . 0 ) ; g l V e r t e x 2 f ( 0 . 6 , 0 . 0 ) ; g l V e r t e x 2 f ( 0 . 4 , 0 . 5 ) ; g l V e r t e x 2 f ( 0 . 0 , 0 . 5 ) ;
g l E n d ( ) ;
g l T r a n s l a t e f ( - 0 . 2 , - 0 . 3 , 0 . 0 ) ;
Все сказанное по поводу восстановления системы координат справедливо и в отношении переноса.
11.3.4. Сохранение и восстановление текущего положения
При повороте и переносе изображения приходится постоянно следить, чтобы после рисования система координат вернулась на прежнее место. Для этого мы пользовались обратными поворотами и перемещениями. Такой подход неэффективен и редко встречает ся в примерах и профессиональных программах, потому что функ ции g l R o t a t e f и g l T r a n s l a t e f реализуются как перемноже ние текущей матрицы на матрицы поворота и переноса соответст венно. Поэтому вместо обратных переносов и поворотов используются команды g l P u s h M a t r i x и g lP o p M a tr ix . Коман
да g lP u s h M a tr ix запоминает значение текущей матрицы, а ко манда g lP o p M a tr ix восстанавливает матрицу. Эти команды оперируют со стеком. Использование этих функций делает код более понятным, а воспроизведение - более быстрым. Для боль шей ясности пример 11.9 из предыдущего раздела можно было бы записать следующим образом:
g lP u s h M a trix ( );
g l T r a n s l a t e f (0 .2 , 0 .3 , 0 .0 ); glBegin(GL_QUADS);
g lV e r te x 2 f (0 .0 , 0 . 0 ) ; g lV e r te x 2 f (0 .6 , 0 . 0 ) ; g lV e r te x 2 f (0 .4 , 0 . 5 ) ; g lV e r te x 2 f (0 .0 , 0 . 5 ) ;
g l E n d ();
g lP o p M a trix ( );
Перед операцией переноса запоминаем текущую систем)’ ко ординат (g lP u s h M a trix ), а после рисования четырехугольника - восстанавливаем ее (g lP o p M a trix ).
11.4. Построения в пространстве
Главной темой этого раздела является трехмерная графика. Начнем мы с обзора методов, позволяющих достичь объемности получаемых образов, а в конце раздела будут описаны методы по строения анимационных программ. Раздел завершает базовый курс изучения основ OpenGL, и после его изучения вы сможете созда вать высококачественные трехмерные изображения. По умолча нию объем сцены ограничен кубом с координатами точек вершин по диагоналям (-1, -1, -1) и (1,1,1).
11.4.1.Параметры вида
Впроцессе построения изображения координаты вершин подвергаются определенным преобразованиям. Подобным преоб разованиям подвергаются заданные векторы нормали. В OpenGL
существуют две матрицы, последовательно применяющиеся в пре
образовании координат. |
Одна из них - |
матрица |
моделирования |
|
(m o d e lv ie w |
m a tr ix ) , |
а другая - |
матрица |
проектирования |
( p r o j e c t i o n |
m a tr ix ) . |
Первая служит для задания положения |
объекта и его ориентации, вторая отвечает за выбранный способ проектирования. OpenGL поддерживает два типа проектирования - параллельное и перспективное.
Существует набор различных процедур, умножающих теку щую матрицу (моделирования или проектирования) на матрицу выбранного геометрического преобразования. Текущая матрица задается при помощи команды g lM a trix M o d e , имеющей один параметр, который может принимать значения GL__MODELVIEW, GL_PROJECTION или GL__TEXTURE, позволяя выбирать в качест ве текущей матрицы матрицу моделирования (видовую матрицу), матрицу проектирования или матрицу преобразования текстуры. Процедура g l L o a d l d e n t i t y устанавливает единичную теку щую матрицу. Обычно задание соответствующей матрицы начи нается с установки единичной и последовательного применения матриц геометрических преобразований.
11.4.1.1. Использование функции glFrustum
Для задания перспективного преобразования в OpenGL слу жит команда:
v o i d g lF r u s tr u m (G L d o u b le |
l e f t , |
G Ldoubie |
r i g h t , |
|
G Ldouble |
b o tto m , G Ldouble |
to p / |
G Ldouble |
n e a r , |
G Ldouble |
f a r ) ; |
|
|
|
Смысл передаваемых параметров ясен из рис. 11.2. Изначаль но камера находится в начале координат и направлена вдоль отри цательного направления оси Z. Обратите внимание на то, что в момент применения матрицы проектирования координаты объек тов уже переведены в систему координат камеры. Величины n e a r и f a r должны быть неотрицательными.
Дальнее
Рис. 11.2. К использованию команды g lF r u s tu m
Для задания параметров вида, в частности, определяющих об ласть воспроизведения в пространстве используется функция g lF ru s tu m . Все, что выходит за пределы этой области, будет от секаться при воспроизведении. Первые 2 аргумента задают коор динаты плоскостей отсечения слева и справа, третий и четвертый параметры определяют координаты плоскостей отсечения снизу и сверху. Последние аргументы задают расстояния до ближней и дальней плоскостей отсечения. Значения этих двух параметров должны быть положительными - это не координаты плоскостей, а расстояния от глаза наблюдателя до плоскостей отсечения.
Устанавливать видовые параметры не обязательно при каж дой перерисовке экрана, достаточно делать это лишь при измене нии размеров окна.
Пример 11.10
Попробуем нарисовать что-нибудь объемное, например, куб. Для придания трехмерности сцене поворачиваем ее по осям:
void __fastcall T F o rm l: : FormResize (TObject *Sender)
{
g l V i e w p o r t (0, |
0, |
C l ie n tW i d th , C lie n tH e ig h t) ; |
g l L o a d l d e n t i t y () ; |
|
|
g l F r u s t u m ( - l , |
1, |
- 1 , 1, 3, 10); |
g l T r a n s l a t e f (0 . 0 , 0 . 0 , - 8 . 0 ) ;
g l R o t a t e f |
( 3 0 . 0 , |
1 . 0 , |
0 . 0 , |
0 . 0 ) ; |
g l R o t a t e f |
( 7 0 . 0 , |
0 . 0 , |
1 . 0 , |
0 . 0 ) ; |
}
Построение куба сводится к построению шести квадратов, от дельно для каждой стороны фигуры:
v o i d __ f a s t c a l l T F o r m l : : F o r m P a i n t ( T O b j e c t *S ender)
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( - 1 . 0 , 1 . 0 , 1 . 0 ) ;
g l V e r t e x 3 f ( - 1 . 0 , - 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , - 1 . 0 , 1 . 0 ) ;
g l E n d ( ) ;
glBegin(GL_ QUADS);
g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , - 1 . 0 ) ;
g l V e r t e x 3 f ( 1 . 0 , - 1 . 0 , |
- 1 . 0 ) ; |
g l V e r t e x 3 f ( - 1 . 0 , - 1 . 0 , |
- 1 . 0 ) ; |
g l V e r t e x 3 f ( - 1 . 0 , 1 . 0 , |
- 1 . 0 ) ; |
g l E n d ( ) ; |
|
glBegin(GL_ QUADS);
g l V e r t e x 3 f ( - 1 . 0 , 1 . 0 , 1 . 0 ) ;
g l V e r t e x 3 f ( - 1 . 0 , 1 . 0 , |
- 1 . 0 ) ; |
|
g l V e r t e x 3 f ( - 1 . 0 , |
- 1 . 0 , |
- 1 . 0 ) ; |
g l V e r t e x 3 f ( - 1 . 0 , |
- 1 . 0 , 1 . 0 ) ; |
|
g l E n d ( ) ; |
|
|
glBegin(GL_QUADS);
g l V e r t e x 3 f ( 1 . 0 , |
1 . 0 , 1 . 0 ) ; |
g l V e r t e x 3 f ( 1 . 0 , |
- 1 . 0 , 1 . 0 ) ; |
g l V e r t e x 3 f ( 1 . 0 , |
- 1 . 0 , |
- 1 . 0 ) ; |
g l V e r t e x 3 f ( 1 . 0 , |
1 . 0 , |
- 1 . 0 ) ; |
g l E n d ( ) ;
glBegin(GL_QUADS); |
|
|
||
g l V e r t e x 3 |
f |
(- 1. 0, |
1.0, |
- 1 . 0); |
g l V e r t e x 3 |
f |
( - 1.0, |
1.0, |
1. 0); |
g l V e r t e x 3 f |
(1.0, |
1.0, |
1. 0); |
|
g l V e r t e x 3 f (1.0, 1.0, - 1 . 0) ; |
||||
g l E n d ; |
|
|
|
|
glBegin(GL_QUADS); |
|
|
||
g l V e r t e x 3 f ( - 1 . 0, |
- 1.0, |
- 1 . 0); |
g l V e r t e x 3 f ( 1 . 0 , - 1 . 0 , - 1 . 0 ) ; g l V e r t e x 3 f ( 1 . 0 , - 1 . 0 , 1 . 0 ) ; g l V e r t e x 3 f ( - 1 . 0 , - 1 . 0 , 1 . 0 ) ;
g l E n d ( ) ;
S w a p B u f f e r s ( D C ) ;
}
- Создайте обработчики событий O nC reate и OnDestroy.
void __fastcall T F o rm l: :FormCreate (TObject *Sender)
{
DC = G e tD C ( H a n d le ) ; |
|
||
S e tD C P ix e lF o r m a t( D C ) ; |
|
||
h r c |
= w g lC re a te C o n te x t(D C ) |
; |
|
wglM akeCurrent(DC, |
h rc ) ; |
|
|
g l C l e a r C o l o r ( 1 . 0 , 1 . 0 , 1 . 0 , 1 . 0 ) ; |
|||
/ / |
g l C l e a r C o l o r (0 . 5 , 0 . 5 , |
0 .75, 1 . 0 ) ; |
|
g l C o l o r 3 f ( 1 . 0 , 0 . 0 , 0 . 5 ) ; |
|
||
} |
|
|
|
v o i d |
__ f a s t c a l l T F o rm l:: FormDestroy (TObject *Sender) |
||
{ |
|
|
|
w g l M a k e C u r r e n t (0, |
0 ) ; |
|
|
w g l D e l e t e C o n t e x t ( h r c ) ; |
|
||
R e le a se D C (H a n d le , |
DC); |
|
|
D e le te D C (D C ); |
|
|
}
