Урок 20. Маскирование
Добро пожаловать на урок номер 20. Растровый формат изображения
поддерживается, наверное, на каждом компьютере, и, скорее всего во всех операционных
системах. С ним не только легко работать, но и очень просто загружать и использовать
как текстуру. До этого урока мы использовали смешивание, чтобы вывести текст
на экран и другие изображения без стирания того, что под текстом или изображением.
Это эффективно, но результат не всегда удовлетворительный.
В большинстве случаев текстура смешивается излишне или не достаточно
хорошо. При разработке игры со спрайтами, Вы не хотите, чтобы сцена за вашим
персонажем просвечивала через его тело. Когда вы выводите текст на экран, Вы
хотите, чтобы текст был сплошным и легким для чтения.
В данном случае очень пригодиться маскирование. Маскирование
— двух шаговый процесс. Вначале мы выводим черно-белое изображение нашей текстуры
поверх сцены. Белое - прозрачная часть нашей текстуры. Черное - сплошная часть
нашей текстуры. Мы будем использовать такой тип смешивания, при котором только
черное будет появляться на сцене. Это похоже на форму для выпечки. Затем мы
меняем режим смешивания, и отображаем нашу текстуру поверх, того, что вырезано
черным. Опять же, из-за того режима смешивания, который мы используем, только
те части нашей текстуры будут скопированы на экран, которые находятся сверху
черной маски.
Я приведу весь код в этом уроке кроме тех разделов, которые не изменились. Итак, если Вы готовы научиться кое-чему новому, давайте начнем!
#include <windows.h> // Заголовочный файл для Windows
#include <math.h> // Заголовочный файл для математической библиотеки Windows
#include <stdio.h> // Заголовочный файл для стандартной библиотеки ввода/вывода
#include <gl\gl.h> // Заголовочный файл для библиотеки OpenGL32
#include <gl\glu.h> // Заголовочный файл для библиотеки GLu32
#include <gl\glaux.h> // Заголовочный файл для библиотеки Glaux
HDC hDC=NULL; // Приватный контекст устройства GDI
HGLRC hRC=NULL; // Постоянный контекст визуализации
HWND hWnd=NULL; // Сохраняет дескриптор окна
HINSTANCE hInstance; // Сохраняет экземпляр приложения
Мы будем использовать 7 глобальных переменных в этой программе.
masking - логическая переменная (ИСТИНА / ЛОЖЬ), которая будет отслеживать,
действительно ли маскировка включена или выключена. mp используется,
чтобы быть уверенным, что клавиша 'M' не нажата. sp используется, чтобы
быть уверенным, что 'Пробел' не нажат, и переменная scene будет отслеживать,
действительно ли мы рисуем первую или вторую сцену.
Мы выделяем память для 5 текстур, используя переменную texture[5]. loop - наш общий счетчик, мы будем использовать его несколько раз в нашей программе, чтобы инициализировать текстуры, и т.д. Наконец, мы имеем переменную roll. Мы будем использовать roll, чтобы сделать прокрутку текстуры по экрану. Создавая изящный эффект! Мы будем также использовать ее для вращения объекта в сцене 2.
bool keys[256]; // Массив для работы с клавиатурой
bool active=TRUE; // Флаг активации окна, по умолчанию = TRUE
bool fullscreen=TRUE;// Флаг полноэкранного режима
bool masking=TRUE; // Маскирование Вкл/Выкл
bool mp; // M нажата?
bool sp; // Пробел нажат?
bool scene; // Какая сцена выводиться
GLuint texture[5]; // Память для пяти наших текстур
GLuint loop; // Общая переменная цикла
GLfloat roll; // Катание текстуры
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Объявление WndProc
Код загрузки картинки не изменился. Он тот же, какой был в уроке
6, и т.д.
В коде ниже мы резервируем память для 5 изображений. Мы очищаем место и загружаем все 5 картинок. Мы делаем цикл по всем изображениям и конвертируем их в текстуры для использования в нашей программе. Текстуры сохранены в texture[0-4].
int LoadGLTextures() // Загрузка картинки и конвертирование в текстуру
{
int Status=FALSE; // Индикатор состояния
AUX_RGBImageRec *TextureImage[5]; // Создать место для текстуры
memset(TextureImage,0,sizeof(void *)*5); // Установить указатель в NULL
if ((TextureImage[0]=LoadBMP("Data/logo.bmp")) && // Текстура эмблемы
(TextureImage[1]=LoadBMP("Data/mask1.bmp")) && // Первая маска
(TextureImage[2]=LoadBMP("Data/image1.bmp")) && // Первое изображение
(TextureImage[3]=LoadBMP("Data/mask2.bmp")) && // Вторая маска
(TextureImage[4]=LoadBMP("Data/image2.bmp"))) // Второе изображение
{
Status=TRUE; // Задать статус в TRUE
glGenTextures(5, &texture[0]); // Создать пять текстур
for (loop=0; loop<5; loop++) // Цикл по всем пяти текстурам
{
glBindTexture(GL_TEXTURE_2D, texture[loop]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
}
}
for (loop=0; loop<5; loop++) // Цикл по всем пяти текстурам
{
if (TextureImage[loop]) // Если текстура существуют
{
if (TextureImage[loop]->data) // Если изображение текстуры существует
{
free(TextureImage[loop]->data); // Освободить память изображения
}
free(TextureImage[loop]); // Освободить структуру изображения
}
}
return Status; // Возвращаем статус
}
Код ReSizeGLScene() не изменился, и мы опустим его.
Код инициализации необходимая формальность. Мы загружаем наши текстуры, задаем цвет очистки, задаем и разрешаем тест глубины, включаем плавное закрашивание, и разрешаем наложение текстуры. У нас простая программа, поэтому нет необходимости в сложной инициализации :).
int InitGL(GLvoid) // Все начальные настройки OpenGL здесь
{
if (!LoadGLTextures()) // Переход на процедуру загрузки текстуры
{
return FALSE; // Если текстура не загружена возвращаем FALSE
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Черный фон
glClearDepth(1.0); // Установка буфера глубины
glEnable(GL_DEPTH_TEST); // Разрешение теста глубины
glShadeModel(GL_SMOOTH); // Разрешить плавное закрашивание
glEnable(GL_TEXTURE_2D); // Разрешение наложения текстуры
return TRUE; // Инициализация завершена OK
}
Теперь самое интересное. Наш код рисования! Мы начинаем как обычно. Мы очищаем фон и буфер глубины. Затем мы сбрасываем матрицу вида, и перемещаемся на 2 единицы вглубь экрана так, чтобы мы могли видеть нашу сцену.
int DrawGLScene(GLvoid) // Здесь мы все рисуем
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очистка экрана и буфера глубины
glLoadIdentity(); // Сброс матрицы вида
glTranslatef(0.0f,0.0f,-2.0f); // Перемещение вглубь экрана на 2 единицы
Первая строка ниже выбирает текстуру 'эмблемы' сайта NeHe. Мы
наложим текстуру на экран, используя четырехугольник. Мы задаем четыре текстурных
координаты совместно с четырьмя вершинами.
Дополнение от Джонатана Роя: помните, что OpenGL - графическая
система на основе вершин. Большинство параметров, которые Вы задаете, регистрируются
как атрибуты отдельных вершин. Текстурные координаты - один из таких атрибутов.
Вы просто задаете соответствующие текстурные координаты для каждой вершины многоугольника,
и OpenGL автоматически заполняет поверхность между вершинами текстурой, в процессе
известном как интерполяция. Интерполяция - стандартная геометрическая техника,
которая позволяет OpenGL определить, как данный параметр изменяется между вершинами,
зная только значение, которое параметр имеет в вершинах непосредственно.
Как и в предыдущих уроках, мы представим, что мы на внешней
стороне четырехугольника и назначаем координаты текстуры следующим образом:
(0.0, 0.0) нижний левый угол, (0.0, 1.0) верхний левый угол, (1.0, 0.0) нижний
правый, и (1.0, 1.0) верхний правый. А теперь, с учетом этой настройки, Вы можете
указать, какие координаты текстуры соответствуют точке в середине четырехугольника?
Правильно, (0.5, 0.5). Но в коде Вы ни где не задавали эту координату, не так
ли? Когда рисуется четырехугольник, OpenGL вычисляет это за Вас. И просто волшебство
то, что он это делает безотносительно к позиции, размера, или ориентации многоугольника!
В этом уроке мы привнесем еще один интересный трюк, назначив
текстурные координаты, которые будут отличаться от 0.0 и 1.0. Текстурные координаты
должны быть нормализованы. Значение 0.0 отображается на одну грань текстуры,
в тоже время как значение 1.0 отображает на противоположную грань, захватывая
всю ширину или высоту изображения текстуры за одну единицу, независимо от размера
многоугольника или размера изображения текстуры в пикселях (о чем мы не должны
волноваться при выполнении наложения текстуры, и это делает жизнь в целом несколько
проще). Значения большие, чем 1.0, будут просто заворачивать наложение с другой
грани и повторять текстуру. Другими словами, например, текстурная координата
(0.3, 0.5) отображает точно тот же самый пиксель в изображении текстуры, как
и координата (1.3, 0.5), или как (12.3,-2.5). В этом уроке, мы добьемся мозаичного
эффекта, задавая значение 3.0 вместо 1.0, повторяя текстуру девять раз (3x3
мозаика) по поверхности четырехугольника.
Дополнительно, мы используем переменную roll, чтобы заставить текстуру перемещаться (или скользить) по поверхности четырехугольника. Значение 0.0 для roll, которое добавлено к вертикальной координате текстуры, означает, что наложение текстуры на нижнею грань четырехугольника начинается на нижней грани изображения текстуры, как показано на рисунке слева. Когда roll равна 0.5, наложение на нижнею грань четырехугольника начинается с половины изображении (см. рисунок справа). Прокрутка текстуры может использоваться, чтобы создать отличные эффекты типа движущихся облаков, вращающихся слов вокруг объектов, и т.д.
glBindTexture(GL_TEXTURE_2D, texture[0]); // Выбор текстуры эмблемы
glBegin(GL_QUADS); // Начало рисования текстурного четырехугольника
glTexCoord2f(0.0f, -roll+0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Лево Низ
glTexCoord2f(3.0f, -roll+0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Право Низ
glTexCoord2f(3.0f, -roll+3.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Право Верх
glTexCoord2f(0.0f, -roll+3.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Лево Верх
glEnd(); // Завершения рисования четырехугольника
Продолжим… Теперь мы разрешаем смешивание. Чтобы этот эффект работал мы также должны отключить тест глубины. Очень важно, чтобы Вы это сделали! Если Вы не отключили тест глубины, вероятно, вы ничего не увидите. Все Ваше изображение исчезнет!
glEnable(GL_BLEND); // Разрешение смешивания
glDisable(GL_DEPTH_TEST); // Запрет теста глубины
Первое, что мы делаем после того, как мы разрешаем смешивание и отключаем тест глубины — проверка надо ли нам маскировать наше изображение или смешивать его на старый манер. Строка кода ниже проверяет истина ли маскировка. Если это так, то мы задаем смешивание таким образом, чтобы наша маска выводилась на экран правильным образом.
if (masking) // Маскировка разрешена?
{
Если маскировка ИСТИНА, что строка ниже задаст смешивание для
нашей маски. Маска — это только черно-белая копия текстуры, которую мы хотим
вывести на экран. Любая белая часть маски будет прозрачной. Любая черная часть
маски будет непрозрачной.
Команда настройки смешивания ниже делает следующее: цвет адресата (цвет на экране) будет установлен в черный, если часть нашей маски, которая копируется на экран, черная. Это означает, что часть экрана, которая попадает под черную часть нашей маски, станет черной. Все, что было на экране под маской, будет очищено в черный цвет. Часть экрана, попавшего под белую маску не будет изменена.
glBlendFunc(GL_DST_COLOR,GL_ZERO); // Смешивание цвета экрана с нулем (Черное)
}
Теперь мы проверим, какую сцену надо вывести. Если scene ИСТИНА, то мы выведем вторую сцену. Если scene ЛОЖЬ, то мы выведем первую сцену.
if (scene) // Рисовать вторую сцену?
{
Мы не хотим, чтобы объекты были слишком большими, поэтому мы
перемещаемся еще на одну единицу в экран. Это уменьшит размер наших объектов.
После того, как мы переместились в экран, мы вращаемся от 0-360 градусов в зависимости от значения roll. Если roll - 0.0, мы будем вращать на 0 градусов. Если roll - 1.0, мы будем вращать на 360 градусов. Довольно быстрое вращение, но я не хочу создавать другую переменную только, для того чтобы вращать изображение в центре экрана. :)
glTranslatef(0.0f,0.0f,-1.0f); // Перемещение вглубь экрана на одну единицу
glRotatef(roll*360,0.0f,0.0f,1.0f); // Вращение по оси Z на 360 градусов
Мы уже имеем прокрутку эмблемы на экране, и мы вращаем сцену по оси Z, при этом любые объекты, которые мы рисуем, вращаются против часовой стрелки, теперь все, что мы должны сделать — это проверить, включена ли маскировка. Если это так, то мы выведем нашу маску, затем наш объект. Если маскировка выключена, то мы выведем только наш объект.
if (masking) // Маскирование включено?
{
Если маскировка ИСТИНА, то код ниже выведет нашу маску на экран.
Наш режим смешивания уже задан ранее. Теперь все, что мы должны сделать — это
вывести маску на экран. Мы выбираем маску 2 (потому что это вторая сцена). После
того, как мы выбрали текстуру маски, мы накладываем текстуру на четырехугольник.
Четырехугольник размером на 1.1 единицу влево и вправо так, чтобы он немного
выходил за край экрана. Мы хотим показать только одну текстуру, поэтому координаты
текстуры изменяются от 0.0 до 1.0.
После отрисовки нашей маски на экране будет находиться сплошная черная копия нашей завершающей текстуры. Это похоже на то, что формочка для выпечки, которая по форме совпадает с нашей завершающей текстурой, вырезала на экране пустое черное место.
glBindTexture(GL_TEXTURE_2D, texture[3]); // Выбор второй маски текстуры
glBegin(GL_QUADS); // Начало рисования текстурного четырехугольника
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Низ Лево
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Низ Право
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Верх Право
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // верх Лево
glEnd(); // Конец рисования четырехугольника
}
Теперь, когда мы вывели нашу маску на экран, пришло время снова
изменить режим смешивания. На сей раз, мы собираемся, указать OpenGL, что надо
копировать на экран любую часть нашей цветной текстуры, которая НЕ черная. Поскольку
завершающая текстура - точная копия маски, но с цветом, выводятся на экран только
те части нашей текстуры, которые попадают сверху черной части маски. Поскольку
маска черная, ничто с экрана не будет просвечивать через нашу текстуру. И с
нами остается сплошная текстура, плавающая сверху по экрану.
Заметьте, что мы выбираем второе изображение после выбора завершающего
режима смешивания. При этом выбирается наше цветное изображение (изображение,
на котором основана вторая маска). Также заметьте, что мы выводим это изображения
с правого верхнего угла маски. Те же самые текстурные координаты, те же самые
вершины.
Если мы не выведем маску, наше изображение будет скопировано на экран, но оно смешает с тем, что было на экране.
glBlendFunc(GL_ONE, GL_ONE); // Копирование цветного изображения 2 на экран
glBindTexture(GL_TEXTURE_2D, texture[4]); // Выбор второго изображения текстуры
glBegin(GL_QUADS); // Начало рисования текстурного четырехугольника
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Низ Лево
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Низ Право
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Верх Право
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Верх Лево
glEnd(); // Завершение рисования четырехугольника
}
Если scene была ЛОЖЬ, мы выведем первую сцену (моя любимая).
else // Иначе
{
Вначале мы проверяем, включена ли маскировка, точно так же как в коде выше.
if (masking) // Вкл. маскировка?
{
Если masking ИСТИНА, то мы выводим нашу маску 1 на экран (маска для сцены 1). Заметьте, что текстура прокручивается справа налево (roll добавляется к горизонтальной координате текстуры). Мы хотим, чтобы эта текстура заполнила весь экран, именно поэтому мы и не перемещаемся глубже в экран.
glBindTexture(GL_TEXTURE_2D, texture[1]); // Выбор первой маски текстуры
glBegin(GL_QUADS); // Начало рисования текстурного четырехугольника
glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Низ Лево
glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Низ Право
glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Верх Право
glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Верх Лево
glEnd(); // Конец рисования четырехугольника
}
Снова мы разрешаем смешивание и выбираем нашу текстуру для сцены 1. Мы накладываем эту текстуру поверх маски. Заметьте, что мы прокручиваем эту текстуру таким же образом, иначе маска и завершающие изображение не совместились.
glBlendFunc(GL_ONE, GL_ONE); // Копирование цветного изображения 1 на экран
glBindTexture(GL_TEXTURE_2D, texture[2]); // Выбор первого изображения текстуры
glBegin(GL_QUADS); // Начало рисования текстурного четырехугольника
glTexCoord2f(roll+0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Низ Лево
glTexCoord2f(roll+4.0f, 0.0f); glVertex3f( 1.1f, -1.1f, 0.0f); // Низ Право
glTexCoord2f(roll+4.0f, 4.0f); glVertex3f( 1.1f, 1.1f, 0.0f); // Верх Право
glTexCoord2f(roll+0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Верх Лево
glEnd(); // Конец рисования четырехугольника
}
Затем мы разрешаем тест глубины, и отключаем смешивание. Это предотвращает странные вещи, происходящие от случая к случаю в остальной части нашей программы. :)
glEnable(GL_DEPTH_TEST); // Разрешение теста глубины
glDisable(GL_BLEND); // Запрещение смешивания
В завершении надо увеличить значение roll. Если roll больше, чем 1.0, мы вычитаем 1.0. Это предотвращает появление больших значений roll.
roll+=0.002f; // Увеличим прокрутку нашей текстуры
if (roll>1.0f) // Roll больше чем
{
roll-=1.0f; // Вычтем 1 из Roll
}
return TRUE; // Все OK
}
Код KillGLWindow(), CreateGLWindow() и WndProc()
не изменился, поэтому мы опустим его.
Первое что изменилось в WinMain() - заголовок окна. Теперь название "Урок Маскирования NeHe". Вы можете изменить это название на такое, какое Вы захотите. :)
int WINAPI WinMain(
HINSTANCE hInstance, // Экземпляр
HINSTANCE hPrevInstance, // Предыдущий экземпляр
LPSTR lpCmdLine, // Параметры командной строки
int nCmdShow) // Показать состояние окна
{
MSG msg; // Структура сообщения окна
BOOL done=FALSE; // Булевская переменная выхода из цикла
// Запросим пользователя какой режим отображения он предпочитает
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?",
"Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; // Оконный режим
}
// Создадим наше окно OpenGL
if (!CreateGLWindow("NeHe's Masking Tutorial",640,480,16,fullscreen))
{
return 0; // Выходим если окно не было создано
}
while (!done) // Цикл, который продолжается пока done=FALSE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Есть ожидаемое сообщение?
{
if (msg.message==WM_QUIT) // Мы получили сообщение о выходе?
{
done=TRUE; // Если так done=TRUE
}
else // Если нет, продолжаем работать с сообщениями окна
{
TranslateMessage(&msg); // Переводим сообщение
DispatchMessage(&msg); // Отсылаем сообщение
}
}
else // Если сообщений нет
{
// Рисуем сцену. Ожидаем нажатия кнопки ESC и сообщения о выходе от DrawGLScene()
// Активно? Было получено сообщение о выходе?
if ((active && !DrawGLScene()) || keys[VK_ESCAPE])
{
done=TRUE; // ESC или DrawGLScene просигналили "выход"
}
else // Не время выходить, обновляем экран
{
SwapBuffers(hDC); // Переключаем буферы (Двойная буферизация)
Теперь наш простой обработчик клавиатуры. Мы проверяем, нажат ли пробел. Если это так, то мы устанавливаем переменную sp в ИСТИНА. Если sp ИСТИНА, код ниже не будет выполняться второй раз, пока пробел не был отпущен. Это блокирует быстрое переключение сцен в нашей программе. После того, как мы устанавливаем sp в ИСТИНА, мы переключаем сцену. Если scene была ИСТИНА, она станет ЛОЖЬ, если она была ЛОЖЬ, то станет ИСТИНА. В нашем коде рисования выше, если scene ЛОЖЬ, первая сцена будет выведена. Если scene ИСТИНА, то вторая сцена будет выведена.
if (keys[' '] && !sp) // Пробел нажат?
{
sp=TRUE; // Сообщим программе, что пробел нажат
scene=!scene; // Переключение сцен
}
Код ниже проверяет, отпустили ли мы пробел (если НЕ ' '). Если пробел был отпущен, мы устанавливаем sp в ЛОЖЬ, сообщая нашей программе, что пробел не нажат. Задав sp в ЛОЖЬ, код выше снова проверит, был ли нажат пробел, и если так, то все повториться.
if (!keys[' ']) // Пробел отжат?
{
sp=FALSE; // Сообщим программе, что пробел отжат
}
В следующем разделе кода проверяется нажатие клавиши 'M'. Если она нажата, мы устанавливаем mp в ИСТИНА, указывая программе не проверять это условие в дальнейшем, пока клавиша не отпущена, и мы переключаем masking с ИСТИНА на ЛОЖЬ или с ЛОЖЬ на ИСТИНА. Если masking ИСТИНА, то в коде рисования будет подключена маскировка. Если masking ЛОЖЬ, то маскировка будет отключена. Если маскировка выключена, то объект будет смешан с содержимым экрана, используя смешивание на старый манер, который мы использовали до сих пор.
if (keys['M'] && !mp) // M нажата?
{
mp=TRUE; // Сообщим программе, что M нажата
masking=!masking; // Переключение режима маскирования Выкл/Вкл
}
Последняя небольшая часть кода проверяет, отпущена ли "M". Если это так, то mp присваивается ЛОЖЬ, давая знать программе, что мы больше не нажимаем клавишу 'M'. Как только клавиша 'M' была отпущена, мы можем нажать ее еще раз, чтобы переключить включение или отключение маскировки.
if (!keys['M']) // M отжата?
{
mp=FALSE; // Сообщим программе, что M отжата
}
Как и в других уроках, удостоверитесь, что заголовок наверху окна правильный.
if (keys[VK_F1]) // Была нажата кнопка F1?
{
keys[VK_F1]=FALSE; // Если так - установим значение FALSE
KillGLWindow(); // Закроем текущее окно OpenGL
fullscreen=!fullscreen; // Переключим режим "Полный экран"/"Оконный"
// Заново создадим наше окно OpenGL
if (!CreateGLWindow("NeHe's Masking Tutorial",640,480,16,fullscreen))
{
return 0; // Выйти, если окно не было создано
}
}
}
}
}
// Сброс
KillGLWindow(); // Закроем окно
return (msg.wParam); // Выйдем из программы
}
Создание маски не сложно, и не требует много времени. Лучший способ сделать маску из готового изображения, состоит в том, чтобы загрузить ваше изображение в графический редактор или программу просмотра изображений, такую как infranview, и перевести изображение в серую шкалу. После того, как Вы сделали это, увеличивайте контрастность так, чтобы серые пиксели стали черными. Вы можете также уменьшить яркость, и т.д. Важно, что белый цвет это ярко белый, и черный это чисто черный. Если Вы имеете любые серые пиксели в вашей маске, то эта часть изображения будет прозрачной. Наиболее надежный способ удостовериться, что ваша маска точная копия вашего изображения, снять копию изображения с черным. Также очень важно, что ваше изображение имеет ЧЕРНЫЙ цвет, и маска имеет БЕЛЫЙ цвет! Если Вы создали маску и заметили квадратную форму вокруг вашей текстуры, или ваш белый - не достаточно яркий (255 или FFFFFF) или ваш черный - не точно черный (0 или 000000). Ниже Вы приведен пример маски и изображения, которое накладывается поверх маски. Изображение может иметь любой цвет, который Вы хотите, но фон должен быть черный. Маска должна иметь белый фон и черную копию вашего изображения.
Это - маска - > . Это
- изображение - > .
(Прим.переводчика: Вы можете в изображении назначить любой цвет фоновым, важно, чтобы он стал белым в маске, а все остальные цвета перешли в черный цвет. Можно воспользоваться для выделения прозрачного цвета (или группы цветов) инструментов Select/Color Range в AdobePhotoshop, а затем залить выделенную область в белый цвет (тем самым вы создадите прозрачные области в маски), а затем инвертировать эту область и залить ее черным цветом (тем самым вы создадите непрозрачные области в маске).
Эрик Десросиерс подсказал, что Вы можете также проверять значение
каждого пикселя в вашем рисунке, во время его загрузки. Если Вы хотите иметь
прозрачный пиксель, Вы можете присвоить ему альфа значение 0. Для всех других
цветов Вы можете присвоить им альфа значение 255. Этот метод будет также работать,
но требует дополнительного кодирования. Текущий урок прост и требует очень немного
дополнительного кода. Я не отвергаю другие методы, но когда я писал обучающую
программу, я пробовал сделать код простым, понятым и удобным. Я только хотел
сказать, что есть всегда другие способы сделать эту работу. Спасибо за замечание
Эрик.
В этом уроке я показал Вам простой, но эффективный способ рисования
частей текстуры на экран без использования альфа канала. Стандартное смешивание
обычно выглядит плохо (текстуры или прозрачные, или они не прозрачные), и текстурирование
с альфа каналом требует, чтобы ваши изображения имели альфа канал. С растровыми
изображениями удобно работать, но они не поддерживают альфа канала, эта программа
показывает нам, как обойти ограничения растровых изображений, демонстрируя крутой
способ создавать эффекты типа штампа (effect overlay).
Благодарю Роба Санте за идею и за пример кода. Я никогда не
слышал об этом небольшом трюке, пока он не указал на него. Он хотел, чтобы я
подчеркнул, что, хотя эта уловка и работает, но для нее требуется два прохода,
и это снижается производительность. Он рекомендует, чтобы Вы использовали текстуры
с альфа каналом для сложных сцен.
Я надеюсь, что Вам понравится этот урок. Если Вы что-то не понимаете,
или Вы нашли ошибку в уроке, пожалуйста, сообщите мне. Я хочу сделать уроки
лучше. Ваши замечания также очень важны!
© Jeff Molofee (NeHe)
3 сентября 2002 (c) Сергей Анисимов