программирование, создание программ, учебник Delphi, учебник по программированию, бейсек, делфи, си, паскаль
 
 
 

 

Трехмерные графики функций


В этой главе мы разработаем Windows-приложение, которое в контексте OpenGL изображает трехмерный график функции, заданной произвольным массивом чисел. Данные для графика могут быть прочтены из файла, на который указывает пользователь. Кроме этого, пользователь будет иметь возможность перемещать график вдоль трех пространственных осей, вращать его вокруг вертикальной и горизонтальной осей и просматривать как в обычном, так и скелетном режим. Регулируя параметры освещения поверхности, пользователь может добиться наибольшей реалистичности изображения, то есть усилить визуальный эффект трехмерного пространства на плоском экране.
Графики могут представлять собой результаты расчета какого-либо физического поля, например поверхности равной температуры, давления, скорости, индукции, напряжения и т. д. в части трехмерного пространства, называемой расчетной областью. Пользователь объекта должен заранее подготовить данные и записать их в определенном формате в файл. Объект по команде пользователя считывает данные, нормирует, масштабирует и изображает в своем окне, внедренном в окно приложения-клиента. Пользователь, манипулируя мышью, управляет местоположением и вращением графика, а открыв окно диалога Properties, изменяет другие его атрибуты.

Настройка проекта

  1. На странице VS Home Page выберите команду (гиперссылку) Create New Project.
  2. В окне диалога New Project выберите уже знакомый вам тип проекта: MFC Application, задайте имя проекта OG и нажмите ОК.
  3. В окне мастера MFC Application Wizard выберите вкладку Application Type и задайте такие настройки проекта: Single documents, MFC Standard, Document/View architecture support, Use MFC in a shared DLL.
  4. Перейдите на страницу Advanced Features диалога и снимите флажки Printing and print preview, ActiveX Controls, так как мы не будем использовать эти возможности.
  5. Нажмите кнопку Finish.

Этот тип стартовой заготовки позволяет работать с окном (cocview), которое помещено в клиентскую область окна-рамки (CMainFrame), и создать в этом окне контекст передачи OpenGL. Класс документа нам не понадобится, так как мы собираемся производить все файловые операции самостоятельно, используя свой собственный двоичный формат данных. В связи с этим нам не нужна помощь в сериализации данных, которую предоставляет документ. Для использования функций библиотеки OpenGL надо сообщить компоновщику, чтобы он подключил необходимые библиотеки OpenGL, на сей раз только две.

  1. Поставьте фокус на элемент OG в окне Solution Explorer и дайте Команду Project > Properties (или ее эквивалент View > Property Pages).
  2. В окне открывшегося диалога OG Property Pages выберите элемент дерева Linker > Input.
  3. Переведите фокус в поле Additional Inputs окна справа и добавьте в конец существующего текста имена файлов с описаниями трех библиотек: OPENGL32.LIB GLU32.LIB. Убедитесь в том, что все имена разделены пробелами и нажмите ОК.

Чтобы покончить с настройками общего характера, вставьте в конец файла StdAfx.h строки, которые обеспечивают видимость библиотеки OpenGL, а также некоторых ресурсов библиотеки STL:
#include <afxdlgs.h>
#include <raath.h>
//=== Подключение заголовков библиотек OpenGL
#include <gl/gl.h>
#include <gl/glu.h>
#include <vector>
using namespace std;
  
Вспомогательный класс
Нам вновь, как и в предыдущем уроке, понадобится класс, инкапсулирующий функциональность точки трехмерного пространства CPoint3D. Контейнер объектов этого класса будет хранить вершины изображаемой поверхности. В коде, который приведен ниже, присутствует слегка измененное по сравнению с предыдущим объявление класса CPoint3D, а также объявления новых данных и методов класса cocview. Заодно мы произвели упрощения стартового кода, которые обсуждались в уроке 5. Весь код введите в файл OGView.h вместо существующей в нем заготовки. Файл должен приобрести следующий вид1:
#pragma once
//========== Вспомогательный класс
class CPointSD
{
public: //====== Координаты точки
float x;
float у;
float z;
//====== Набор конструкторов
CPointSD ()
{
х = у - z = 0.f;
}
CPoint3D (float cl, float c2, float c3)
{
x = cl; z = c2; У = сЗ; ,
}
//====== Операция присвоения
CPoint3DS operator= (const CPointSDS pt)
x = pt.x; z = pt.z;
return *this;
У = pt.y;
//====== Конструктор копирования
CPointSD (const CPoint3D& pt)
{
*this = pt;
//=========== Класс окна OpenGL
class COGView :
public CView
{
protected:
COGView () ;
DECLARE_DYNCREATE(COGView)
public:
virtual ~COGView();
virtual void OnDraw(CDC* pDC) ;
virtual BOOL PreCreateWindow(CREATESTRUCT& cs) ,
//======= Новые данные класса
long m_BkClr; //
int m_LightParara[ll]; //
HGLRC m_hRC; //
HDC m_hdc; //
GLfloat m_AngleX; //
GLfloat m_AngleY; //
GLfloat m_AngleView; //
GLfloat m_fRangeX; //
GLfloat m_fRangeY; //
GLfloat m_fRangeZ; //
GLfloat m_dx; //
GLfloat m_dy; //
GLfloat m_xTrans; //
GLfloat m_yTrans; //
GLfloat m_zTrans; //
GLenura m_FillMode; //
bool m_bCaptured; //
bool m_bRightButton; //
bool m_bQuad; //
CPoint m_pt; //
UINT m_xSize; //
UINT m_zSize; //
//====== Массив вершин поверхности
vector <CPoint3D> m_cPoints;
//====== Новые методы класса
//=-==== Подготовка изображения
void DrawScene();
Цвет фона окна Параметры освещения Контекст OpenGL Контекст Windows Угол поворота вокруг оси X Угол поворота вокруг оси Y Угол перспективы Размер объекта вдоль X Размер объекта вдоль Y Размер объекта вдоль Z Квант смещения вдоль X Квант смещения вдоль Y Смещение вдоль X Смещение вдоль Y Смещение вдоль Z Режим заполнения полигонов Признак захвата мыши Флаг правой кнопки мыши Флаг использования GL_QUAD Текущая позиция мыши Текущий размер окна вдоль X Текущий размер окна вдоль Y
//====== Создание графика по умолчанию
void DefaultGraphic();
//====== Создание массива по данным из буфера
void SetGraphPoints(BYTE* buff, DWORD nSize);
//====== Установка параметров освещения
void SetLight();
//====== Изменение одного из параметров освещения
void SetLightParam (short lp, int nPos);
//====== Определение действующих параметров освещения
void GetLightParams(int *pPos); //====== Работа с файлом данных
void ReadData();
//====== Чтение данных из файла
bool DoRead(HANDLE hFile);
//====== Установка Работа с файлом данных
void SetBkColor();
DECLARE MESSAGE MAP()
  
Реакции на сообщения Windows
Вспомните, как вы ранее вводили в различные классы реакции на сообщения Windows и повторите эти действия для класса cOGView столько раз, сколько необходимо, чтобы в нем появились стартовые заготовки функций обработки следующих сообщений:

  • WM_CREATE — приложение требует создать окно вызовом CreateEx или Create;
  • WM_DESTROY — окно исчезло с экрана, но не из памяти;
  • WM_ERASEBKGND — фон окна должен быть стерт;
  • WM_LBUTTONDOWN — нажата левая кнопка мыши;
  • WM_LBUTTONUP — отпущена левая кнопка мыши;
  • WM_MOUSEMOVE — курсор мыши перемещается;
  • WM_RBUTTONDOWN — нажата правая кнопка мыши;
  • WM_RBUTTONUP — отпущена правая кнопка мыши;
  • WM_SIZE — изменился размер окна;
  • WM_TIMER — истек квант времени какого-то из таймеров.

В конструктор класса вставьте код установки начальных значений переменных:
COGView::COGView()
{
//====== Контекст передачи пока отсутствует
m_hRC = 0;
//====== Начальный разворот изображения
m_AngleX = 35.f;
m_AngleY = 20.f;
//====== Угол зрения для матрицы проекции
m_AngleView = 45.f;
//====== Начальный цвет фона
m_BkClr = RGB(0, 0, 96);
// Начальный режим заполнения внутренних точек полигона
m_FillMode = GL_FILL;
//====== Подготовка графика по умолчанию
DefaultGraphic();
//====== Начальное смещение относительно центра сцены
//====== Сдвиг назад на полуторный размер объекта
m_zTrans = -1.5f*m_fRangeX;
m_xTrans = m_yTrans = 0.f;
//== Начальные значения квантов смещения (для анимации)
m_dx = m_dy = 0.f;
//====== Мышь не захвачена
m_bCaptured = false;
//====== Правая кнопка не была нажата
m_bRightButton = false;
//====== Рисуем четырехугольниками
m_bQuad = true;
//====== Начальный значения параметров освещения
m_LightParam[0] = 50; // X position
m_LightParam[l] = 80; // Y position
m_LightParam[2] = 100; // Z position
m_LightParam[3] = 15; // Ambient light
m_LightParam[4] = 70; // Diffuse light
m_LightParam[5] = 100; // Specular light
m_LightParam[6] = 100; // Ambient material
m_LightParam[7] = 100; // Diffuse material
m_LightParam[8] = 40; // Specular material
m_LightParam[9] = 70; // Shininess material
m_LightParam[10] =0; // Emission material
}

Подготовка окна


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

  • установка стиля окна;
  • обработка сообщения WM_ERASEBACKGROUND и отказ от стирания фона;
  • установка pixel-формата;
  • создание контекста устройства (HDC) и контекста передачи (HGLRC);
  • специфическая обработка сообщения WM_SIZE;
  • обработка сообщения WM_PAINT;
  • освобождение контекстов при закрытии окна.

Как было отмечено ранее, окнам, которые в своей клиентской области используют контекст передачи OpenGL, при создании следует задать биты стиля WS_CLIPCHILDREN и ws_CLiPSiBLiNGS. Сделайте это внутри существующего тела функции PreCreateWindow класса cocview, добавив нужные биты стиля к тем, что устанавливаются в заготовке:
BOOL COGView::PreCreateWindow(CREATESTRUCT& cs)
{
//====== Добавляем биты стиля, нужные OpenGL
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
return CView::PreCreateWindow(cs);
}
Вы помните, что окно OpenGL не должно позволять Windows стирать свой фон, так как данная операция сильно тормозит работу конвейера. В связи с этим введите в функцию обработки WM_ERASEBKGND код, сообщающий системе, что сообщение уже обработано:
BOOL COGView::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
Окно OpenGL имеет свой собственный формат пикселов. Нам следует выбрать и установить подходящий формат экранной поверхности в контексте устройства HDC, а затем создать контекст передачи изображения (HGLRC). Для описания формата пикселов экранной поверхности используется структура PIXELFORMATDESCRIPTOR. Выбор формата зависит от возможностей карты и намерений разработчика. Мы зададим в полях этой структуры такие настройки:

  • глубину цвета — 24;
  • тип буферизации — двойной;
  • схему образования цвета RGBA;
  • количество бит для буфера глубины — 32;
  • поддержку регулировки прозрачностью и другие специфические настройки выключим.

В функцию OnCreate введите код подготовки окна OpenGL. Работа здесь ведется со структурой PIXELFORMATDESCRIPTOR. Кроме того, в ней создается контекст m_hRC и устанавливается в качестве текущего:
int COGView::OnCreate(LPCREATESTROCT IpCreateStruct)
{
if (CView::OnCreate(IpCreateStruct) == -1)
return -1;
PIXELFORMATDESCRIPTOR pfd = // Описатель формата
{
sizeof(PIXELFORMATDESCRIPTOR), // Размер структуры
1, // Номер версии
PFD_DRAW_TO_WINDOW | // Поддержка GDI
PFD_SUPPORT_OPENGL | // Поддержка OpenGL
PFD_DOUBLEBUFFER, // Двойная буферизация
PFD_TYPE_RGBA, // Формат RGBA, не палитра
24, // Количество плоскостей
//в каждом буфере цвета
24, 0, // Для компонента Red
24, 0, // Для компонента Green
24, 0, // Для компонента Blue
24, 0, // Для компонента Alpha
0, // Количество плоскостей
// буфера Accumulation
0, // То же для компонента Red
0, // для компонента Green
0, // для компонента Blue
0, // для компонента Alpha
32, // Глубина 2-буфера
0, // Глубина буфера Stencil
0, // Глубина буфера Auxiliary
0, // Теперь игнорируется
0, // Количество плоскостей
0, // Теперь игнорируется
0, // Цвет прозрачной маски
0 // Теперь игнорируется };
//====== Добываем дежурный контекст
m_hdc = ::GetDC(GetSafeHwnd());
//====== Просим выбрать ближайший совместимый формат
int iD = ChoosePixelForraat(m_hdc, spfd);
if ( !iD )
{
MessageBoxC'ChoosePixelFormat: :Error") ;
return -1;
}
//====== Пытаемся установить этот формат
if ( ISetPixelFormat (m_hdc, iD, Spfd) )
{
MessageBox("SetPixelFormat::Error");
return -1;
}
//====== Пытаемся создать контекст передачи OpenGL
if ( !(m_hRC = wglCreateContext (m_hdc)))
{
MessageBox("wglCreateContext::Error");
return -1;
}
//====== Пытаемся выбрать его в качестве текущего
if ( IwglMakeCurrent (m_hdc, m_hRC))
{
MessageBox("wglMakeCurrent::Error");
return -1;
//====== Теперь можно посылать команды OpenGL
glEnable(GL_LIGHTING); // Будет освещение
//====== Будет только один источник света
glEnable(GL_LIGHTO);
//====== Необходимо учитывать глубину (ось Z)
glEnable(GL_DEPTH_TEST);
//====== Необходимо учитывать цвет материала поверхности
glEnable(GL_COLOR_MATERIAL);
//====== Устанавливаем цвет фона .
SetBkColor () ;
//====== Создаем изображение и запоминаем в списке
DrawScene () ;
return 0;
}
Контекст передачи (rendering context) создается функцией wglCreateContext с учетом выбранного формата пикселов. Так осуществляется связь OpenGL с Windows. Создание контекста требует, чтобы обычный контекст существовал и был явно указан в параметре wglCreateContext. HGLRC использует тот же формат пикселов, что и НОС. Мы должны объявить контекст передачи в качестве текущего (current) и лишь после этого можем делать вызовы команд OpenGL, которые производят включение некоторых тумблеров в машине состояний OpenGL. Вызов функции DrawScene, создающей и запоминающей изображение, завершает обработку сообщения. Таким образом, сцена рассчитывается до того, как приходит сообщение о перерисовке WM_PAINT. Удалять контекст передачи надо после отсоединения его от потока. Это делается в момент, когда закрывается окно представления. Введите в тело заготовки OnDestroy следующие коды:
void COGView::OnDestroy(void)
{
//====== Останавливаем таймер анимации
KillTimer(1);
//====== Отсоединяем контекст от потока
wglMakeCurrent(0, 0); //====== Удаляем контекст
if (m_hRC)
{
wglDeleteContext(m_hRC);
m_hRC = 0;
}
CView::OnDestroy() ;
}
Так же как и в консольном проекте OpenGL, обработчик сообщения WM_SIZE должен заниматься установкой прямоугольника просмотра (giviewport) и мы, так же как и раньше, зададим его равным всей клиентской области окна. -Напомним, что конвейер OpenGL использует эту установку для того, чтобы поместить изображение в центр окна и растянуть или сжать его пропорционально размерам окна. Кроме того, в обработке onSize с помощью матрицы проецирования (GL_PROJECTION) задается тип проекции трехмерного изображения на плоское окно. Мы выбираем центральный или перспективный тип проецирования и задаем при этом угол зрения равным m_AngleView. В конструкторе ему было присвоено значение в 45 градусов:
void COGView::OnSize(UINT nType, int ex, int cy)
{
//====== Вызов родительской версии
CView::OnSize(nType, ex, cy) ;
//====== Вычисление диспропорций окна
double dAspect = cx<=cy ? double(cy)/ex : double(ex)/cy;
glMatrixMode (GL_PROJECTION) ;
glLoadldentity() ;

//====== Установка режима перспективной проекции
gluPerspective (m_AngleView, dAspect, 0.01, 10000.);
//====== Установка прямоугольника просмотра
glViewport(0, 0, сх, су);
}
  
Реакция на сообщение о перерисовке
В функции перерисовки должна выполняться стандартная последовательность действий, которая стирает back-буфер и буфер глубины, корректирует матрицу моделирования, вызывает из списка команды рисования и по завершении рисования переключает передний и задний буферы. Полностью замените существующий текст функции OnDraw на тот, который приведен ниже: void COGView:: OnDtaw (CDC" pDC]
glClear<GL_COLOft_BUFFER_BIT | GL_BEPTH_3UFFER_BIT);
glMatrixMode(GLjtoDELVIEH) ;
glLoadldentitylT;
SetLight() ;
//=====Формировать
//===== Переключение буферов SwapBuffera
}

Параметры освещения


Установка параметрпв освещения осуществляется подобно тому, как это делалось в предыдущем уроке. Но здесь мы храним все параметры для тога, чтобы можно было управлять освещенностью изображения. Немного позже разработаем диалог, с помощью которого пользователь программы сможет изменять настройки освещения, а сейчас введите коды функции SetLight:
void COGView::SetLight()
{
//====== Обе поверхности изображения участвуют
//====== при вычислении цвета пикселов
//====== при учете параметров освещения
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
//====== Позиция источника освещения
//====== зависит от размеров объекта
float fPos[] =
{
(m_LightParam[0]-50)*m_fRangeX/100,
(m_LightParam[l]-50)*m_fRangeY/100,
(m_LightParam[2]-50)*m_fRangeZ/100,
l.f
};
glLightfv(GL_LIGHTO, GL_POSITION, fPos);
/1 ====== Интенсивность окружающего освещения
float f = m_LightParam[3]/100.f;
float fAmbient[4] = { f, f, f, O.f };
glLightfv(GL_LIGHTO, GL_AMBIENT, fAmbient);
//====== Интенсивность рассеянного света
f = m_LightParam[4]/100.f;
float fDiffuse[4] = { f, f, f, O.f };
glLightfv(GL_LIGHTO, GL_DIFFUSE, fDiffuse);
//====== Интенсивность отраженного света
f = m_LightParam[5]/100.f;
float fSpecular[4] = { f, f, f, 0.f };
glLightfv(GL_LIGHTO, GL_SPECULAR, fSpecular);
//====== Отражающие свойства материала
//====== для разных компонентов света
f = m_LightParam[6]/100.f;
float fAmbMat[4] = { f, f, f, 0.f };
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, fAmbMat);
f = m_LightParam[7]/100.f;
float fDifMat[4] = { f, f, f, 1.f };
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fDifMat);
f = m_LightParam[8]/100.f;
float fSpecMat[4] = { f, f, f, 0.f };
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fSpecMat);
//====== Блесткость материала
float fShine = 128 * m_LightParam[9]/100.f;
glMaterialf(GL FRONT AND BACK, GL SHININESS, fShine);
//====== Излучение света материалом
f = m_LightParam[10]/100.f;
float f Emission [4] = { f , f , f , 0 . f } ;
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, fEmission) ;
}
  
Установка цвета фона
Введите вспомогательную функцию, которая позволяет вычислить и изменить цвет фона окна OpenGL. Позже мы введем возможность выбора цвета фона с помощью стандартного диалога Windows по выбору цвета:
void COGView: :SetBkColor ()
{
//====== Расщепление цвета на три компонента
GLclampf red = GetRValue (m_BkClr) /255 . f ,
green = GetGValue (m_BkClr) /255. f ,
blue = GetBValue(m_BkClr) /255. f ;
//====== Установка цвета фона (стирания) окна
glClearColor (red, green, blue, 0.f);
//====== Непосредственное стирание
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ;
}
  
Подготовка изображения
Разработаем код функции DrawScene, которая готовит и запоминает изображение на основе координат вершин, хранимых в контейнере m_cPoints. Изображение по выбору пользователя формируется либо в виде криволинейных четырехугольников (GL_QUADS), либо в виде полосы связанных четырехугольников (GL_QUAD_STRIP). Точки изображаемой поверхности расположены над регулярной координатной сеткой узлов в плоскости (X, Z). Размерность этой сетки хранится в переменных m_xSize и m_zSize. Несмотря на двухмерный характер сетки, для хранения координат вершин мы используем линейный (одномерный) контейнер m_cPoints, так как это существенно упрощает объявление контейнера и работу с ним. В частности, упрощаются файловые операции. Выбор четырех смежных точек генерируемого примитива (например, GL_QUADS) происходит с помощью четырех индексов (n, i, j, k). Индекс п последовательно пробегает по всем вершинам в порядке слева направо. Более точно алгоритм перебора вершин можно определить так: сначала проходим по сетке узлов вдоль оси X при Z = 0, затем увеличиваем Z и вновь проходим вдоль X и т. д. Индексы i, j, k вычисляются относительно индекса п. В ветви связанных четырехугольников (GL_QUAD_STRIP) работают только два индекса.
В контейнер m_cPoints данные попадают после того, как они будут прочитаны из файла. Для того чтобы при открытии приложения в его окне уже находился график функции, необходимо заранее создать файл с данными по умолчанию, открыть и прочесть его содержимое. Это будет сделано в коде функций
DefaultGraphic и SetGraphPoints. Алгоритм функции DrawScene разработан в предположении, что контейнер точек изображаемой поверхности уже существует. Флаг m_bQuad используется для выбора способа создания полигонов: в виде отдельных (GL_QUADS) или связанных (GL_QUAD_STRIP) четырехугольников. Позднее мы введем команду меню для управления этой регулировкой:
void COGView: : DrawScene ()
{
//====== Создание списка рисующих команд
glNewList (1, 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++)
//====== Связанные полигоны начинаются
//====== на каждой полосе вновь
if (!m_bQuad)
glBegin (GLJ2UAD_STRIP) ;
//====== Цикл прохода вдоль оси X
for (UINT x=0; x<nx; x++)
// i, j, k, n — 4 индекса вершин примитива при
// обходе в направлении против часовой стрелки
int j = i + m_xSize, // Индекс узла с большим Z
k = j+1/ // Индекс узла по диагонали
n = i+1; // Индекс узла справа
//=== Выбор координат 4-х вершин из контейнера
float
xi = m_cPoints [i] .x,
yi = m_cPoints [i] .у,
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;
//====== Задание вектора нормали
glNorma!3f (vx,vy,vz);
// Ветвь создания несвязанных четырехугольников
if (m_bQuad)
{
//==== Обход вершин осуществляется
//==== в направлении против часовой стрелки
glColorSf (0.2f, 0.8f, l.f);
glVertexSf (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) ;
glColor3f (0.7f, 0.8f, l.f);
glVertexSf (xn, yn, zn) ;
}
else
//==== Ветвь создания цепочки четырехугольников
{
glColor3f (0.9f, 0.9f, l.0f);
glVertexSf (xn, yn, zn) ;
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). В связи с этим равны нулю компоненты az и bх векторов боковых сторон в формуле для нормали (см. раздел «Точное вычисление нормалей» в предыдущем уроке).

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика