Jusper Jusper

EfimovMax есть че сказать по этому поводу?

coderDimka coderDimka

Спасибо большое за этот гайд! А то уже волосы дыбом от отсутствия инфы)) Хотелос бы больше по этой теме. Например, что дальше. Т.е. я подключился к тестовому серверу стим. Как теперь мне синхронизировать двух и более игроков...

...
Tartal Tartal

Jusper, да, беда)

Tartal Tartal

Jusper, патронную ленту я всё же по-другому в итоге проведу (к герою, скорее всего). А затвор передёргивать не нужно, это не затвор, а рукоятка для левой руки, дробовик полностью автоматический)

Вот так он сейчас выглядит в ортографическом виде сбоку...

...
Jusper Jusper

У нас тут два экстерминатора нарисовалось)

Jusper Jusper

Геймплей выглядит довольно динамичным даже в прототипе, а вот визуал надо очень сильно подтянуть. Свет и взаимодействие моделей с ним создает вообще несуразную картинку, ничо не понятно.
Не увидел нигде...

Jusper Jusper

Tartal,

Мне как раз нравился его резкий прямой скос, который ты в модели превратил в ручку гвоздемета.

Tartal Tartal

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

Jusper Jusper

Tartal,

Это, блин, нифига не скетч, а целый промоматериал :)

Valdram Valdram

Скриншоты из заявки для джема об участии в котором можно лишь жалеть со всякой точки зрения. Но сборка за такой срок представляет собой пусть и несовершенный, а именно Идейный пирог. Результат: https://valdram.itch.io/pipe-and-3-legs

...
alexprey alexprey

Хм, шотган миниган хорошш, представляю какое решето из анурана получится если сразу из четырех стволов пальнуть)

Tartal Tartal

Недавно набросал скетч автоматического дробовика. А сегодня доделал его модель (в процессе не слабо поменяв концепцию), но пока без каких-либо текстур и, тем более, анимаций.

Кратко об этом оружии:

Jusper Jusper

E.S., не там чисто на игру трехмерную был.
Я в рамках нее экспериментировал.

E.S. E.S.

Jusper, спасибо, я думал по шейдерам курс) Но я там и по шейдерам нашел парочку.

Jusper Jusper

E.S., сейчас курс поделился на 2, раньше было и 3D и 2D в одном.
https://www.udemy.com/share/1000PU/

E.S. E.S.

а можно ссылку на курс на Udemy?

Логотип проекта Unity

#6 Surface Shader

Surface Shader

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

Вступление

Думаю мало кто не согласится с тем, что написание вертексных и пиксельных шейдеров работа достаточно трудоёмкая. Для того, чтобы написать простой шейдер с поддержкой карты нормалей и бликов, придется вручную написать 400 строк кода. И всё потому, что реализация содержит множество технических аспектов, которые нужно учитывать (forward/deffered lighting, множественные проходы, различные матрицы переходов и тд). Поэтому разработчики движка создали упрощенный вид шейдера - поверхностный, который выполняет все те-же действия за кулисами, позволяя нам добиваться нужных эффектов без столь громоздкого кода.

Немного теории

!Начнем! с того, что это хоть и упрощенный, но всё-таки шейдер, следовательно, его мы пишем на языке *CG* внутри блока CGPROGRAM...ENDCG, который, в свою очередь, находится внутри блока SubShader{}, но в отличие от вертексных/пиксельных шейдеров, поверхностный не нужно помещать внутрь блока прохода *Pass{}*.
Вот как выглядит шаблон поверхностного шейдера:

 Shader "MyShaders/ExampleShader"
{
 Properties
{...}
SubShader
{
 CGPROGRAM 
	#pragma surface surfaceFunction lightModel
	
	struct Input
	{
	 ...
	}; 
	void surfaceFunction (Input IN, inout SurfaceOutput o)
	{...}
ENDCG
}
Fallback "Diffuse"
}

!Разберем! все по порядку:
#pragma surface surfaceFunction .. - идентификация поверхностного шейдера.
lightModel - указываем, какую модель освещения мы будем использовать.
Другими словами, строчка #pragma surface surf Lambert говорит компилятору, что функция surfaceFunction - это поверхностный шейдер, который использует модель освещения
lightModel. // Встроенные в юнити модели освещения: Lambert, BlinnPhong, Standard.
struct Intput{} - задаем структуру с входными данными
void surfaceFunction (Input IN, inout SurfaceOutput o){} - собственно, сама шейдерная функция. На вход принимает входную структуру Input IN. Внутри функции шейдера мы производим вычисления и заполняем все поля специальной выходной структуры SurfaceOutput o.

Входная структура

!Входная! структура в поверхностном шейдере может содержать следующие переменные:
* float3 viewDir - содержит направление взгляда
* float4 SomeName : COLOR - содержит интерполированный цвет
* float4 screenPos - содержит экранные координаты
* float3 worldPos - содержит мировые координаты точки
* float3 worldRefl - содержит направление отражения вектора взгляда от данной точки в мировых координатах //(если не задана карта нормалей)
* float3 worldNormal - содержит направление вектора нормали в мировых координатах // (если не задана карта нормалей)
* float3 worldRefl; INTERNAL_DATA - содержит направление отражения вектора взгляда от данной точки в мировых координатах //(если задана карта нормалей, используется вместе с функцией WorldReflectionVector (IN, o.Normal) )
* float3 worldNormal; INTERNAL_DATA - содержит направление вектора нормали в мировых координатах // (если задана карта нормалей, используется вместе с функцией WorldNormalVector (IN, o.Normal) )

Чтобы задать uv координаты, нужно подписать префикс uv_ к имени названию текстуры. Например, float 2 uv_mainTexture

Выходная структура

!Выходная! структура содержит в себе параметры поверхности, которые мы можем заполнить в поверхностном шейдере. Юнити предлагает нам две структуры на выбор:
Стандартная, используется для моделей освещения Lambert'а и BlinnPhonga'а.

struct SurfaceOutput
{
    fixed3 Albedo;  // цвет
    fixed3 Normal;  // нормаль в пространстве касательных ( из карты нормалей) 
    fixed3 Emission; // свечение
    half Specular;  // сила спекуляра в пределах от 0 до 1
    fixed Gloss;    // интенсивности спеуляра
    fixed Alpha;    // прозрачность
};

Более физически корректные, используются для моделей Standard

struct SurfaceOutputStandard
{
    fixed3 Albedo;      //  цвет
    fixed3 Normal;      // нормаль в пространстве касательных ( из карты нормалей)
    half3 Emission; // свечение
    half Metallic;      // 0 - не метал, 1 - метал
    half Smoothness;    // 0 - грубая поверхность, 1 - гладкая поверхность
    half Occlusion;     // влияние на цвет окружения
    fixed Alpha;        // прозрачность
};

-

Дополнительные модификации

-
!Дополнительные! модификации задаются в директиве препроцессора после указания модели освещения.

Кастомные функции ===

* vertex:vertexFunction - функция для изменения вертексного шейдера
* finalcolor:ColorFunction - функция для изменения финального цвета
* finalgbuffer:ColorFunction - функция для изменения контента G-буффера в отсроченном освещении

Тени и тесселяция ==

* fullforwardshadows - добавляет тени для всех источников света в Forward rendering освещении
* tessellate:TessFunction - функция, которая вычисляет параметры тесселяции. (для работы требует DX11или выше)

Функции генерации кода ==

!Обычно! поверхностный шейдер реализует все возможные сценарии освещения/затенения/лайтмаппинга. Однако, в некоторых случаях это нам совсем не нужно. Для отключения ненужных опций шейдера существуют специальные команды. Вот некоторые из них:
* exclude_path:deferred, exclude_path:forward, exclude_path:prepass - отключение генерации прохода модели освещения
* noshadow - отключение всех теней
* noambient - отключение влияния ambient lighting или light probes
* nolightmap - отлючение всех лайтмапов
* noforwardadd - отключение добавочного прохода в forward rendering

Пишем шейдер

!Ну! вот и всё, пока нам этих знаний должно хватить. Настал тот самый момент, когда мы можем сделать свой велосипед применить полученные знания на практике.
Создадим новый поверхностный шейдер: Create->Shader->Standard Surface Shader
Я назову его "FirstSurface"
Делаем двойной клик по шейдеру и смотрим, что у нас тут:

#6 Surface Shader — Unity — DevTribe: Разработка игр

!Да!, собственно, ничего особенного. Этот шейдер берет значения из инспектора и прямиком транслирует их в шейдерную функцию. Думаю, стоит сделать из него что-нибудь более интересное. Задумка состоит в том, чтобы мы сделалии ледяную сферу с трещинами, в которых будем время от времени проявляться лава.
Ингредиенты:
* Сфера
* Текстура льда
* Текстура лавы
* Наспех сделанная карта нормалей льда

Начнем модифицировать шейдер. Сперва добавим в окно свойств недостающие переменные:

Properties 
	{
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {} // текстура льда
		_NormalMap("NormalMap",2D) = "white"{} // карта нормалей льда
		_SecondTex ("Lava_Texture",2D) = "white"{} // текстура лавы
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0
		_Str ("Strenght", Range(0,10)) = 1 // переменная для регулировки силы
	}

Следующим нашим действием будет добавление объявление внешних переменных из окна свойств

sampler2D _NormalMap;
sampler2D _SecondTex;
half _Str;

и добавлением переменных с uv координатами в входную структуру Input

struct Input 
		{
			float2 uv_MainTex;
			float2 uv_NormalMap;
			float2 uv2_SecondTex;
		};
заметьте, что к внешним переменным не обязательно обозначать как !uniform!, хотя, по канону, это желательно делать

Вот мы и добрались до самой шейдерной функции. Первым делом назначим нашу карту нормалей:

o.Normal = UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));
float3 norm = o.Normal;

Затем создадим переменные для льда и лавы:

fixed4 lava = tex2D(_SecondTex,(IN.uv2_SecondTex));
fixed4 ice = tex2D(_MainTex,IN.uv_MainTex);

Теперь нам стоит подумать как добиться, чтобы лава была только в трещинах. Какая переменная у нас отвечает за высоту поверхности ? Правильно, нормаль. Путем плясок с бубном было выяснено, что за трещины отвечает xy компонента вектора нормали. Интерполируем цвет между цветом льда и красным с использованием длинны norm.xy:

fixed4 col = lerp(ice,fixed4(1,0,0,0),length(norm.xy)); 

Вот что у нас получилось:

#6 Surface Shader — Unity — DevTribe: Разработка игр

Как-то слабовато. Умножим length(norm.xy) на _Str=2 и посмотрим что получиться:

#6 Surface Shader — Unity — DevTribe: Разработка игр

Уже намного лучше. Заменим красный цвет на лаву:

fixed4 col = lerp(ice,lava,length(norm.xy)*_Str);

Ну, и последний штрих - добавить мерцание. Здесь я буду использовать переменную _SinTime.w, т.к. она изменяется в пределах от 0 до 1, что и требуется. Еще немного плясок с бубном и вот что я вывел:

fixed4 c = lerp(ice * _Color,max(lava-0.5,lava*(_SinTime.w+_CosTime.x)*length(norm.xy)*_Str),length(norm.xy)*_Str);

При желании, можно поиграться со значениями и формулой, чтобы получить более интересный результат.

Что получилось у меня:

Shader "Custom/FirstSurface" {
	Properties 
	{
		_Color ("Color", Color) = (1,1,1,1)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
		_NormalMap("NormalMap",2D) = "white"{}
		_SecondTex ("Lava_Texture",2D) = "white"{}
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0
		_Str ("Strenght", Range(0,10)) = 1
	}
	SubShader 
	{
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
		#pragma surface surf Standard fullforwardshadows

		// Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 3.0

		sampler2D _MainTex;
		sampler2D _NormalMap;
		sampler2D _SecondTex;
		//float4 _SecondTex_ST;

		struct Input 
		{
			float2 uv_MainTex;
			float2 uv_NormalMap;
			float2 uv2_SecondTex;
		};

		half _Glossiness;
		half _Metallic;
		fixed4 _Color;
		half _Str;
		void surf (Input IN, inout SurfaceOutputStandard o) {
			// Albedo comes from a texture tinted by color
			o.Normal = UnpackNormal(tex2D(_NormalMap,IN.uv_NormalMap));
			float3 norm = o.Normal;
			fixed4 lava = tex2D(_SecondTex,(IN.uv2_SecondTex));
			fixed4 ice = tex2D(_MainTex,IN.uv_MainTex);
			fixed4 col = lerp(ice * _Color,max(lava-0.5,lava*(_SinTime.w+_CosTime.x)*length(norm.xy)*_Str),length(norm.xy)*_Str);
			o.Albedo = col.rgb;
			// Metallic and smoothness come from slider variables
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			o.Alpha = col.a;
		}
		ENDCG
	}
	FallBack "Diffuse"
}

Если есть вопросы, буду рад ответить.
Удачи!



Спасибо.
Благодаря тебе и курсу на Udemy, я запрограммировал свой первый Surface шейдер.

а можно ссылку на курс на Udemy?

E.S., сейчас курс поделился на 2, раньше было и 3D и 2D в одном.
https://www.udemy.com/share/1000PU/

Jusper, спасибо, я думал по шейдерам курс) Но я там и по шейдерам нашел парочку.

E.S., не там чисто на игру трехмерную был.
Я в рамках нее экспериментировал.