В процессе поисков наилучшей реализации термального шейдера я наткнулся на matcap-шейдер, опубликованный пользователем bgolus в форумах Unity — неплохая отправная точка.
Я сильно изменил его, чтобы модели выглядели более целостными под разными углами, привязав cap к вершинам и нормалям. То есть по сути это уже не matcap, но мне всё равно хотелось бы отдать должное этому шейдеру, с которого всё началось.
Будем двигаться по порядку, сначала вершинная функция:
1. Нормали объекта:
float3 v.normal;
2. Нормализованная позиция вершины, начинается с опорной точки модели
float3 vertexN = normalize(v.vertex);
3. Векторное произведение этих значений:
float3 viewCross = cross(vertexN, v.normal);
4. Уменьшаем crossproduct.xy вдвое и смещаем, чтобы создать более ровный результат
o.cap = viewCross.xy * 0.5 + 0.5;
Во фрагментной функции
5. Создадим маску и умножим
fixed4 m = tex2D(_Mask, i.uv);
i.cap *= (1 - m.g);
6. Спроецируем цветовую текстуру на результат. (Я использую оранжево-фиолетовый стиль, но вы можете использовать градиент побольше или что-то другое)
fixed4 color = tex2D(_Matcap, i.cap);
Вот и всё, термальный шейдер готов; важный момент — нужно дать ему собственную метку, чтобы скрипт замены задавал его правильно, так что убедитесь, что метки содержат это:
Tags{ "ThermalVision" = "ThermalColors"}
Вот полный шейдер: ссылка на Pastebin
Замена шейдера
Чтобы термальный шейдер мог заменить другой, вам просто нужно применить ту же метку к шейдеру, который нужно заменить:
Tags{ "ThermalVision" = "ThermalColors"}
И убедитесь, что все нужные свойства открыты для заменяемого шейдера:
_Mask("G = no thermal", 2D) = "black" {}
_Matcap("Matcap", 2D) = "white" {}
_Strength("Mask Strength", Range(0,2)) = 0.4
Чтобы переключить шейдер, вам нужно добавить вторую камеру и вызывать для неё
camera.SetReplacementShader(Shader, "NamedShader");
.
Итак создайте новую камеру, и сделайте дочерней по отношению к основной камере, а затем обнулите позицию и поворот, чтобы они были расположены одинаково.
Установите ей Solid Color и чёрный фон.
В «Camera Preview» вы должны видеть всё тоже самое, что и в обычной камере, только без скайбокса, с чёрным фоном.
Настала пора менять шейдеры,
Добавьте публичную переменную Shader
и перетащите на неё шейдер термального эффекта.
Затем добавьте камеру как новую переменную и в Start вызовите SetReplacementShader
, использовав публичный шейдер и имя, заданное в метках
public Shader ThermalShader;
Camera cam;
void Start()
{
cam = GetComponent<Camera>();
cam.SetReplacementShader(ThermalShader, "ThermalVision");
}
После запуска вы должны увидеть термальный эффект только на мешах с заменённым шейдером.
Image Effect
На этом этапе термальный шейдер чётко виден, но кроме него не видно ничего. Чтобы вернуть окружение в камеру, я использую текстуру DepthNormals в качестве Image Effect в сочетании с текстурой Opaque.
Шейдер с Image Effect: ссылка на Pastebin
Добавьте этот простой Blit-скрипт к термальной камере и перетащите шейдер с Image Effect
Я поместил то же изображение, что и в термальный шейдер, но вы можете использовать и другое!
Скрипт переключения термального видения
Теперь достаточно просто переключать его во время игры.
Для переключения булевой переменной, включающей камеру, выполняющей замену и объём постобработки, я использую клавишу T. Для обратного переключения нужно снова нажать T.
Чтобы закончить, добавим термальной камере немного постобработки. Я добавил Chromatic Abberation и Depth of Field.
Вот скрипт переключения термального видения: ссылка на Pastebin Link
(При использовании PPS 2 может потребоваться обновление до более новой версии Post Processing stack)
Дополнительно
Если вы не хотите, чтобы эффект просвечивал сквозь стены, то для камеры замены установите вместо «Solid Color» значение «Don't Clear». Если вы хотите, чтобы скайбокс тоже становился чёрным, то в коде переключения кешируйте материал скайбокса и установите его в null
Private Material SkyboxMaterial;
skyboxMaterial = RenderSettings.skybox;
RenderSettings.skybox = null;
а затем возвращайте его при отключении термального эффекта:
RenderSettings.skybox = skyboxMaterial;
Теперь это меньше похоже на геймплейную механику и больше на простой визуальный эффект.
Быстрый старт, если вам не интересны объяснения
1. Скачайте шейдер термального эффекта
2. Скачайте заменённый Toon-шейдер ИЛИ добавьте в собственный шейдер "ThermalVision" = "ThermalColors"
и переменные _Matcap
, _Mask
, _Strength
.
3. Создайте вторую камеру, дочернюю для основной, установите ей Solid Color, Black Background
4. Возьмите термальный шейдер с Image Effect, создайте материал и добавьте к этой второй камере скрипт ThermalBlit
, перетащив этот материал.
5. Добавьте скрипт ThermalController
, перетащите шейдер термального эффекта, добавьте объём постобработки (необязательно)
6. Нажмите T
, чтобы включить видение. (У того, что должно отображаться, должен быть заменённый шейдер).
Ресурсы
Код шейдера термального эффекта: ссылка на Pastebin
Код шейдера, заменяющий Toon-шейдер: ссылка на Pastebin
Код шейдера с Image Effect: ссылка на Pastebin
Blit-скрипт на C#: ссылка на Pastebin
Скрипт переключателя термального видения на C#: ссылка на Pastebin
Matcap-изображения:
Дополнительная информация о заменяемых шейдерах:
Это старое видео, из которого я впервые о них узнал: Video on Replacement Shaders by Making stuff look good in Unity
Автор: PatientZero