Красивый туман в Unity3d с высокой оптимизацией

В этом посте хотелось бы поделиться очень интересным способом реализации тумана на движке Unity, который прекрасно будет работать на мобильных устройствах. Ниже можно ознакомиться с конечным вариантом:

Красивый туман в Unity3d с высокой оптимизацией Unity3D, Gamedev, Туман, Мобильные игры, Разработка игр, Шейдеры, Indiedev, Fog, Длиннопост

Многие из вас сталкивались с подобными ситуациями, когда используется туман, чтобы сгладить объекты на дальней дистанции. Зачастую берется обычный туман в виде статического цвета близкий к цвету неба. Таким образом удаленные объекты плавно растворяются, тем самым скрывая резкий край уровня.


У этого подхода есть один большой минус - такой тип тумана подходит хорошо только тогда, когда небо у нас одного монотонного цвета, стоит добавить хоть малейший градиент - получишь некрасиво затуманенные объекты, которые очень сильно выделяются на заднем фоне. Например как здесь:

Красивый туман в Unity3d с высокой оптимизацией Unity3D, Gamedev, Туман, Мобильные игры, Разработка игр, Шейдеры, Indiedev, Fog, Длиннопост

Как мы видим, цвет тумана был взят с правой стороны, около солнца. Картинка справа имеет сглаженные края, однако слева можно заметить резкую линию, ибо скайбокс в том месте имеет совсем другой цвет.


Если попытаться поменять цвет тумана ближе к левой стороне, то получим тот же проблемный эффект, но уже с другой стороны:

Красивый туман в Unity3d с высокой оптимизацией Unity3D, Gamedev, Туман, Мобильные игры, Разработка игр, Шейдеры, Indiedev, Fog, Длиннопост

Как же правильно поступить в данной ситуации? Есть несколько вариантов, например вместо одного цвета тумана, можно использовать градиент. Решение интересное, но не практичное в применении, небо иногда может иметь засветы (например воронка от солнца), как результат, одним градиентом здесь не обойтись.


Есть множество вариантов опробовать глобальный туман, использующий post processing, но к сожалению, данный вариант дает очень сильную нагрузку на GPU, использовать его в мобильном проекте, где GPU в основном и является проблемным местом, не рационально. На выходе получится огромное падение fps и ускоренный нагрев устройства.


К счастью, удалось найти очень интересный пост на реддите, в котором человек делится своими мыслями относительно решения этой проблемы. Он предлает накладывать вместо монотонного цвета кубомапу, которая была бы копией скайбокса, но с сильно уменьшенным разрешением + небольшим замытием. Решение великолепное, но есть небольшая загвоздка, для этого нужно переписать все шейдеры в игре, которые должны иметь этот туман. К счастью, сделать это довольно просто.


Править будем vertex-fragment шейдер. Если вы используете Surface шейдеры, то придется сгенерировать vertex-fragment шейдер и вносить изменения уже в нем (можно конечно сделать правки в исходном Surface шейдере, но я не пробовал, т.к. сам использую vertex-fragment).


Ниже предоставлена полная версия шейдера с внесенными изменениями для работы тумана. Все что нужно добавить выделено жирным шрифтом. Осталось только аккуратно перенести это в свои шейдеры:

Shader "Mobile/CustomFogCube"
{
Properties
{
_FogStart("Fog Start", float) = 0 //объявляем наши новые переменные для тумана
_FogEnd ("Fog End", float) = 50

}
SubShader
{
Tags{ "RenderType" = "Opaque" }
Fog{ Mode off }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ LIGHTMAP_ON
#include "UnityCG.cginc"
half _FogStart;  //определяем новые переменные в рамках CGPROGRAM
half _FogEnd;

struct appdata
{
float4 vertex : POSITION;
float4 color : COLOR;
float4 uv : TEXCOORD1;
};
struct v2f
{
float4 pos : SV_POSITION;
float4 uv : TEXCOORD1;
half fog : TEXCOORD2;  //добавляем новую переменную для расчета расстояния отображения тумана и последующей передачи в fragment функцию
float4 color : COLOR;
half3 viewDir : TEXCOORD3;
};
v2f vert(appdata v)
{
v2f o;
o.color = v.color;
o.pos = UnityObjectToClipPos(v.vertex);
//lightmaps
o.uv.xy = v.uv.xy * unity_LightmapST.xy + unity_LightmapST.zw;
//fog высчитываем положение тумана в зависимости от заданных значений
half fogz = UnityObjectToViewPos(v.vertex).z;
o.fog = saturate((fogz + _FogStart) / (_FogStart - _FogEnd));

float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.viewDir = -(normalize(UnityWorldSpaceViewDir(worldPos)));
return o;
}
half4 frag(v2f i) : COLOR
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
fixed4 c = i.color * 0.5;
//lightmaps
#ifdef LIGHTMAP_ON
  fixed4 lm = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv.xy);
c.rgb *= lm.rgb * 4;
#endif
//fog заменяем плавно цвет поверхности на цвет кубомапы (он же наш туман). Кубомапу нужно задать в настройках освещение (Lighting > Scene > Environment Reflection > Source = Custom > Cubemap = Ваша кубомапа)
half4 fogCube = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, i.viewDir);
return lerp(c, fogCube, i.fog);

}
ENDCG
}
}
Fallback "Mobile/VertexLit"
}

Для работы шейдера нужно выставить кастомную кубомапу в настройках освещения. Просто проследуйте по следующему пути: Lighting > Scene > Environment Reflection > Source = Custom > Cubemap = Ваша кубомапа, которая дублирует ваш скайбокс, предварительно размазанная в каком-нибудь графическом редакторе, либо просто уменьшенная до размера 32х32 пикселей)


Собственно ещё раз покажу как данный туман выглядит в сцене:

Красивый туман в Unity3d с высокой оптимизацией Unity3D, Gamedev, Туман, Мобильные игры, Разработка игр, Шейдеры, Indiedev, Fog, Длиннопост

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


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

Лига Разработчиков Видеоигр

6.6K постов22.1K подписчиков

Добавить пост

Правила сообщества

ОБЩИЕ ПРАВИЛА:

- Уважайте чужой труд и используйте конструктивную критику

- Не занимайтесь саморекламой, пишите качественные и интересные посты

- Никакой политики


СТОИТ ПУБЛИКОВАТЬ:

- Посты о Вашей игре с историей её разработки и описанием полученного опыта

- Обучающие материалы, туториалы

- Интервью с опытными разработчиками

- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе

НЕ СТОИТ ПУБЛИКОВАТЬ:

- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры

- Посты, единственная цель которых - набор команды для разработки игры

- Посты, не относящиеся к тематике сообщества

Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.

ЗАПРЕЩЕНО:

- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции

- Выдавать чужой труд за свой

Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.


О РАЗМЕЩЕНИИ ССЫЛОК:

Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:

- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества

- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз

- Cсылка размещается в формате: "Страница игры в Steam: URL"