Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2633

.pdf
Скачиваний:
6
Добавлен:
07.01.2021
Размер:
54.26 Mб
Скачать

находится. Если не сместить влево, пирамида начнется у левого края экрана и не будет выглядеть как пирамида.

На оси Y вычитается yloop из 6, иначе пирамида будет нарисована сверху вниз. Результат умножаетсям на 2.4. Иначе кубы будут находиться друг на друге. (2.4 примерная высота куба). Затем мы отнимается 7, чтобы пирамида начиналась от низа экрана и строилась вверх.

По оси Z осуществляется перемещение на двадцать единиц в глубь экрана. Таким способом пирамида вписывается в экран.

Поворот вокруг оси X осуществляется наклоном кубов вперед на 45 градусов минус 2 и умножить на yloop. Режим перспективы наклоняет кубы автоматически, поэтому необходимо вычитание, чтобы скомпенсировать наклон.

xrot дает управление углом с клавиатуры.

После этого осуществляется поворот кубов на 45 градусов вокруг оси Y, и добавление yrot, для того чтобы управлять с клавиатуры поворотом вокруг оси Y:

glRotatef(45.0f-(2.0f*yloop)+xrot,1.0f,0.0f,0.0f); // Наклонять

кубы вверх и вниз

 

glRotatef(45.0f+yrot,0.0f,1.0f,0.0f);

// Вращать кубы

вправо и влево

 

Далее для выбора цвета куба используется команда glColor3fv(), которая выполняет загрузку всех трех значений цвета (красный, зеленый, голубой). Аббревиатура 3fv означает 3 значения, числа с плавающей точкой, v указатель на массив. Цвет выбирается по переменной yloop-1, что дает разные цвета для каждого ряда кубиков:

glColor3fv(boxcol[yloop-1]);

// Выбор цвета куба

Куб будет нарисован цветом, выбранным командой glColor3fv(), в

месте, куда он передвинут:

 

 

glCallList(box);

 

// Рисование куба

Цвет крышки (темный).

 

 

glColor3fv(topcol[yloop-1]);

 

// Выбор цвета верха

Рисование списка отображения top.

 

glCallList(top);

 

// Рисование крышки

}

 

 

}

 

 

return TRUE;

 

// Возвращение обратно.

}

 

 

Все необходимые изменения будут выполнены в WinMain(). Код

будет добавлен сразу после строки SwapBuffers(hDC):

SwapBuffers(hDC);

// Двойная буферизация)

if (keys[VK_LEFT])

// Была нажата стрелка влево?

113

{

 

yrot-=0.2f;

// Если так, то поворот кубов влево

}

 

if (keys[VK_RIGHT])

// Была нажата стрелка вправо?

{

 

yrot+=0.2f;

// Если так, то поворот кубов вправо

}

 

if (keys[VK_UP])

// Была нажата стрелка вверх?

{

 

xrot-=0.2f;

// Если так, то наклон кубов вверх

}

 

if (keys[VK_DOWN])

// Была нажата стрелка вниз?

{

 

xrot+=0.2f;

// Если так, то наклон кубов вниз

}

 

Как во всех предыдущих программах далее следует проверка того, что заголовок на верху окна правильный:

if (keys[VK_F1])

// Была нажата кнопка F1?

{

 

keys[VK_F1]=FALSE;

// Если так - установка значения FALSE

KillGLWindow();

// Закроем текущее окно OpenGL

//Переключение режима "Полный экран"/"Оконный" fullscreen=!fullscreen;

//Создание окна OpenGL

if (!CreateGLWindow("Display List Tutorial", 640, 480, 160, fullscreen))

{

return 0;// Выйти, если окно не было создано

}

}

}

}

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

Измените код программы так, чтобы убрать верхнюю часть пирамиды (куб в вершине пирамиды), чтобы она казалась обрезанной, как на рис. 20.

114

Рис. 19. Списки отображения

Рис. 20. Изменение пирамиды

Контрольные вопросы

1.Для чего служат списки отображения?

2.Зачем необходим цикл for (yloop=1;yloop<6;yloop++)?

3.Почему BuildList() находится после LoagGLTextures(),а не

наоборот?

4.Какие параметры имеет функция glNewList()и за что отвечает каждый параметр?

115

5.Что сохраняется в списке отображения?

6.Для чего и как именуются списки отображения?

7.Какой командой добавляется цвет к текстуре?

2.13. Растровые шрифты

Вначале необходимо внести следующее примечание о том, что этот код применим только в Windows. Он использует функции wgl Windows, для построения шрифтов. Очевидно, Apple имеет функции agl, которые должны делать то же самое, а X имеет glx и невозможно гарантировать, что этот код переносим.

Текст данного примера так же основывается на коде первого проекта. С начала необходимо добавить: заголовочный файл stdio.h для стандартных операций ввода/вывода, stdarg.h – для разбора текста и конвертирования переменных в текст, и, наконец – math.h, для того чтобы перемещать текст по экрану, используя SIN и COS:

#include <windows.h>

// Заголовочный файл для Windows

#include <stdio.h>

// Заголовочный файл для стандартной

библиотеки ввода/вывода

 

#include <gl\gl.h>

// Заголовочный файл для библиотеки

OpenGL32

 

 

#include <gl\glu.h>

// Заголовочный файл для библиотеки

GLu32

 

 

#include <gl\glaux.h

// Заголовочный файл для библиотеки

GLaux

 

 

#include <math.h>

// Заголовочный файл для

математической библиотеки (НОВОЕ)

#include <stdarg.h>

// Заголовочный файл для функций для

работы с переменным

// Количеством аргументов (НОВОЕ)

HDC hDC=NULL;

// Приватный контекст устройства GDI

HGLRC hRC=NULL;

// Постоянный контекст рендеринга

HWND hWnd=NULL;

// Сохраняет дескриптор окна

HINSTANCE hInstance;

// Сохраняет экземпляр приложения

Также следует

добавить

три новых переменных. В base будет

сохранен номер первого списка отображения, который создается. Каждому символу требуется собственный список отображения. Символ 'A' – 65 список отображения, 'B' – 66, 'C' – 67, и т.д. Поэтому 'A' будет сохранен в списке отображения base плюс 65.

Затем добавляется два счетчика (cnt1 и cnt2), которые будут изменяться с разной частотой, и используются для перемещения текста

116

по экрану, использу SIN и COS, что будет создавать эффект хаотичного движения строки текста по экрану. Также счетчики будут использоваться, чтобы изменять цвет символов:

GLuintbase;

// База списка отображения

GLfloatcnt1;

// Первый счетчик для передвижения и

закрашивания текста

 

GLfloat cnt2;

// Второй счетчик для передвижения и

закрашивания текста

 

boolkeys[256];

// Массив для работы с клавиатурой

boolactive=TRUE;

// Флаг активации окна, по умолчанию =

TRUE

 

boolfullscreen=TRUE; // Флаг полноэкранного режима

LRESULTCALLBACK WndProc(HWND, UINT, WPARAM,

LPARAM);

// Объявление WndProc

В следующей части кода происходит построение шрифта:

GLvoid BuildFont(GLvoid) // Построение растрового шрифта

{

 

HFONT font;

// Идентификатор фонта

base = glGenLists(96);

// Выделим место для 96 символов

(Новая часть кода)

 

Это наиболее трудная часть кода. Объявление 'HFONT font' задает шрифт в Windows. Затем происходит определение base. Здесь создается группа из 96 списков отображения, используя glGenLists(96). После того, как списки отображения созданы, переменная base будет содержать номер первого списка.

Необходимо создать новый шрифт. Начать следует с задавания размера шрифта. Размер задается отрицательным числом. Минус указывает Windows, что надо найти нам шрифт, основанный на высоте символов:

font = CreateFont(24), // Высота фонта ( НОВОЕ )

Если используется положительное число, выбирается шрифт, основанный на высоте ячейки.

Затем необходимо определить ширину ячейки. Здесь она установлена в 0. При помощи установки значения в 0, Windows будет использовать значение по умолчанию:

0, // Ширина фонта

Угол отношения (Angle of Escapement) позволяет вращать шрифт. Исключая 0, 90, 180, и 270 градусов, так как у шрифта будет обрезаться то, что не попало внутрь невидимой квадратной границы. Угол наклона (Orientation Angle), цитируя справку MSDN, определяет угол, в десятых долях градуса, между базовой линией символа и осью X устройства:

0, // Угол отношения

117

0, // Угол наклона В качестве параметра ширины шрифта можно использовать числа

от 0-1000, или использовать одно из предопределенных значений: FW_DONTCARE-0, FW_NORMAL-400,FW_BOLD-700, и FW_BLACK900. Есть множество других предопределенных значений, но и эти четыре дают хорошее разнообразие. Чем выше значение, тем более толстый, или иначе жирный шрифт:

FW_BOLD,

// Ширина шрифта

Курсив, подчеркивание и перечеркивание может быть или TRUE

или FALSE. Если подчеркивание TRUE, шрифт будет подчеркнут. Если

FALSE то, нет.

 

FALSE,

// Курсив

FALSE,

// Подчеркивание

FALSE,

// Перечеркивание

Идентификатор набора символов описывает тип набора символов, который необходимо использовать. ANSI – это набор типов, используемый в этом примере.

Для использования шрифта типа Webdings или Wingdings, необходимо использовать SYMBOL_CHARSET вместо

ANSI_CHARSET:

ANSI_CHARSET, // Идентификатор набора символов Точность вывода очень важна, так как этот параметр сообщает

Windows какой из наборов символов использовать, если их доступно больше чем один. OUT_TT_PRECIS сообщает Windows что, если доступен больше чем один тип шрифта, то выбрать с тем же самым названием Truetype версию шрифта.

OUT_TT_PRECIS,

// Точность вывода

Точность отсечения – тип отсечения, который применяется, когда

вывод символов идет вне области отсечения:

CLIP_DEFAULT_PRECIS,

// Точность отсечения

Качество вывода – очень важный параметр. Здесь возможно

выбрать тип отсечения:

 

PROOF, DRAFT, NONANTIALIASED, DEFAULT

Или:

 

ANTIALIASED.

 

Сглаживание (Antialiasing)

шрифта – это тот же самый эффект,

который получается, когда включается сглаживание шрифта в Windows. При этом буквы выглядят более четко:

ANTIALIASED_QUALITY,

// Качество вывода

Затем идут настройка шага.

Для настройки шага необходимо

выбрать: DEFAULT_PITCH, FIXED_PITCH и VARIABLE_PITCH.

Для настройки семейства, необходимо выбрать:

118

FF_DECORATIVE, FF_MODERN, FF_ROMAN, FF_SCRIPT, FF_SWISS, FF_DONTCARE, т.е. FF_DONTCARE|DEFAULT_PITCH, //

Семейство и шаг

В данной программе будем использовать шрифт Courier New. Для использования любого другого шрифта, необходимо просто ввести его

имя вместо указанного:

 

"Courier New");

// Имя шрифта

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

необходимо:

 

 

 

SelectObject(hDC, font);

// Выбрать созданный шрифт

wglUseFontBitmaps(hDC, 32, 96, base);

// Построить 96

символов, начиная с пробела

 

 

}

 

 

 

Следующий код удаляет 96 списков отображения из памяти,

начиная с первого списка, заданного 'base':

 

GLvoid KillFont(GLvoid)

// Удаление шрифта

{

 

 

 

glDeleteLists(base, 96);

// Удаление всех 96 списков

отображения

 

 

 

}

 

 

 

Далее задается функция вывода текста GL. Эта часть кода

вызывается по команде glPrint:

 

 

GLvoid glPrint(const char *fmt, ...)

 

// Заказная функция «Печати» GL

 

{

 

 

 

Текст находится в строке символов *fmt.

 

В первой строке кода ниже выделяется память для строки на 256

символов:

 

 

 

char text[256];

// Место для строки

 

va_list ap;

// Указатель на список аргументов

Text – это строка, которую необходимо напечатать на экране. Во второй строке ниже создается указатель, который указывает на список параметров, которые передаются наряду со строкой. Если переменные посылаются вместе с текстом, строка укажет на них.

В следующих двух строках кода проверяется, если, что-нибудь для вывода. Если нет текста, fmt – NULL, и ничего не будет выведено на

экран:

 

if (fmt == NULL)

// Если нет текста

return;

// Ничего не делать

va_start(ap, fmt);

// Разбор строки переменных

119

vsprintf(text, fmt, ap); // Конвертирование символов в реальные

коды

va_end(ap); // Результат помещается в строку

Затем в стек заносится GL_LIST_BIT, это защищает другие списки отображения, которые используются в программе, от влияния

glListBase:

 

glPushAttrib(GL_LIST_BIT);

// Заполнить биты списка

отображения

 

glListBase(base - 32);

// Задать базу символа в 32

Теперь, когда известно, где находятся символы, можно сообщить, что пора выводить текст на экран.

glCallLists – команда, которая может вывести больше, чем один список отображения на экран одновременно.

Встроке ниже делается следующее: сообщается, что будут показываться на экране списки отображения; при вызове функции strlen(text) вычисляется, сколько символов необходимо отобразить на экране, затем необходимо указать максимальное значение посылаемых символов (нельзя посылать больше чем 255 символа, поэтому можно использовать UNSIGNED_BYTE – любое значение от 0-255); сообщается, что надо вывести, передачей строки 'text'.

Правая сторона у каждого символа известна для каждого списока отображения. После того, как символ выведен, OpenGL переходит на правую сторону выведенного символа. Следующий символ или выведенный объект будут выведены, начиная с последнего места вывода GL, которое будет справа от последнего символа.

Наконец происходит возвращение настройки GL GL_LIST_BIT обратно, как было прежде, чем была установлена база, используя glListBase(base-32):

glCallLists(strlen(text),GL_UNSIGNED_BYTE, text); // Текст списками отображения

glPopAttrib();

// Возврат битов списка отображения ( НОВОЕ )

}

Вкоде Init изменилось только одно: добавлена строка BuildFont(), которая вызывает код выше для построения шрифта, чтобы OpenGL мог использовать его позже:

int InitGL(GLvoid) // Все начальные настройки OpenGL здесь

{

 

glShadeModel(GL_SMOOTH);

// Разрешить плавное затенение

glClearColor(0.0f, 0.0f, 0.0f, 0.5f);

// Черный фон

glClearDepth(1.0f);

// Установка буфера глубины

glEnable(GL_DEPTH_TEST);

// Разрешение теста глубины

120

glDepthFunc(GL_LEQUAL); // Тип теста глубины

// Действительно хорошие вычисления перспективы glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);

BuildFont();

// Построить шрифт

return TRUE;

// Инициализация окончена

}

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

Следует обратить внимание, что размер шрифта не зависит от перемещения текста вглубь. Если переместиться на 1 единицу в экран, то можно расположить текст, где-нибудь от -0.5 до +0.5 по оси X. Это дает возможность лучше контролировать точное позиционирование текста, не используя десятичные разряды. При этом размер текста не изменится:

int DrawGLScene(GLvoid) // Здесь производится все рисование

{

glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT); // Очистка экран и буфера глубины

glLoadIdentity();

// Сброс просмотра

glTranslatef(0.0f,0.0f,-1.0f);

// Передвижение на одну единицу

вглубь Далее необходимо воспользоваться нестандартными

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

// Цветовая пульсация, основанная на положении текста glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),1.0f-

0.5f*float(cos(cnt1+cnt2)));

Красный меняется от 1.0 до 1.0, используя COS и счетчик 1, зеленый – от 1.0 до 1.0 используя SIN и счетчик 2, синий – от 0.5 до 1.5 используя COS и счетчики 1 + 2. Тем самым синий никогда не будет равен 0, и текст не должен никогда полностью исчезнуть.

Далее команда glRasterPos2f(x,y) будет позиционировать растровый шрифт на экране.

Центр экрана как прежде в 0,0. Следует отметить, что нет координаты Z. Растровые шрифты используют только ось X (лево/право) и ось Y (вверх/вниз). Поскольку перемещение происходит

121

на одну единицу в экран, левый край равен -0.5, и правый край равен +0.5. Необходимо переместиться на 0.45 пикселей влево по оси X. Это устанавливает текст в центр экрана, иначе он был бы правее на экране, потому что текст будет выводиться от центра направо.

Нестандартные вычисления делают в большой степени то же самое, как и при вычислении цвета. Происходит перемещение текста по оси X от -0.50 до -0.40. При этом текст на экране будет всегда. Текст будет ритмично раскачиваться влево и вправо, используя COS и счетчик 1. Текст будет перемещаться от -0.35 до +0.35 по оси Y, используя SIN и счетчик 2:

// Позиционирование текста на экране glRasterPos2f(-0.45f+0.05f*float(cos(cnt1)), 0.35f*float(sin(cnt2)));

Далее реализуется сам вывод текста на экран. Текст будет выведен на экран точно в том месте, где его установили– glPrint("{любой текст}".

glPrint("Active OpenGL Text With … - %7.2f", cnt1);

// Вывод

текста GL на экран

 

 

В конце надо увеличить значение обоих счетчиков на разную

величину, чтобы была цветовая пульсация и передвижение текста:

cnt1+=0.051f;

// Увеличение первого счетчика

 

cnt2+=0.005f;

// Увеличение второго счетчика

 

return TRUE;

// Все правильно

 

}

 

 

Также необходимо добавить KillFont() в конец KillGLWindow(). При этом списки отображения очищаются прежде, чем осуществляется выход из программы:

if (!UnregisterClass("OpenGL",hInstance))

{

MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);

hInstance=NULL;

// Установить копию приложения в ноль

}

 

KillFont();

// Уничтожить шрифт

}

 

Результаты работы программ представлены на рис.21.

122

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]