UnityShader
December 17, 2020

Туман для мобильных устройств с высокой производительностью

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

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

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

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

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

Есть множество вариантов опробовать глобальный туман, использующий 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 multicompile _ LIGHTMAPON #include "UnityCG.cginc" half _FogStart; //определяем новые переменные в рамках CGPROGRAMhalf _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 * unityLightmapST.xy + unityLightmapST.zw; //fog высчитываем положение тумана в зависимости от заданных значений half fogz = UnityObjectToViewPos(v.vertex).z;o.fog = saturate((fogz + FogStart) / (FogStart - _FogEnd)); float3 worldPos = mul(unityObjectToWorld, v.vertex).xyz; o.viewDir = -(normalize(UnityWorldSpaceViewDir(worldPos))); return o; } half4 frag(v2f i) : COLOR { UNITYSETUP_SSTEREOE_INDINDEX_VERTEVERTEX(i); fixed4 c = i.color * 0.5; //lightmaps #ifdef LIGHTMAPxed4 lm = UNITYSAMPLESAMPLEity_Lightmap, i.uv.xy); c.rgb *= lm.rgb * 4; #endif //fog заменяем плавно цвет поверхности на цвет кубомапы (он же наш туман). Кубомапу нужно задать в настройках освещение (Lighting > Scene > Environment Reflection > Source = Custom > Cubemap = Ваша кубомапа) half4 fogCube = UNITYSAMPLETEXCUBE(unity_SpecCube0, i.viewDir);return lerp(c, fogCube, i.fog); } ENDCG } } Fallback "Mobile/VertexLit" }

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

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

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

Оригинальная статья взята с сообщества Pikabu