Готовый перевод Ogre3D Basic Tutorials / Базовые туториалы Ogre3D: Basic Tutorial 3: Terrain, Sky, and Fog
ВВЕДЕНИЕ
В этом уроке мы рассмотрим, как работать с ландшафтом, небом и туманом в ваших приложениях Ogre.
После этой статьи вы поймете разницу между Skybox, Skyplane и Skydome, и научитесь пользоваться ими.
Также вы узнаете разницу между типами туманов и сможете их использовать.
С ЧЕГО НАЧАТЬ
В предыдущих версиях Ogre вы должны были использовать <b>менеджер сцены Terrain</b>, если вы хотели добавить поддержку ландшафта в ваши приложения Ogre.
Теперь в этом нет необходимости, поскольку появилась новая <b>система ландшафта Ogre</b>.
Начиная с Ogre 1.7 (Cthugha) нам стали доступными три компонента: Terrain, Paging и Property.
Ogre Terrain использует компонент Paging для динамической подгрузки ландшафта.
Этот урок научит основам использования компонента Ogre Terrain, подкачку оставим для последующих уроков.
КОД
Измените заголовочный файл вашего класса BaseTurtorial3 таким образом:
Убедитесь что ваш .cpp файл выглядит так (до функции main()):
ПАРАМЕТРЫ ЛИНКОВЩИКА
Для того чтобы мы могли скомпилировать наш код, вам необходимо прилинковать библиотеку компонента Terrain.
Добавьте ссылку на библиотеку 'OgreTerrain.lib' для Release конфигурации вашего проекта, и 'OgreTerrain_d.lib' для Debug версии соответственно. (В Code::Blocks это правый клик по проекту -> Build options -> вкладка Linker settings -> кнопка Add).
Чтобы сделать это, щелкните правой кнопкой мыши по проекту BasicTutorial3 в окне обозревателя решений (Solution Window).
Выберите пункт Свойства (Properties), появится окно Configuration manager, выберите конфигурацию из выпадающего меню слева вверху, затем перейдите по пути Configuration properties -> Linker -> Input и добавьте ссылку на файл.
Если вы используете систему сборки CMake, добавьте следующий код в файл CMakeLists.txt после строки "find_package(OGRE REQUIRED)", чтобы система сборки CMake знала о зависимости:
Если вы используете систему сборки autotools, следуйте инструкциям в документе "Настройка приложения при помощи autotools" ("Setting Up An Application with the autotools") для создания файла конфигурации configure.ac, включающего компонент Terrain.
После выполнения этих инструкций, файл должен выглядеть как-то так:
ЛАНДШАФТ
Проект, которые мы будем делать в этой части руководства содержит два основных компонента: классы TerrainGroup и Terrain.
Еще есть класс <b>TerrainGlobalOptions</b>, но он используется как вспомогательный компонент.
Класс TerrainGroup представляет собой небольшой кусок земли типа поляны, луга или игрового поля.
Внутри себя он может содержать несколько частей ландшафта, представленных классом Terrain. Это подразделение используется для отрисовки LOD'ов в зависимости от расстояния от камеры до каждой части ландшафта.
В свою очередь, объект Terrain состоит из плитки полигонов (тайлов) с привязанным к ней материалом.
Мы будем использовать один объект TerrainGroup без использования страничной подкачки; использование страничного ландшафта будет описано в продвинутых уроках.
Перед собственно отрисовкой ландшафта мы должны установить все необходимые параметры <b>системы ландшафта Ogre</b>.
РАБОТА С КАМЕРОЙ
Во-первых, давайте изменим наш объект камеры для лучшей визуализации ландшафта.
Добавьте этот код в функцию createScene класса BasicTutorial3 (BasicTutorial3::createScene)
Помимо установки позиции и ориентации камеры, этот код также устанавливает расстояния до ближней и дальней плоскостей отсечения.
Ландшафты обычно имеют большой размер, и мы хотим чтобы наша камера могла видеть достаточно далеко.
Если система визуализации поддерживает такую возможность, сделаем расстояние до дальней плоскости отсечения бесконечным.
НАСТРОЙКА НАПРАВЛЕННОГО И ОКРУЖАЮЩЕГО ОСВЕЩЕНИЯ
Компонент Terrain использует направленный свет для расчета собственной карты освещения, поэтому помещаем на сцену источник такого освещения:
Также устанавливаем некоторый уровень окружающего освещения чтобы сделать переходы между границами света и тени более размытыми.
НАСТРАИВАЕМ НАШ ЛАНДШАФТ
Сперва мы создадим новый набор глобальных свойств для объекта Terrain. Для этой цели используется класс TerrainGlobalOptions.
Этот класс просто содержит настройки по умолчанию для всех объектов ландшафта, которые мы будем создавать, а также предоставляет функции для получения и установки этих настроек.
Также для каждого объекта TerrainGroup существует локальная настройка DefaultImportSettings, которую мы рассмотрим далее.
Затем создадим объект TerrainGroup – вспомогательный класс, обеспечивающий управление сеткой ландшафта, но не выполняющий динамической подгрузки (которую берет на себя компонент Paging, но его мы рассмотрим в продвинутых уроках).
Конструктор TerrainGroup в качестве параметров принимает ссылку на экземпляр SceneManager, параметр выравнивания (в какой плоскости будет находиться ландшафт), количество вершин по стороне одного куска ландшафта и размер этого куска в мировых координатах.
Затем мы указываем TerrainGroup, какое имя мы желаем использовать при сохранении нашего ландшафта, используя функцию setFilenameConvention.
И напоследок указываем начало координат внутри данной группы ландшафта.
Пришло время настройки нашего ландшафта:
С функцией configureTerrainDefaults мы познакомимся позже.
Обратите внимание, что в качестве параметра она принимает наш источник направленного освещения.
Теперь определяем все куски ландшафта и приказываем TerrainGroup загрузить их:
Так как мы создаем только один ландшафт, мы вызываем функцию defineTerrain один раз.
Но если бы мы захотели создать несколько кусков ландшафта, мы сделали бы это здесь.
Более подробно с функцией defineTerrain мы познакомимся позднее.
Если мы впервые импортировали куски ландшафта, нам необходимо рассчитать карты смешивания:
Этот цикл перебирает все доступные нам ландшафты и у каждого вызывает функцию initBlendMaps.
Описание этой функции будет позднее.
Теперь все, что осталось сделать – это подчистить за собой мусор, оставшийся после начального создания ландшафта:
ВИД ФУНКЦИИ createScene В ДАННЫЙ МОМЕНТ
В данный момент функция createScene() нашего класса BasicTutorial3 выглядит так:
Не пытайтесь скомпилировать программу сейчас - она не будет работать, поскольку мы ещё не реализовали несколько служебных функций.
Функция configureTerrainDefaults
Компонент Ogre Terrain довольно хорошо настраивается.
mTerrainGlobals - это экземпляр класса Ogre::TerrainGlobalOptions.
Найдите функцию configureTerrainDefaults и добавьте следующий код:
Во-первых, мы определили две глобальные опции: MaxPixelError и CompositeMapDistance.
MaxPixelError определяет, насколько точным будет наш ландшафт.
Меньшее значение этого параметра будет означать более аккуратный ландшафт за счет большей нагрузки на компьютер (из-за увеличения количества полигонов)
CompositeMapDistance определяет, на сколько далеко ландшафт будет отрисовываться с наложенной картой освещения.
Далее, давайте разберемся с картой освещения, используя наш источник направленного освещения:
Здесь мы используем наш источник света для установки направления и цвета рассеянного освещения, в то время как цвет окружающего освещения берется из параметров менеджера сцены.
Далее мы определяем некоторые значения ImportData:
В этом уроке мы не будем останавливаться на том, что это за значения и какими они могут быть, но отметим, что параметры terrainSize и worldSize установлены таким образом, что они совпадают с нашими глобальными размерами (которые мы задали в конструкторе TerrainGroup), а параметр inputScale определяет степень увеличения (масштаба) изображения карты высот.
Здесь мы используем масштабирование, поскольку изображения имеют ограниченную точность представления (замечание: вы можете использовать карты высот с необработанными значениями с плавающей точкой для того, чтобы избежать использования параметра inputScale, но эти карты обычно используют сжатие с потерями. В этом случае вы можете установить параметр inputScale равным 1).
Карту высот, например, с необработанными значениями, обычно не нужно масштабировать, поскольку ее значения сохранены в виде массива неотмасштабированных чисел с плавающей точкой.
Последнее – текстуры:
Здесь мы устанавливаем количество текстурных слоев ландшафта равным 3 с помощью вызова defaultimp.layerList.resize(3).
Потом мы инициализируем каждый слой, устанавливая параметр 'worldSize' и определяя имена текстур.
Параметр 'worldSize' определяет насколько большой будет каждая текстура при мультитекстурировании ландшафта(сплаттинге).
Меньшие значения будут увеличивать разрешение отрисованных текстурных слоев.
Генератор материала по-умолчанию берет по две текстуры на каждый слой:
1. diffuse_specular – текстура рассеянного освещения с картой бликов в альфа-канале.
2. normal_height – карта нормалей с картой высот в альфа-канале.
Вся наша функция configureTerrainDefaults выглядит так:
А это наша функция defineTerrain:
Это функция простая, но предельно ясная:
Сначала она узнает у объекта TerrainGroup, какое имя файла тот использует при генерации ландшафта.
Во-вторых, она проверяет, есть ли файл с таким именем в нашей группе ресурсов.
И если он там есть, значит мы уже сгенерировали двоичный файл данных с ландшафтом и таким образом нет необходимости импортировать его из изображения.
Если же его там нет, это означает, что мы должны сгенерировать ландшафт, а для этого загрузить изображение и использовать его для определения ландшафта.
Функция использует маленькую вспомогательную функцию, называемую getTerrainImage().
Поскольку эта функция действительно мала и используется только один раз, она определена в файле реализации как статическая локальная функция:
Она загружает картинку 'terrain.png' и места, где расположены ресурсы, отражая ее, если в этом есть необходимость.
Замечание: отражение необходимо для имитации бесшовного ландшафта, так что вы можете сделать неограниченный по размерам ландшафт, используя всего одну карту высот размером 513x513, это просто трюк.
Если карты высот вашего ландшафта уже бесшовные, вы не должны их отражать, просто определите собственные карты высот для каждого куска ландшафта.
В этом уроке мы используем объект TerrainGroup размером 1x1 (посмотрите на функцию createScene()), так что на самом деле этот код не используется.
Вы помните три типа слоев ландшафта, определенных в configureTerrainDefaults()?
Теперь мы будем смешивать их на основе ВЫСОТЫ тайла.
В настоящем проекте вы можете использовать альфакарту сплаттинга, хранящуюся в каналах RGBA в файле или файлах, которые отделены от карты высот.
Это функция initBlendMaps во всей своей полноте:
В этом уроке мы не будем углубляться в подробности того, как она работает.
Достаточно знать то, что она использует высоту ландшафта для сплаттинга трех его слоев.
Также обратите внимание на использование методов getLayerBlendMap() и getBlendPointer().
КОМПИЛЯЦИЯ И ЗАПУСК - ЧАСТЬ ПЕРВАЯ
Пойдем дальше – скомпилируем и запустим приложение.
Вы должны получить неплохо выглядящий ландшафт.
Однако, вы можете улучшить ваше маленькое приложение, сделав три добавления к нему:
1. Индикатор генерации ландшафта
2. Сохранение ландшафта
3. Очистка за собой
Во-первых нам нужно добавить новый член данных в класс BasicTutorial3:
Нам нужно переопределить три функции в нашем классе BasicTutorial3:
1. frameRenderingQueued – для показа процесса генерации ландшафта и сохранения сгенерированной версии
2. createFrameListener – для создания метки с информацией
3. destroyScene – для подчистки за собой
Итак, без лишних слов:
Во-первых мы спрашиваем наш TerrainGroup, работает ли он над генерацией ландшафта: isDerivedDataUpdateInProgress.
Если работает, давайте покажем информационную метку с правильной подписью.
А если нет – проверим, импортировали ли мы новый ландшафт.
Если да, сохраним все куски ландшафта, чтобы в следующий раз при запуске нашего приложения пропустить процесс генерации.
Мы переопределяем эту функцию, поскольку нам нужно добавить новую информационную метку к нашему объекту mTrayMgr класса OgreBites::SdkTrayManager.
Так как BaseApplication конструирует его в функции createFrameListener, мы не можем добавить ее в нашу функцию 'createScene', поскольку функция createFrameListener вызывается 'после' функции createScene...
Очень просто:
Вызовем метод BaseApplication::createFrameListener, а затем создадим нашу информационную метку.
Здесь мы подчищаем мусор после себя:
OGRE_NEW требует использования OGRE_DELETE.
КОМПИЛЯЦИЯ И ЗАПУСК - ЧАСТЬ ВТОРАЯ
Если вы сейчас скомпилируете и запустите приложение, вы должны будете увидеть информационную метку, сообщающую вам, что TerrainGroup занята генерацией ландшафта.
Когда метка исчезнет, TerrainGroup сохранит этот ландшафт.
Вы, возможно, обратите внимание, что вы не можете выйти из программы во время процесса генерации ландшафта.
Однако, если вы запустите программу во второй раз, вы не получите сообщения вовсе и процесс запуска пройдет намного быстрее, поскольку мы напрямую загружаем уже сгенерированный файл с ландшафтом.
Теперь перейдите в папку OGRE_HOME/media. Вы должны увидеть здесь файл 'BasicTutorial3Terrain_00000000.dat'...
ВВЕДЕНИЕ В СТРАНИЧНЫЙ ЛАНДШАФТ (этот параграф будет перемещен в продвинутые уроки)
У загрузки страницы на данный момент две стадии:
1. Подготовка(обычно асинхронная)
2. Загрузка (должна быть синхронизированной и вызываться из главного потока)
Для простоты в этом уроке будет использоваться только второй шаг.
Чтобы использовать страничную подкачку, рекомендуется подготовить страницы ландшафта (как рассмотренный выше 'BasicTutorial3Terrain_00000000.dat') заранее.
Вы можете использовать вызов mTerrainGroup->saveTerrains(true) или инструмент Ogitor для этой задачи.
Добавте этот код в секцию private класса BasicTutorial3:
Добавьте этот код к функции createScene():
Нашей целью является создание PagedWorld, которую будет использовать менеджер страниц, используя PageManager и TerrainPaging.
Система страничной подкачки Ogre - это гораздо большее, чем просто работа с ландшафтом.
Страница может содержать не только Ландшафт, но и другие объекты.
Страничная подкачка ландшафта работает поверх более общей страничной системы и DummyPageProvider гарантирует, что ядро системы всегда думает, что загружает страницы, поэтому страницы кусков ландшафта обрабатываются таким же образом.
НЕБО
Ogre предоставляет три различных типа неба: SkyBoxes, SkyDomes, и SkyPlanes.
Мы подробно рассмотрим каждый из них.
SKYBOXES
SkyBox – обычно это гигантский куб, который окружает все объекты в сцене.
Лучший способ описать его - просто показать его вам.
Добавьте эту строку кода в метод createScene:
Скомпилируйте и запустите программу. Уловили?
(Заметим, что SkyBox зернистый, потому что используемая текстура низкого разрешения; SkyBox высокого разрешения выглядит намного лучше. )
Имеется несколько полезных параметров, которые можно установить SkyBox-ам при вызове setSkyBox.
Первый – показывать ли SkyBox.
Если позже вам потребуется отключить SkyBox, просто вызовите 'mSceneMgr->setSkyBox(false, "");'
Второй параметр – скрипт материала, используемого SkyBox-ом.
Третий и четвертый параметры setSkyBox весьма важные для понимания работы неба.
Третий параметр устанавливает расстояние, на которое будут отдалены стенки куба от камеры, а четвертый указывает, должен ли SkyBox рисоваться до отрисовки сцены или после.
Итак, давайте посмотрим, что получится, когда вы смените расстояние до SkyBox со значения по умолчанию в 5000 единиц на что-то более близкое:
Ничего не изменилось! Это потому, что четвертый параметр, обеспечивающий отрисовку SkyBox до сцены, по умолчанию установлен в true.
Если SkyBox рисуется первым, а затем что-то еще отрисовывается после него (вроде нашего ландшафта), оно будет нарисовано поверх куба, таким образом получается, что SkyBox всегда появляется на фоне.
(Обратите внимание, что вы не должны устанавливать расстояние до SkyBox-а ближе, чем ближняя плоскость отсечения камеры, иначе вы ожидаемо не увидете SkyBox!)
Не всегда требуется рисовать SkyBox первым, потому что он будет отрисовываться целиком.
Когда вы рисуете его последним, рисуются только видимые части, что дает небольшой прирост производительности.
Поэтому давайте попробуем сделать так, чтобы наш SkyBox рисовался последним:
Все выглядит как и прежде, но теперь те части SkyBox-а, которые невидимы, не отрисовываются.
Однако есть одна вещь, заставляющая вас осторожно использовать эту технику.
Если вы установите SkyBox слишком близко к камере, вы можете обрезать часть геометрии сцены. К примеру, попробуйте это:
Как вы теперь можете видеть, ландшафт "прорезается" сквозь SkyBox.
Определенно не то, что мы хотели. Если вы используете SkyBox-ы в своем приложении, вы должны решить, как вы будите их использовать.
Ускорение, которое вы получаете от отрисовки SkyBox-а после ландшафта, очень невелико, и вы должны быть осторожны, чтобы он не загораживал вашу геометрию (если вы не для этого его делаете).
Вообще говоря, оставить все параметры, после второго, по умолчанию – очень безопасный выбор.
SKYDOME-Ы (НЕБОСВОДЫ)
SkyDome-ы очень похожи на SkyBoxe-ы, и вы можете использовать их, вызвав <b>setSkyDome</b>.
Точно так же создается и отрисовывается гигантский куб вокруг камеры, но огромное различие в том, что текстура "проектируется" на SkyBox сферическим способом.
И хотя вы глядите на куб, выглядит он так, как будто текстура обернута вокруг поверхности сферы.
Главный недостаток этого метода состоит в том, что нижняя часть куба будет нетекстурированной, поэтому вам всегда нужно иметь некоторый ландшафт(земная поверхность), который бы скрывал основание.
Пример текстуры, которую Ogre предоставляет для SkyDome-ов, позволит вам ясно это увидеть.
Уберите вызов setSkyBox из createScene и добавьте вместо него следующий код:
Когда вы запустите пример, переместите камеру в мертвую точку (?) ландшафта как можно ближе к поверхности (так выглядит лучше).
После занятия позиции нажмите клавишу R, чтобы переключится в представление сетки.
Как видите, мы все же смотрим на куб (без основания), но выглядит он так, как если бы облака были обернуты вокруг верхней половины сферы.
(Также отметим, что перемещение облаков – свойство материала "Examples/CloudySky", а вовсе не SkyDome-а.)
Первые два параметра setSkyDome те же, что и у setSkyBox, у вы можете выключить SkyDome вызвав 'mSceneMgr->setSkyDome(false, "");'.
Третий параметр - это значение кривизны используемое для SkyDome.
Руководство по API <b>предлагает</b> использовать значения между 2 и 65; меньшие значения для лучшего эффекта расстояния, большие – для меньших искажений и лучшего сглаживания.
Попытайтесь установить третий параметр в 2 и в 65 и посмотрите на различие.
Эффект расстояния, на который ссылалось руководство по API, ясно виден на этих скриншотах.
Здесь кривизна равна 2:
Здесь кривизна равна 64:
Четвертый параметр – количество повторов текстуры, которое вы должны будите настроить в зависимости от размера вашей текстуры.
Обратите внимание, что этот параметр не целочисленный, а значение типа Real (c плавающей точкой). На <b>задолба!ли</b> и так много историй.
Вы можете повторить ее 1.234 раз, если так она выглядит хорошо в вашем приложении.
Пятый и шестой параметры – расстояние и флаг рисования небосвода первым, соответственно; их мы уже описали в разделе, посвященном SkyBox-у.
SKYPLANE-Ы (НЕБЕСНЫЕ ПЛОСКОСТИ)
SkyPlane-ы очень отличаются от SkyBoxe-ов и SkyDome-ов.
Вместо куба на котором рендеряться текстуры неба, мы просто используем одну плоскость.
(отметьте, для всех следующих конфигураций SkyPlane вам нужно быть повернутым куда-то по направлению к середине ландшафта близко к земле.)
Уберите весь код SkyDome из createScene. Первая вещь, которую мы собираемся сделать – создать плоскость и повернуть ее лицом вниз.
У метода setSkyPlane , который мы будем вызывать, нет параметра distance как у SkyBox и SkyDome.
Вместо этого, этот параметр установлен в переменной d плоскости:
Теперь, имея определение плоскости, мы можем создать SkyPlane.
Обратите внимание, что четвертый параметр – размер SkyPlane (в данном случае 1500x1500 единиц), а пятый параметр указывает количество повторов текстуры:
Скомпилируйте и запустите программу. Есть две проблемы, которые создает использование SkyPlane в данном примере.
Во-первых, используемая текстура низкого разрешения, поэтому она размножается (тайлится) неважно.
Это можно исправить, просто создав хорошую текстуру высокого разрешения для неба, которая будет тайлится хорошо.
Однако, основная проблема с этой методикой состоит в том, что если вы взгляните на горизонт, вы увидите, где оканчивается SkyPlane.
И даже если у вас есть хорошая текстура, она совсем не станет выглядеть лучше если вы будете смотреть на горизонт.
В основном использование SkyPlane оправдано, только когда у вас есть высокие стены (или горы) вокруг точки обзора.
Использование SkyPlane в такой ситуации значительно менее графически накладно, чем создание полного SkyBox/SkyDome.
К счастью, это не единственное, что мы можем сделать с нашим SkyPlane.
Шестой параметр setSkyPlane – уже знакомый нам параметр "рисовать первым", который мы уже обсудили в разделах, посвященным SkyBox и SkyDome.
Седьмой параметр позволяет определить кривизну SkyPlane, т.е. вместо плоскости мы будем использовать кривую поверхность.
Мы также должны теперь определить количество сегментов по осям x и y, используемых для создания SkyPlane (первоначально это один большой квадрат, но искривляя его, мы вынуждены разбить плоскость на маленькие квадратики).
Восьмой и девятый параметр этой функции – число сегментов по x и y соответственно:
Скомпилируйте и запустите приложение. Теперь наша SkyPlane выглядит намного лучше, хотя над тайлингом все еще нужно поработать.
Но кроме этого вы можете использовать ее с материалом облаков:
Скомпилируйте и запустите приложение. Движение облаков и способ их повтора, кажется, заставляет выглядеть их хуже, чем на SkyDome-е, особенно когда вы приближаетесь близко к краю ландшафта и глядите на горизонт.
Еще одно замечание, вы можете отчистить SkyPlane вызвав 'mSceneMgr->setSkyPlane(false, Ogre::Plane(), "");'
ЧТО ИСПОЛЬЗОВАТЬ?
Какое небо использовать, целиком зависит от вашего приложения.
Если вам нужно видеть все вокруг себя, даже в отрицательном направлении оси y (<i>напомним, вверх направлена ось y, а не z – прим. пер.</i>), тогда у вас остается только один реальный выбор – использование SkyBox.
Если у вас есть ландшафт или какой-то вид пола, блокирующий обзор в отрицательном направлении оси y, то использование SkyDome, кажется, дает наиболее реалистичные результаты.
Для областей, где вы не можете видеть горизонт (такие, как долина, окруженная горами со всех сторон, или внутренний двор замка) SkyPlane даст очень хорошо выглядящие результаты при минимальной нагрузке на GPU.
Основная причина использования SkyPlane, как мы увидим в следующем разделе – ее использование отлично сочетается с эффектами тумана.
Это только указания. В вашем приложении вам следует экспериментировать и использовать то, что выглядит лучше всего.
ТУМАН
ВВЕДЕНИЕ В ТУМАН
Наиболее важная вещь, которую нужно знать о настройке тумана, это то, что в действительности не создается сущности тумана в пустом пространстве, как вы могли бы предположить.
Напротив, это просто фильтр применяемый на все объекты, на которые вы сейчас смотрите.
Это имеет некоторые интересные последствия, наиболее существенным из которых будет то, что когда вы смотрите в никуда (т.е. когда вы не смотрите на объект), вы не видите туман.
На самом деле вы увидите всего лишь цвет фона области просмотра.
Поэтому, для того, чтобы туман выглядел правильно, вам нужно установить цвет фона соответствующим текущему цвету тумана.
Существуют два основных типа тумана: линейный и экспоненциальный.
Линейный туман сгущается по линейному закону, в то время как экспоненциальный сгущается экспоненциально (на каждую единицу расстояния плотность тумана увеличивается больше, чем за предыдущую единицу).
Легче увидеть разницу, чем объяснить ее, поэтому перейдем к примерам.
ТИПЫ ТУМАНОВ
Первым типом тумана, который мы рассмотрим, будет линейный, это самая легкая для понимания разновидность тумана.
Первой вещью, которую мы сделаем после вызова setWorldGeometry – установим фоновый цвет области просмотра.
Мы могли бы сделать это, переопределив функцию createViewport (как мы сделали в последнем уроке), но иногда нам нужно установить его без пересоздания области просмотра каждый раз.
Поэтому мы сделаем вот так:
Вы можете использовать функцию-член getNumViewports для получения количества областей просмотра и итерации по ним, если вы имеете более одной области просмотра, но так как это редко имеет место (и так как мы знаем, что у нас есть только одна область просмотра), мы можем просто напрямую получить ее.
Как только мы установим цвет фона, мы можем создать туман:
Первый параметр setFog – тип тумана (в данном случае линейный).
Второй параметр setFog – цвет тумана, который мы используем (в данном случае очень очень светлый серый или "WhiteSmoke" для C#).
Третий параметр не используется линейным туманом. Четвертый и пятый парамтеры определяют диапазон, в котором туман будет сгущаться.
В данном случае мы установили начальную позицию тумана в 50 единиц, а конечную – в 500 единиц.
Это означает, что в диапазоне от 0 до 50 перед камерой нет тумана. От 50 до 500 единиц туман сгущается по линейному закону.
На расстоянии 500 единиц от камеры вы не сможете увидеть ничего, кроме тумана. Скомпилируйте и запустите приложение.
Другим типом тумана, который мы можем использовать, является экспоненциальный туман. Вместо установки начальной и конечной границ для тумана, мы устанавливаем его плотность (четвертый и пятый параметры не используются). Замените предыдущий вызов setFog этим:
Скомпилируйте и запустите приложение.
В этом случае туман выглядит по другому.
Существует также другая экспоненциальная функция тумана, которая жестче первой (т.е. туман становится намного плотнее с каждой единицей расстояния от камеры, чем при использовании первой функции).
Обратите внимание, что видимая плотность тумана возрастает еще быстрее при использовании FOG_EXP2.
Замените предыдущий вызов setFog этим:
Скомпилируйте и запустите приложение снова.
Туман, по большей части, взаимозаменяем между этими тремя предоставляемыми Ogre функциями (<i>следует заметить, что здесь под функциями подразумевавется не функция установки тумана setFog – она одна, а функции расчета плотности тумана – FOG_LINEAR, FOG_EXP и FOG_EXP2 – прим. пер.</i>).
Вам стоит поэкспериментировать со всеми тремя функциями тумана, и посмотреть, при какой туман будет выглядеть наилучшим образом в вашем приложении.
ТУМАН И НЕБО
Вы можете столкнуться с некоторыми интересными проблемами, когда будете пытаться использовать туман с SkyBox и SkyDome.
Так как SkyDome-ы и SkyBoxe-ы – просто кубы, использование их с туманом проблематично, так как туман работает сферическим образом.
Удалите содержимое метода createScene.
Если мы разумно выберем наш SkyDome и параметры тумана, мы сможем непосредственно увидеть проблему:
Скомпилируйте и запустите приложение.
Если вы подвигаете камерой, вы увидите, что различные части SkyDome протыкают туман в зависимости от того, на какую часть SkyDome-а вы смотрите (обратите внимание на синий цвет, проступающий на сторонах, но не в середине):
Это, конечно, не то, что мы хотим. Другой вариант – использовать SkyPlane.
Замените код в createScene этим:
Теперь выглядит нормально.
Если мы взглянем вверх, мы сможем увидеть небо (которое видно в реальной жизни, если туман действительно правильный), а не протыкает его забавными способами.
Независимо от того, используете ли вы искривление или нет, это решает нашу проблему возможности пользователя увидеть горизонт, где SkyPlane не выглядит правильным.
Есть возможность заставить туман не затрагивать небо полностью, но для этого потребуется изменить скрипт материала для текстуры неба.
Это выходит за рамки данного урока, но на будущее, <b>этот</b> параметр отключает туман для материала.
ТУМАН КАК ТЬМА
Вы не можете использовать небо вообще, когда вы устанавливаете туман, потому что если туман достаточно плотный, вы в любом случае не увидите неба.
Трюк с туманом, который мы описали выше, это довольно изящный прием, который может быть полезен в некоторых случаях.
Вместо установки туману яркого цвета укажите очень темный и увидите, что получится (обратите внимание, что мы установили расстояние до SkyPlane всего в 10 единиц, что ближе, чем начало тумана).
Скомпилируйте и запустите приложение. Вот то, что мы получим:
Не так уж ужасно. Конечно, как только вы вы сможете, вы должны использовать освещение вместо этого хака, но он показывает как гибко можно настроить туман, и некоторые из интересных вещей, которые вы можете сделать с движком.
Использование черного тумана также может быть интересным путем создания эффектов заклинаний "слепота" или "тьма", если вы пишете игру c видом от первого лица.
ВЫВОД
Теперь у вас должно сложиться базовое понимание ландшафта (Terrain), неба (Sky) и тумана (Fog).
Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется
Переведено на Нотабеноиде
http://notabenoid.org/book/19615/64002
Переводчики: Mingun, wuffur, alexx_x, proVIDec
ghostintheshell
Настройки
Готово:
