DuCats Games DuCats Games

Трудимся над Мечеломом)
Обновляем локации. Добавили кучу новой травы и растений + немного эффектиков + горы! Теперь пейзажи стали гораздо красивее.

РОМАН РОМАН

Работаю над роботом пауком ходящем по стенам.
ВК: https://vk.com/co_of_co

ASH2 ASH2

Тестируем электрическую сеть.
ВК: https://vk.com/snail_ninja

Jusper Jusper

Tartal, + если это конкретно имплант (то есть чуваку вырвали кусок, поставили механизированный), то искажение органической части должно быть больше. Глянь фотки после косметических операций, когда щи распухшие и раскрашенные пуще некуда...

Tartal Tartal

Jusper, с первыми двумя моментами я всё понял и принял.
А вот с третьим немного не разобрался. Ты имеешь ввиду, что область вокруг глаза-импланта должна быть искажена и повреждена намного сильнее?

Jusper Jusper

Tartal,

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

...
Tartal Tartal

Потихоньку доделал концепт-портрет главного героя:

Jusper Jusper

Чем-то напомнило мне мотоциклы из Throne: Legacy, только обернутые в оформление харконеннов из Emperor: Battle for Dune.

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

alexprey alexprey

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

alexprey alexprey

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

kirsakshlil kirsakshlil

alexprey, мобильная среда разработки, использующая Java.

alexprey alexprey

kirsakshlil, милота) APDE это что такое, что то не слышал про такое..

kirsakshlil kirsakshlil

alexprey, о, ахах, я тоже как-то начинал делать что-то в этом духе, только в APDE. Ручная отрисовка GUI - та еще муть :с . Правда, работать прямо в телефоне не очень удобно. Зато весело, хех. По факту не более чем баловался...

ASH2 ASH2

Jusper, спасибо. Нет, просто "типичный портрет").

Jusper Jusper

И вот мне казалось, что мы все побороли и сможем перейти на 19.3, но началась так называемая кок-магия.
Много нативных вещей отвалилось (в том числе и легаси управление. новое не взлетело). И даже когда мы худо...

...
Jusper Jusper

ASH2,

Рисовка хорошая, я уже говорил ранее.

ASH2 ASH2

Когда личное пространство имеет значение.
ВК: https://vk.com/snail_ninja

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 шейдер.

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

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

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

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

Справка