danilaxxl danilaxxl

CollectableItemData.cs

[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]

GoloGames GoloGames

vadya_ivan, рад, что вам игра показалась интересной : )

P.S. Кстати уже доступна бесплатная демо-версия в Steam

vadya_ivan vadya_ivan

Визуал, задумка, музыка , механики, все в цель

GoloGames GoloGames

Ato_Ome, спасибо за позитивные эмоции, будем стараться : )

Ato_Ome Ato_Ome

Потрясающий результат, все так четенько, плавненько)
То ли саунд, то ли плавность напомнили мне игрушку World of Goo, удачи вам в разработке и сил побольше дойти до релиза!)

Cute Fox Cute Fox

Graphics are a little cool, good HD content. But this game doesn't cause nary interest me.
However the game is well done.

GMSD3D GMSD3D

Почему действие после всех условий выполняется?
[step another object]

Zemlaynin Zemlaynin

Jusper, Везде, но наугад строить смысла нет. Нужно разведать сперва территорию на наличие ресурсов.

Jusper Jusper

Zemlaynin, а карьеры можно будет везде запихать?
Или под них "особые" зоны будут?

Zemlaynin Zemlaynin

Это так скажем тестовое строительство, а так да у города будет зона влияния которую нужно будет расширять.

Jusper Jusper

А ссылка есть?

Jusper Jusper

Я не оч понял из скриншота, как вообще работает стройка. У игрока будет как бы поле строительства?

split97 split97

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

split97 split97

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

ViktorJaguar ViktorJaguar

Почему я нигде не могу найти нормальный туториал, где покажут как экипировать предмет (например, меч) в определенную (выделенную под оружие) ячейку???

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

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

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

Справка