Pull to refresh

Как работать с Shader Graph в Unity3D

Reading time 6 min
Views 42K


Графы шейдеров это новый инструмент для создания шейдеров в юнити. Он позволяет создавать шейдеры людям не имеющим навыков написания кода. Результат каждой операции виден при редактировании. Идеальный инструмент для новичков и экспериментаторов.

Добавление Shader Graph в проект делается при помощи Package Manager.



Но на сегодня доступна версия только для Lightweight Render Pipeline, поэтому для экспериментов нужно создавать проект так:



Простейший шейдер


Shader Graph позволяет создать два вида шейдеров Unlit(без освещения) и PBR(фотореалистичный рендер), а так же sub(нода для шейдера). Последнее можно использовать внутри Unlit и PBR шейдеров.



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

Создаём материал и создаём Unlit Graph. Перетягиванием назначаем материалу шейдер.



Открыв шейдер двойным кликом мы увидим мастер-ноду



На выходе этого шейдера мы можем контролировать:

  • Position — позицию вершины
  • Color — цвет каждого пикселя поверхности
  • Alpha — его прозрачность
  • AlphaClipThreshold — порог прозрачности, если мы не используем полупрозрачность

Подавляющее большинство шейдеров не использует полупрозрачность из-за вычислительной сложности и ограничений, которые накладываются на такие шейдеры. И там, где можно обойтись без полупрозрачности, надо обходиться без неё.

Будет ваш шейдер использовать полупрозрачность или нет, можно настроить в мастер-ноде:



  • Opaque — непрозрачный
  • Transparent — полупрозрачный

Чтобы раскрасить модель мы можем подать на вход(color) мастер-ноды трёхмерный вектор или цвет, что для шейдера по сути одно и то же, только по разному отображается в графе.
Новые ноды создаются через контекстное меню



в данном случае мы можем использовать две ноды Color или Vector4



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



а потом мышкой перетянуть его в граф тем самым создав ноду.



  • Exposed — позволяет это свойство видеть в инспекторе при редактировании материала
  • Default — задаёт значение цвета по умолчанию
  • Mode — позволяет выбрать диапазон яркости(HDR позволяет выйти за рамки обычной яркости)

Чтобы созданное свойство влияло на цвет материала надо его выход соединить со входом Color у мастер-ноды.



Тот же шейдер, но кодом
Shader "Tutorial/Simpliest" // Shader - обязательное начало для любого шейдера, 
                             // после этого ключевого слова в
							 // кавычках пишется url-подобное имя шейдера
{
	Properties // блок свойств, которые будут отображаться во вкладке 
               //  inspector в Unity
	{
		_Color ("Color", Color) = (0.5,0.6,0.7,1)
		//Каждая строка связана с переменной и содержит следующие данные:
		// _Color - имя переменной, оно должно совпадать по написанию с 
        //          одной из переменных внутри CGPROGRAM
		// "Color" - текст, который будет отображаться в окне 
        //           Inspector перед значением переменной
		// Color - тип данных, от него зависит как будет выглядеть 
        //         блок выбора значения в Inspector, бывает
		//         5 типов данных: 
		//         Color - выглядит как выбор цвета, 
		//         Vector - как 4 текстовых поля ввода,
		//         Float - поле ввода одного числа с плавающей запятой, 
		//         2D - текстура, 
		//         CUBE - выбор кубической текстуры
		// (0.5,0.6,0.7,1) - значение переменной по умолчанию
	}
	SubShader // в одном шейдере может быть несколько этих блоков, 
              // но одновременно работать может только один
	{
		// No culling or depth
		// Это зарезервированные слова, в этом шейдере они указаны 
        // для упрощения работы GPU, благодаря им потребуется меньше 
        // вычислений для отображения шейдера, а ZTest Always говорит 
        // о том, что этот шейдер будет рисовать объект поверх всех остальных
		Cull Off 
		ZWrite Off 
		ZTest Always
​
		Pass // этих блоков может быть несколько внутри одного SubShader,
             // каждый Pass это отдельный проход рендера по объекту с этим 
             // шейдером, результат следующего Pass рисуется поверх предыдущего
		{
			CGPROGRAM // Зарезервированное слово, оно лишь отмечает начало 
                      //  кода самого шейдера
​
			#pragma vertex vert_img // использовать для вертексного шейдера 
                                     // vert_img, которая описана не нами
			#pragma fragment frag   // для фрагментного шейдера использовать 
                                     //функцию frag, описанную ниже
			
			#include "UnityCG.cginc" // позволяет нам использовать готовые функции, 
                                      // написанные командой Unity
			                          // в их числе и vert_img, код файла можно найти 
                                      // в файле:
		                              // %UNITY%\Editor\Data\CGIncludes\UnityCG.cginc
​
			fixed4 _Color; // fixed4 - тип переменной, именно этот тип представляет 
                           // собой структуру из 4х переменных типа fixed
​
			fixed4 frag (v2f_img i) : COLOR
			{
				fixed4 col = _Color;
				return col;
			}
			ENDCG // Зарезервированное слово, оно отмечает конец кода самого шейдера
		}
	}
}


Самый простой шейдер с текстурой


Чтобы наложить нашу текстуру на меш, нам надо создать ноду, которую можно будет настроить извне Shader Graph. Для этого создадим свойство



и вытянем его, при этом создастся нода текстуры



После этого нужно создать ноду сэмплера текстуры, которая сможет на вход получать текстуру и uv-координату, а на выход давать цвет пикселя.



Выход сэмплера соединяем со входом color мастер-ноды



Простейший шейдер с текстурой кодом
Shader "Tutorial/Texture"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		// В предыдущей части в качестве свойства был цвет, 
		// а теперь текстура, в Inspector будет предложено
		// выбрать одну. Здесь "white" это значение по умолчанию,
		// означает, что будет создана текстура белого цвета.
	}
	SubShader
	{
		Cull Off
		ZWrite Off
		ZTest Always

		Pass
		{
			CGPROGRAM
			#pragma vertex vert_img 
			#pragma fragment frag 

			#include "UnityCG.cginc"

			sampler2D _MainTex;
			// sampler2D - это тип переменной который надо ставить когда
			// мы хотим работать с текстурой, заметьте, что имя переменной
			// _MainTex совпадает с именем свойства в блоке Properties,
			// это обязательно для корректной работы.

			fixed4 frag(v2f_img i) : COLOR
			{
				// Следующая строка позволяет нам получить цвет пикселя в точке
				// с которой мы работаем в этой итерации, если помните, то ранее 
				// было сказано, что фрагментный шейдер работает с каждым видимым 
				// пикселем объекта. 
				// tex2D - это готовая функция для получения значения цвета,
				// первый параметр (_MainTex) это из какой текстуры мы хотим
				// получить цвет, а второй (i.uv) это UV координаты в которых 
				// мы хотим узнать цвет.
				fixed4 col = tex2D(_MainTex, i.uv);

				return col;
			}
			ENDCG
		}
	}
}


Негатив текстуры


Перед отображением текстуры на экране мы можем её изменить применив математические операции. Например создать негатив простым вычитанием.

Добавим ноду Substract уменьшаемое будет (1;1;1;1), а вычитаемое выходом текстуры.



Негатив текстуры кодом
Shader "Tutorial/Texture"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Cull Off
		ZWrite Off
		ZTest Always

		Pass
		{
			CGPROGRAM
			#pragma vertex vert_img 
			#pragma fragment frag 

			#include "UnityCG.cginc"

			sampler2D _MainTex;

			fixed4 frag(v2f_img i) : COLOR
			{
				fixed4 col = tex2D(_MainTex, i.uv);

				col = 1 - col;
				// Инверсия цвета, цвет в шейдере представлен 4-мя компонентами
				// RedGreenBlueAlpha - КрасныйЗелёныйСинийНепрозрачность,
				// причём значение каждого компонента в диапазоне от 0 до 1

				return col;
			}
			ENDCG
		}
	}
}


Смешение двух текстур


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

А саму операцию смешивания произведёт нода Lerp.



Смешение двух текстур кодом
Shader "Tutorial/NoiseOverlay"
{
	Properties
	{
		_MainTex("Main Texture", 2D) = "white" {}
		_NoiseTex("Noise Texture", 2D) = "white" {}
		_LerpValue("Lerp Value", Range(0, 1)) = 0.5
	}
	SubShader
	{
		Cull Off
		ZWrite Off
		ZTest Always
​
		Pass
		{
			CGPROGRAM
			#pragma vertex vert_img 
			#pragma fragment frag 
​
			#include "UnityCG.cginc"
​
			sampler2D _MainTex;
			sampler2D _NoiseTex;
			float _LerpValue;
​
			fixed4 frag(v2f_img i) : COLOR
			{ 
				half4 base = tex2D(_MainTex, i.uv);
				half4 overlay = tex2D(_NoiseTex, i.uv);
​
				return lerp(base, overlay ,  _LerpValue);
			}
			ENDCG
		}
	}
}


Cutout маска


Чтобы сделать полностью прозрачной часть модели нужно подать на вход мастер-ноды значение Alpha канала, создать свойство-слайдер и подать его на вход AlphaClipThreshold



Слайдер нужен для исправления бага в Graph shader, который не позволяет сделать cutout и к тому же он позволит менять значение из настроек материала.

Инверсия UV


Для работы с UV нужно создать ноду UV и подключить её к сэмплеру текстуры



здесь же можно выбрать канал UV, у некоторых моделей этих каналов может быть несколько, на случай многослойных текстур. Этим шагом мы не изменили ровным счётом ничего, а чтобы инвертировать UV, нам нужно создать ноду которая инвертирует значения UV, нам подойдёт обычное умножение на -1 координаты по Y.



Отзеркаливание текстуры можно сделать настраиваемым из материала, для этого нам понадобится нода Branch, она на вход получает булево значение, а на выход одно из двух значений, в нашем случае 1 или -1



Впечатления


Создание шейдера при помощи Shader Graph сильно упрощает задачу хотя бы потому, что можно наблюдать за изменением результата после каждого этапа вычислений. Сам принцип создания через редактор нод позволяет создать что-то новое даже тем, кто ничего не понимает в шейдерах. Инструмент ещё сырой, но уже очень полезный. Так как можно поэкспериментировать в редакторе, а потом воспроизвести созданное кодом, названия функций в большинстве случаев совпадает с названием нод, поэтому Shader Graph может оказаться полезным и программистам.

P.S. благодарю Darkirius за помощь в подготовке статьи

P.P.S. так же вы можете ознакомиться с уроками по написанию шейдеров в юнити по ссылке Гит-книга
Tags:
Hubs:
+12
Comments 3
Comments Comments 3

Articles