В своё время попалась мне в руки одна очень интересная задачка — реализация программы, которая строит ландшафты по карте высот. В качестве карты высот можно было бы использовать любое растровое изображение, яркость точки на котором была бы для нас высотой её над поверхностью …
Как известно, Э.Э. Александров никогда не подкидывает неинтересных задачек, что ж, я взялся, кое-что из этого вышло …
Мы реализуем метод построения поверхности по «карте высот». Картой высот называют растровое изображение, на котором интенсивность света определяет высоту точки над некоторым нулевым уровнем.
Все материалы по данному проекту можно скачать отсюда:
- Летняя версия программы с исходниками
- Зимняя версия программы с исходниками
- Старая папка Landscape Project на Box
В нашем случае мы используем полноцветное изображение bmp. Использование именно полноцветного RGB а не greyscale удобно тем, что мы можем выбирать требуемый цветовой канал для построения поверхности, что даёт возможность кодировать в одном изображении несколько поверхностей. Применение может быть следующим: в игре при построении модели пещеры r-канал используется для построения нижней поверхности — пола, g-канал — для создания потолка пещеры и b-канал для моделирования воды в разных по высоте нишах пещеры.
Дополнительно, для более удобной демонстрации функции построения поверхностей, мы реализуем некоторые интересные возможности: создание водной поверхности с применением эффекта отражения, добавление «неба» и эффекта тумана, реализация режима обхода карты (с явлением инерции и способностью ходить по воде). Плюс к этому, посвящая Новому году, появился снег, музыка Jingle Bells на фоне и синхронизация 30fps, чтобы это всё не выглядело страшно.
Реализация программ Landscape и Landscape WE (Winter Edition) на языке C++ с использованием функций OpenGL.
Исходный код программы (зимняя версия, как более полная) находится в приложении 1.
В целом, программа работает следующее образом:
Инициализация: на этом этапе происходит создание окна Windows, создание контекста OpenGL, загрузка текстур, загрузка карты высот и создание массива высот, инициализация основных параметров рендеринга, установка начального положения наблюдателя, в зимней версии создаётся снег и включается музыка.
Основной цикл программы: обрабатываются нажатия клавиш, происходит обработка эффекта частиц, перемещения наблюдателя, происходит отрисовка сцены по элементам.
Выход из программы: восстанавливаются контексты устройств, отключается музыка, освобождается память.
Это первый взгляд на функционирование программы. Рассмотрим весь ход программы подробнее. Для начала, определимся с используемыми переменными.
Основная функция построения поверхности применяет следующие переменные и константы:
- MAP_SIZE — размер создаваемого ландшафта, и соответственно используемого изображения.
- STEP_SIZE — шаг построения поверхности, равен степени двойки.
- MAP_CHAN — канал изображения, используемый для построения.
- SKY_H, WATER_H — высота неба и воды.
- MN_HEIGHT, MX_HEIGHT — минимальная и максимальная высота поверхности.
- g_HeightMap — массив высот.
Для реализации обходчика требует создание переменных и констант:
- MAX_SPEED — максимальная скорость движения.
- CAM_HEIGHT — высота камеры над землёй.
- wx, wy, wz — позиция наблюдателя.
- cx, cy, cz — координаты точки, на которую смотрит наблюдатель
- yang, xang — углы поворота головы наблюдателя горизонтально и вертикально.
- wspeed, sspeed — скорости движения вперёд и «стрейфа».
- MOUSE_SENS — чувствительность мышки.
- MOUSE_STX, MOUSE_STY — начальные координаты мышки.
- mmvx, mmvy — сдвиг мышки.
В зимней версии добавлены переменные для реализации снега:
- SNOW_COUNT — количество снежинок в мире
- MIN_SNOW_DIST, MAX_SNOW_DIST — максимальная и минимальная дистанция отображения снега
- SNOW_SPEED — скорость падения снега
- SNOW_SIZE — размер снежинок
- snows — массив снежинок структур snow
Используются несколько элементарных функций для обработки переменных:
- g2r — преобразует градусы в радианы
- randf — случайное число в пределах от 0 до 1.
Основные функции программы:
LoadTextures
Загрузка текстур. Используется массив texname, в котором указаны названия загружаемых текстур. В цикле загружаются изображения, создаются текстуры с линейной фильтрацией и освобождается память. Текстуры будут доступны через массив textures.
LoadHeightFile
Загрузка карты высот. Изначально проверяется наличие файла высот, во избежание ошибок. Файл загружается как изображение. Для получения конкретного значения высоты мы используем указатель на байт. Изначально он устанавливается в первую позицию данных изображения, сдвигается на величину используемого канала изображения. Далее, в цикле значение под указателем помещается в массив высот, указатель сдвигается на три позиции вперёд, индекс увеличивается на единицу. Цикл продолжается до тех пор, пока не будут считаны все данные, то есть пока индекс не дойдет до значения квадрата размера карты. Затем память изображения очищается, и массив высот передаётся для дальнейшего использования.
Height
Возвращает значение высоты в дискретной точке. Получает на вход ссылку на массив высот и номер требуемой точки карты. Функция напрямую считывает значение массива высот, преобразует его в зависимости от максимальной и минимальной высоты и возвращает это значение.
GetGroundHeight
Возвращает значение высоты земли в произвольной точке. Получает на вход ссылку на массив высот и координаты точки.
Функция считывает значения высот в четырёх точках, составляющих квадрат в котором находится требуемая точка, и на основании некоторых формул вычисляет значение высоты в данной точке.
Высоту точки на наклонной плоскости можно определить, если разницу высот между опорными точками домножить на расстояние от одной точки до наблюдателя, деленное на расстояние между опорными точками, прибавив высоту этой точки. Таким образом, мы находим высоты точек на двух параллельных плоскостях, и затем, используя полученные значения, находим результирующую высоту, как среднюю на плоскости, перпендикулярной этим плоскостям.
SetVertexColor
Устанавливает тон данной точки ландшафта зависимости от высоты точки над нулевой поверхностью. Цвет устанавливается от 50% серого до 100% белого цвета.
RenderHeightMap
Создаёт ландшафт по массиву высот из квадратных сегментов. На вход функции поступает массив высот.
В двух вложенных циклах осуществляется проход по всему массиву. На каждом проходе строятся 4 точки для каждого конкретного квадрата в пространстве, устанавливается тон точки и накладываются координаты текстур. В зависимость от режима рендеринга карта создаётся из элементов типа QUADS или LINE_STRIP для solid и wireframe режимов соответственно.
RenderWater, RenderSkyBox
Отвечают за вывод плоскостей земли и неба. Функция создаёт плоскость два раза больше ландшафта.
Разница между этими функциями только в используемых текстурах и высоте плоско-сти.
CreateSnow
Заполняет массив снежинок данными. В зависимости от позиции наблюдательность на расстоянии, не превышающем максимальную дистанцию снега, устанавливают «снежинки», имеющие различную высоту над поверхностью, скорость падения и раз-мер.
ProcessSnow
Обрабатывает массив частиц снежинок. Если частица выходит из поля зрения на-блюдателя, она возвращается в поле зрения с противоположной стороны. Если части-ца опускается ниже поверхности земли, то она удаляется и вместо неё создается новая частица в случайной позиции на высоте неба. Если частица находится в «свободном падении», ее высота уменьшается на величину скорости.
DrawSnowOne
Рисует снежинку. Происходит отрисовка плоскости снежинки, параллельно плоско-сти взгляда наблюдателя. В случае, если включено текстурирование снежинок, зада-ются текстурные координаты.
RenderSnow
Выводит снежинки на экран. Функция проходит по массиву снежинок, запуская для каждой отрисовку. Если включено текстурирование снежинок, то сначала отрисовыва-ется маска текстуры, а затем сама снежинка.
InitGL
Инициализует движок OpenGL. Включается текстурирование, устанавливается по-ложение мышки и скрывается курсор, устанавливается тест глубины, цвет фона, вклю-чается туман, добавляется источник света, вызываются функции загрузки текстур и карты высот, устанавливается позиция наблюдателя, включается музыка и создаётся массив частиц снега.
ReSizeGLScene
Изменяет пропорции окна. Сбрасывается текущая матрица перспективы и устанав-ливается новая.
DrawGLScene
Прорисовывает сцену. Сбрасывается текущая проекция и очищается экран.
Изначально обрабатывается движение мышки. После получения и использования сдвигов, они обнуляются, и мышь устанавливается в изначальную позицию.
Далее, происходит обработка позиции наблюдателя, в зависимости от его угла по-ворота yang и скоростей поступательного движения wspeed и стрейфа sspeed. Уста-навливаются координаты вектора взгляда наблюдателя по углам поворота xang и yang и текущей позиции наблюдателя. Эти значения передаются функции gluLookAt и позиционируют наблюдателя.
Основная часть функции отвечает за саму отрисовку сцены. Если не включено от-ражение, то просто отрисовывается сцена вызовом функций рендеринга. Если же от-ражение включено, то сначала отрисовывается перевернутая сцена в буфере шаблона, затем сцена отрисовывается заново с использованием буфера шаблона.
WndProc
Оконная функция Windows, обрабатывающая сообщения. Практически все сообще-ния обрабатываются стандартным образом.
Нажатия на клавиши и отпускание клавиш используют особый обработчик, устанав-ливающий в true/false элементы массива, соответствующие нажатым клавишам.
Обработчик события перемещения мыши задаёт две переменных — сдвиг мыши от изначальной позиции.
WinMain
Основная функция приложения Windows.
В главном цикле программы вызывается функция отрисовки сцены. Мы вызываем её вручную сами, не обрабатывая сообщение WM_PAINT, чтобы обеспечить правильную отрисовку динамической сцены.
Все нажатия клавиш отрабатываются ниже. Клавиши курсора отвечают за переме-щения объекта. При нажатии на них возрастает или убывает значение скорости отно-сительно одной из осей. При отпускании клавиш срабатывает эффект инерции — зна-чение скорости медленно приближаются к нулю.
При нажатии на клавиши W, S, T, R, M активируются или дезактивируются те или иные режимы рендеринга — отрисовка и текстовом снега, режим каркасного отображе-ния ландшафта, музыка и отражение от воды.
Последняя команда отвечает за синхронизацию вывода в 30 кадров в секунду.
Выход из цикла осуществляется при нажатии на клавишу Escape.