|
Считая, что данные о координатах точек
изображаемой поверхности уже известны и
расположены в контейнере m_cPoints, напишем коды
функции DrawScene, которая создает изображение
поверхности и запоминает его в виде списка
команд OpenGL. Как вы помните, одним из
технологических приемов OpenGL, которые ускоряют
процесс передачи (rendering), является
предварительная заготовка изображения, то есть
запоминание и компиляция списка рисующих команд.
Напомним, что отображаемый график представляет
собой криволинейную поверхность (например,
равного уровня температуры). Ось Y, по которой
откладываются интересующие пользователя значения
функции, направлена вверх. Ось X направлена
вправо, а ось Z — вглубь экрана. Часть плоскости
(X, Z), для точек которой известны значения Y,
представляет собой координатную сетку.
Изображаемая поверхность расположена над
плоскостью (X, Z), а точнее, над этой сеткой.
Поверхность можно представить себе в виде
одеяла, сшитого из множества лоскутов. Каждый
лоскут мы будем задавать в виде
четырехугольника, как-то ориентированного в
пространстве. Все множество четырехугольников
поверхности также образует сетку. Для задания
последовательности четырехугольников в OpenGL
существует пара команд:
glBegin (GL_QUADS)
;
// Здесь располагаются команды, задающие
четырехугольники
glEnd() ;
Четырехугольник задается координатами своих
вершин. При задании координат какой-либо
вершины, например, командой givertex3f (х, у,
z);, можно сразу же определить ее цвет,
например, командой gicolor3f (red, green, blue);.
Если цвета вершин будут разными, а режим
заполнения равен константе GL_FILL, то цвета
внутренних точек четырехугольника примут
промежуточное значение. Конвейер OpenGL
производит аппроксимацию цвета так, что при
перемещении от одной вершины к другой он
изменяется плавно.
Режим растеризации или заполнения промежуточных
точек графического примитива задается командой
glPolygonMode. OpenGL различает фронтальные (front-facing
polygons), обратные (back-facing polygons) и
двухсторонние многоугольники. Режим заполнения
их отличается, поэтому первый параметр функции
glPolygonMode должен определить тип полигона (GL_FRONT,
GL_BACK или GL_FRONT_AND_BACK).
Второй параметр собственно и определяет режим
заполнения. Он может принимать значение GL_POINT,
GL_LINE или GL_FILL. Первый выбор даст лишь
обозначение примитива в виде его вершин, второй
— даст некий скелет, вершины будут соединены
линиями, а третий заполнит все промежуточные
точки примитива. По умолчанию принят режим
GL_FILL и мы получаем сплошной лоскут.'Если в
качестве первого параметра задать
GL_FRONT_AND_BACK, то изменения второго
параметра будут касаться обеих поверхностей
одеяла. Другие сочетания дают на первый взгляд
странные эффекты: так, если задать сочетание (GL_FRONT,
GL_LINE), то лицевая сторона одеяла будет
обозначена каркасом (frame view), а изнаночная
по умолчанию будет сплошной (GL_FILL).
Поверхность при этом будет полупрозрачна.
Мы решили оставить
неизменным значение GL_FRONT_AND_BACK для
первого параметра и дать пользователю
возможность изменять режим заполнения (второй
параметр glPolygonMode) по его желанию.
Впоследствии внесем эту настройку в диалог
свойств СОМ-объекта, а результат выбора
пользователя будем хранить в переменной
m_FillMode. С учетом сказанного введите коды
реализации функции DrawScenel
//====== Подготовка изображения
void
COpenGL::DrawScene()
{
//====== Создание списка рисующих команд
glNewListd,
GL_COMPILE) ;
//====== Установка режима заполнения
//====== внутренних точек полигонов
glPolygonMode(GL_FRONT_AND_BACK, m_FillMode);
//====== Размеры изображаемого объекта
UINTnx = m_xSize-l,
nz = m_zSize-l;
//====== Выбор способа создания полигонов
if
(m_bQuad)
glBegin (GL QUADS);
//=== Цикл прохода по слоям изображения (ось Z)
for (UINT z=0, i=0; z<nz; z++, i++)
//=== Связанные полигоны начинаются
//=== на каждой полосе вновь if (!m_bQuad)
glBegin(GL_QUAD_STRIP)
;
//=== Цикл прохода вдоль оси X
for
(UINT x=0; x<nx;
х++,
i++)
{
// i, j, k, n — 4 индекса вершин примитива при
// обходе в направлении против часовой стрелки
int
j = i + m_xSize,
// Индекс узла с
большим Z
k = j+1, // Индекс
узла по диагонали
n = i+1; // Индекс
узла справа
// Выбор координат 4-х вершин из контейнера
float
xi = m_cPoints [i] .
х,
yi = m_cPoints [i]
.y,
zi = m_cPoints [i]
. z,
xj = m_cPoints [ j
] .x,
yj = m_cPoints [ j
] .y,
zj = m_cPoints [ j
] .z,
xk = m_cPoints [k]
.x,
yk = m_cPoints [k]
. y,
zk = m_cPoints [k]
. z,
xn = m_cPoints [n]
.x,
yn = m_cPoints [n]
.y,
zn = m_cPoints [n]
. z,
//=== Координаты векторов боковых сторон
ах = xi-xn,
ay = yi-yn,
by = yj-yi,
bz = zj-zi,
//=== Вычисление вектора нормали
vx = ay*bz,
vy = -bz*ax,
vz = ax*by,
//===
Модуль нормали
v = float (sqrt (vx*vx + vy*vy + vz*vz) ) ;
//====== Нормировка вектора нормали
vx /= v;
vy /= v;
vz /= v;
//====== Задание вектора нормали
glNormalSf (vx,vyfvz);
// Ветвь создания несвязанных четырехугольников
if
(m_bQuad)
{
//====== Обход вершин осуществляется
//=== в направлении против часовой стрелки
glColorSf (0.2f,
0.8f, l.f);
glVertex3f (xi, yi, zi);
glColor3f <0.6f, 0.7f, l.f);
glVertexSf (xj,
уj,
zj);
glColorSf (0.7f,
0.9f, l.f);
glVertexSf (xk, yk,
zk);
glColorSf (0.7f,
0.8f, l.f);
glVertexSf (xn, yn,
zn); }
else
// Ветвь создания цепочки четырехугольников
{
glColor3f (0.9f, 0..9f, l.Of);
glVertexSf (xi, yi,
zi);
glColorSf (0.5f,
0.8f, l.0f);
glVertexSf (xj,
уj,
zj);
}
}
//======
Закрываем блок команд
GL_QUAD_STRIP
if
(!m_bQuad)
glEnd(); }
//======
Закрываем блок команд
GL_QUADS
if
(m_bQuad) glEnd() ;
//====== Закрываем
список команд OpenGL
glEndList ();
}
Для осмысления алгоритма надо учитывать, что
количество узлов сетки вдоль того или иного
направления (X или Z) на единицу больше
количества промежутков (ячеек). Кроме того, надо
иметь в виду, что при расчете освещения OpenGL
учитывает направление нормали (перпендикуляра) к
поверхности. Реалистичность изображения во
многом достигается благодаря аккуратному
вычислению нормалей. Нормаль является
характеристикой вершины (узла сетки). |