Создание ландшафта

Xtreme3D

Урок 15
Создание ландшафта

Уровень: средний
Версия Xtreme3D: 3.0.x
Автор урока: Gecko

Ландшафт (Terrain) является важной составляющей игр многих жанров, моделирующих ситуации реального мира - это гонки, стратегии, многие шутеры и различные игры с открытым миром. Обычно ландшафт не моделируется вручную, а генерируется из так называемой карты высот - изображения, где темные участки означают понижение высоты, а светлые - повышение. Генерирование ландшафта может происходить как в программе 3D-моделирования, так и в самой игре - в последнем случае есть возможность оптимизировать рендеринг ландшафта, динамически изменяя его детализацию в зависимости от удаленности от камеры (динамический LOD). В Xtreme3D также имеется поддержка такой технологии.
Чтобы отрисовать ландшафт, сначала необходимо загрузить карту высот, в терминологии Xtreme3D - HDS (Height Data Source, источник данных о высоте):

hds = BmpHDSCreate('heightmap.bmp');
BmpHDSSetInfiniteWarp(hds, 0);

Функцией BmpHDSSetInfiniteWarp можно сделать карту высот бесконечно зацикленной во все четыре стороны - очень удобно, если вы хотите сделать безграничный мир.

Теперь создаем ландшафт - объект Terrain:

terrain = TerrainCreate(global.scene);
TerrainSetHeightData(terrain, hds);
TerrainSetTileSize(terrain, 32);
TerrainSetTilesPerTexture(terrain, 8);
TerrainSetQualityDistance(terrain, 100);
TerrainSetQualityStyle(terrain, hrsFullGeometry);
TerrainSetMaxCLodTriangles(terrain, 10000);
TerrainSetCLodPrecision(terrain, 50);
TerrainSetOcclusionFrameSkip(terrain, 0);
TerrainSetOcclusionTesselate(terrain, totTesselateIfVisible);

Если запустить игру на данном этапе, то ландшафт, скорее всего, будет слишком высоким и повернутым на 90 градусов. Это нетрудно исправить, установив желаемый масштаб по оси Z и повернув объект по оси X:

ObjectSetScale(terrain, 1, 1, 0.1);
ObjectRotate(terrain, 90, 0, 0);

Отдельного слова заслуживает наложение текстуры на ландшафт. Это можно сделать разными способами, я же предлагаю следующий: первая текстура материала (диффузная) будет натянута на весь ландшафт, а вторая (текстура детализации) будет многократно повторяться с необходимым масштабом, накладываясь на первую в режиме modulate (то есть, изменяя яркость предыдущей). Таким образом, возникнет иллюзия того, что ландшафт использует огромную детализированную текстуру.

MaterialCreate('mTerrain', 'terrain-diffuse.jpg');
MaterialSetOptions('mTerrain', false, true);
MaterialCreate('detmap', 'terrain-detail.jpg');
MaterialSetTextureScale('detmap', 100, 100);
MaterialSetSecondTexture('mTerrain', 'detmap');
ObjectSetMaterial(terrain, 'mTerrain');

Обратите внимание, что мы отключаем освещение для материала ландшафта - дело в том, что динамический LOD не позволяет задавать нормали для вершин (поскольку наборы вершин постоянно меняются), что необходимо для корректного освещения полигонов. Поэтому для ландшафта следует использовать статическое освещение - карту освещенности, объединенную с диффузной текстурой.

Осталась еще одна задача: перемещение персонажа по ландшафту. Это нетрудно сделать при помощи специально предусмотренной функции - TerrainGetHeightAtObjectPosition, которая возвращает высоту земли в точке, совпадающей с абсолютной позицией объекта:

ObjectSetPositionY(camPos, TerrainGetHeightAtObjectPosition(terrain, camPos) + 1);