Красивый туман в Unity3d с высокой оптимизацией
В этом посте хотелось бы поделиться очень интересным способом реализации тумана на движке Unity, который прекрасно будет работать на мобильных устройствах. Ниже можно ознакомиться с конечным вариантом:
Многие из вас сталкивались с подобными ситуациями, когда используется туман, чтобы сгладить объекты на дальней дистанции. Зачастую берется обычный туман в виде статического цвета близкий к цвету неба. Таким образом удаленные объекты плавно растворяются, тем самым скрывая резкий край уровня.
У этого подхода есть один большой минус - такой тип тумана подходит хорошо только тогда, когда небо у нас одного монотонного цвета, стоит добавить хоть малейший градиент - получишь некрасиво затуманенные объекты, которые очень сильно выделяются на заднем фоне. Например как здесь:
Как мы видим, цвет тумана был взят с правой стороны, около солнца. Картинка справа имеет сглаженные края, однако слева можно заметить резкую линию, ибо скайбокс в том месте имеет совсем другой цвет.
Если попытаться поменять цвет тумана ближе к левой стороне, то получим тот же проблемный эффект, но уже с другой стороны:
Как же правильно поступить в данной ситуации? Есть несколько вариантов, например вместо одного цвета тумана, можно использовать градиент. Решение интересное, но не практичное в применении, небо иногда может иметь засветы (например воронка от солнца), как результат, одним градиентом здесь не обойтись.
Есть множество вариантов опробовать глобальный туман, использующий 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 пикселей)
Собственно ещё раз покажу как данный туман выглядит в сцене:
Оригинальный шейдер можно найти в данном пакете, которым поделился пользователь с реддита (различия от вышеуказанного только в том, что там кубомапу нужно выставлять непосредственно в параметрах материала, выше же кубомапа будет браться с настроек освещения, таким образом не нужно будет в каждом материале выставлять кубомапы). В этом же пакете есть тестовая сцена, все необходимые материалы, чтобы можно было поиграться с данным эффектом.
В целом это все. В принципе отредактировать шейдеры достаточно просто, так что, если вас так же как и меня волнует вопрос реализации красивого тумана, думаю это решение будет одним из наиболее лучших для использования в мобильном проекте, ибо тут высокая оптимизация и картинка получается очень красивой.
Лига Разработчиков Видеоигр
6.6K постов22.1K подписчиков
Правила сообщества
ОБЩИЕ ПРАВИЛА:
- Уважайте чужой труд и используйте конструктивную критику
- Не занимайтесь саморекламой, пишите качественные и интересные посты
- Никакой политики
СТОИТ ПУБЛИКОВАТЬ:
- Посты о Вашей игре с историей её разработки и описанием полученного опыта
- Обучающие материалы, туториалы
- Интервью с опытными разработчиками
- Анонсы бесплатных мероприятий для разработчиков и истории их посещения;
- Ваши работы, если Вы художник/композитор и хотите поделиться ими на безвозмездной основе
НЕ СТОИТ ПУБЛИКОВАТЬ:
- Посты, содержащие только вопрос или просьбу помочь
- Посты, содержащие только идею игры
- Посты, единственная цель которых - набор команды для разработки игры
- Посты, не относящиеся к тематике сообщества
Подобные посты по решению администрации могут быть перемещены из сообщества в общую ленту.
ЗАПРЕЩЕНО:
- Публиковать бессодержательные посты с рекламой Вашего проекта (см. следующий пункт), а также все прочие посты, содержащие рекламу/рекламные интеграции
- Выдавать чужой труд за свой
Подобные посты будут перемещены из сообщества в общую ленту, а их авторы по решению администрации могут быть внесены в игнор-лист сообщества.
О РАЗМЕЩЕНИИ ССЫЛОК:
Ссылка на сторонний ресурс, связанный с игрой, допускается только при следующих условиях:
- Пост должен быть содержательным и интересным для пользователей, нести пользу для сообщества
- Ссылка должна размещаться непосредственно в начале или конце поста и только один раз
- Cсылка размещается в формате: "Страница игры в Steam: URL"