Dreaman Dreaman

Mariya, законченный вариант реально классно выглядит! Молодец :)

Mariya Mariya

Всем привет!
Сегодня хочу показать законченный вариант домика Сырны.

alexprey alexprey

StarPlosion: Битва с пиратами за планету

Wings' might Wings' might

Всем привет)
Добавил на этой неделе начальное окно, новую валюту и достижения:

alexprey alexprey

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

...
shadeborn shadeborn

Это всё круто, но...зачем? Ок есть Стим. Хорошо, теперь еще и ЕГС есть. Благо, комп не зашкварен Оригином...но у людей и он стоит. И для работы игр внутри этих сервисов нужны, собсно, эти сервисы. Так поверх этих сервисов нужно накатить еще один...

Devion Devion

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

Так точно, либо любое изменение в объекте на стороне редактора.

alexprey alexprey

Devion, только рад за дополнение, Спасибо)

хм, а вот это интересно, не пробовал никогда такой кейс.

Devion Devion

лёш, ты же не против если я дополню? )

скриптаблы, для понимания могут храниться на уровне ассета и на уровне сцены.

Mariya Mariya

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

Wings' might Wings' might

Всем привет)
За неделю в игру было добавлено меню настроек, переделана старая локация, добавлены новые враги и повышена производительности

alexprey alexprey

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

Dreaman Dreaman

Всем привет!
Для проекта "Mental State" разработано новое устройство, которое уже полностью функционирует внутри игрового мира. Оно носит название "Репульсивер". Совместно с очередными большими воротами это устройство образует новую головоломку...

...
Mariya Mariya

Всем привет!
На этой неделе мы научили Сырну летать!

Tartal Tartal

alexprey, кастомизации - создание внешности персонажа? Я всегда любил это, но не думаю, что это будет к месту в мясном шутере)
EfimovMax, да, есть немного)

Tartal Tartal

Jusper, точно не помню, уже как полгода точно) Я вроде в Дискорде немного обсуждал эту тему. Пока немножко попробовал движок - мне очень нравится. Ну, в конце концов, он идеально подходит под жанры, с которыми я хочу работать...

alexprey alexprey

Первая тема крутая очень!

Логотип проекта 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 шейдер.