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

Guassian Blur

Guassian Blur

Размытие по Гауссу

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

Если вбить в википедии формулу Гауссовского размытия, то нам выдаст что-то подобное

формула Гаусса — Guassian Blur  — Unity — DevTribe: Разработка игр (blur, shader, Unity, размытие, Шейдеры, шейдеры, юнити)
формула Гаусса

Не очень-то приятная формула. Но если мы посмотрим на график ее функции, получим вот что

Guassian Blur  — Unity — DevTribe: Разработка игр (blur, shader, Unity, размытие, Шейдеры, шейдеры, юнити)

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

Теперь суть гаусовского алгоритма : мы берем какой-либо пиксель изображения и считаем его центральным. Затем суммируем его цвет + цвета всех его соседей. Количество этих самых соседей, цвета которых мы суммируем, определяется kernel картой. Они могут быть 3х3, 4х4, 6х6 и т.д. Но суммируем не просто бездумно ( как это делалось в статье с PCF размытием), а сначала умножаем цвет на определенный коэффициент в зависимости от удаленности пикселя от центра. Прямо как в нормальном распределении.
Коэффициенты удаленности от центра закодированы в матрице свертки, которая выглядит вот так:

Guassian Blur  — Unity — DevTribe: Разработка игр (blur, shader, Unity, размытие, Шейдеры, шейдеры, юнити)

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

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

С теорией вроде разобрались, переходим к практике.

Пишем шейдер

Первым делом нужно подготовить почву. Размывать мы будем изображение в тот момент, когда оно уже прошло все основные этапы графического конвеера Unity и готово выводится на экран. Т.е. наш эффект будет будет применяться к текстуре. Такие эффекты называются постэффектами (Post Effects/ Image Effects).
В движке есть функция, которая вызывается в момент рендера изображения на экран.
void OnRenderImage(RenderTexture src, RenderTexture dest) {}
RenderTexture src - это текстура, которая поступает с графического конвеера
RenderTexture dest - текстура, которая будет выведена на экран.

Переносить содержимое одной рендер текстуры на другую можно при помощи функции
public static void Blit(Texture source, RenderTexture dest, Material mat)
source - источник
dest - разультат
mat - материал, который будет применен к source, после чего source будет записана в dest

Напишем небольшой скрипт, который повесим на камеру:

using UnityEngine;

public class Image_Effets : MonoBehaviour {

    public Material mat;

    RenderTexture rt;

	void Start () {
        rt = new RenderTexture(Screen.width, Screen.height, 1);
        mat.SetTexture("_MainTex", rt);
	}


    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        rt = src;
        mat.SetTexture("_MainTex", rt);
        Graphics.Blit(src, dest, mat);   
    }
}

mat - материал, на котором будет висеть шейдер размытия
rt - рендер текстура, которую передаем в материал для обработки шейдером

Вешаем скрипт на камеру и идем писать шейдер. Я назвал шейдер просто Blur.

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

Итак, блок свойств:

Shader "Hidden/Blur"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_BlurDev("Avarage_Koef", Range(0,50)) = 16
		_uvOffset("Offset",Range(0,0.01)) = 0.05
		_useBlur ("Use Blur", Int) = 1
	}

_BlurDev - переменная для регулировки коэффициента деления
_uvOffset - переменная для смещения по uv коордиантам
_useBlur - для переключения между размытым и не размытым изображением

Далее идут стандартные преобразования, здесь нет ничего необычного:

SubShader
	{
		// No culling or depth
		Cull Off 
		//ZWrite Off ZTest Always
	
	Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
								
				return o;
			}

Опишем внешние переменные

			uniform sampler2D _MainTex;
			uniform float4 _MainTex_TexelSize;
			uniform float _BlurDev;
			uniform float _uvOffset;
			uniform int _useBlur;

В переменную float4 _MainTex_TexelSize движок передает размер текселей _MainTex. ( они равны 1/width, 1/height)
Далее введем Гаусовскую матрицу, которую будем использовать для умножения на цвета пикселей

float3x3 GM = float3x3
			( 1.0,2.0,1.0,
			  2.0,4.0,2.0,
			  1.0,2.0,1.0
			);

И напишем функцию размытия

float4 guassianBlur(sampler2D tex, float2 uv, float2 tex_size)
			{
				float4 result = 0;
				tex_size+= _uvOffset;
				result = 
				tex2D ( tex, uv + float2(-1.0,1.0) * tex_size )* GM[0][0] + 
				tex2D ( tex, uv + float2(0.0,1.0) * tex_size) * GM[0][1]  + 
				tex2D ( tex, uv + float2(1.0,1.0) * tex_size )* GM[0][2]  + 
				tex2D ( tex, uv + float2(-1.0,0.0) * tex_size) * GM[1][0]  + 
				tex2D ( tex, uv + float2(0.0,0.0) * tex_size) * GM[1][1]  + 
				tex2D ( tex, uv + float2(1.0,0.0) * tex_size) * GM[1][2]  + 
				tex2D ( tex, uv + float2(-1.0,-1.0) * tex_size )* GM[2][0]  + 
				tex2D ( tex, uv + float2(0.0,-1.0) * tex_size)* GM[2][1]  + 
				tex2D ( tex, uv + float2(1.0,-1.0) * tex_size) * GM[2][2] ;
				return result/_BlurDev;

			}

result - переменная, которая хранит сумму всех окрестных пикселей.
Суммирование идет по часовой стрелки, начиная верхнего левого пикселя.
Например, одна строчка tex2D ( tex, uv + float2(-1.0,1.0) * tex_size )* GM[0][0] значит, что мы берем цвет из tex, по координатам uv(это центр) + левый верхний угол float2(-1.0,1.0), который умножается на размер текселя. Потом вот этот цвет домнажаем на значение веса из матрицы, у которого левая верхняя позиция (GM[0][0]). И так для каждого пикселя.
И теперь вызываем эту функцию из фрагментного шейдера. У меня еще есть условия для включение и отключения размытия.

float4 frag (v2f i) : SV_Target
			{
				float4 col = 1;
				if(_useBlur > 1) col = guassianBlur(_MainTex,i.uv,_MainTex_TexelSize.xy);
				else col = tex2D(_MainTex, i.uv);
				col.rgb = col.rgb;
				return col;
			}
			ENDCG
		}
	}
}
до — Guassian Blur  — Unity — DevTribe: Разработка игр (blur, shader, Unity, размытие, Шейдеры, шейдеры, юнити)
до
после — Guassian Blur  — Unity — DevTribe: Разработка игр (blur, shader, Unity, размытие, Шейдеры, шейдеры, юнити)
после
со смещением по uv — Guassian Blur  — Unity — DevTribe: Разработка игр (blur, shader, Unity, размытие, Шейдеры, шейдеры, юнити)
со смещением по uv
Shader "Hidden/Blur"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_BlurDev("Avarage_Koef", Range(0,50)) = 16
		_uvOffset("Offset",Range(0,0.01)) = 0.05
		_useBlur ("Use Blur", Int) = 1
	}
	SubShader
	{
		// No culling or depth
		Cull Off 
		//ZWrite Off ZTest Always
	

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
								
				return o;
			}


			
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_TexelSize;
			uniform float _BlurDev;
			uniform float _uvOffset;
			uniform int _useBlur;
			float3x3 GM = float3x3
			( 1.0,2.0,1.0,
			  2.0,4.0,2.0,
			  1.0,2.0,1.0
			);

			float4 guassianBlur(sampler2D tex, float2 uv, float2 tex_size)
			{
				float4 result = 0;
				tex_size+= _uvOffset;
				result = 
				tex2D ( tex, uv + float2(-1.0,1.0) * tex_size )* GM[0][0] + 
				tex2D ( tex, uv + float2(0.0,1.0) * tex_size) * GM[0][1]  + 
				tex2D ( tex, uv + float2(1.0,1.0) * tex_size )* GM[0][2]  + 
				tex2D ( tex, uv + float2(-1.0,0.0) * tex_size) * GM[1][0]  + 
				tex2D ( tex, uv + float2(0.0,0.0) * tex_size) * GM[1][1]  + 
				tex2D ( tex, uv + float2(1.0,0.0) * tex_size) * GM[1][2]  + 
				tex2D ( tex, uv + float2(-1.0,-1.0) * tex_size )* GM[2][0]  + 
				tex2D ( tex, uv + float2(0.0,-1.0) * tex_size)* GM[2][1]  + 
				tex2D ( tex, uv + float2(1.0,-1.0) * tex_size) * GM[2][2] ;
				return result/_BlurDev;

			}


			float4 frag (v2f i) : SV_Target
			{
				float4 col = 1;
				if(_useBlur > 1) col = guassianBlur(_MainTex,i.uv,_MainTex_TexelSize.xy);
				else col = tex2D(_MainTex, i.uv);
				col.rgb = col.rgb;
				return col;
			}
			ENDCG
		}
	}
}

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

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

Если есть вопросы, буду рад ответить )

Удачи!



А мне вот кст не особо нравится размытие по гауссу из за двоения изображения. В итоге пользовался варицией сжатия и расширения исходной текстуры. Это конеяно затратнее по ресурсам но результат был без двоения.

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

Формат прекрасен и очень полезен.

alexprey, если ты про последнее фото, то там не совсем чистое гауссовское размытие. Чисто гауссовское это вторая фотка. Двоение происходит только при извращении типо как со смещением пикселей kernel мапы по uv.

alexprey, для усиления размытия нужно увеличивать матрицу свертывания

lehanru, хммм, так вот оно в чем дело, в моей криворукости тогда 😂

alexprey, алгоритм размытия по Гауссу что приведен в статье - это в общем и целом алгоритм фильтрации (так работают обычный фильтра для фото). Просто у каждого конкретного фильтра свое конкретное ядро (матрица с конкретными значениями), в данной статье это ядро Гаусса. Но не об этом.

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

Меньше окно -> больше влияние самых-самых соседних пикселей -> каскадный эффект.
Больше окно -> меньше влияние самых-самых соседних пикселей.