Пишем Dissolve Shader

Unity3D
Tutorial

Введение


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



Путей реализации есть несколько:
  • Alpha
  • CutOff
  • Grab Texture

В итоге у нас получится 3 шейдера, 2 использующие только альфу и которые могут взлететь на моб. девайсах и один с AlphaTest который выглядит посимпатичней но более прожорливый. Благодаря AlphaTest мы можем отключить отсечение невидимых полигонов и не получать наслоение. Но заплатить придётся шейдерной моделью 2.0 и использовать 3.0 из-за чего нельзя будет использовать на моб. девайсах.

Каркас


Общий алгоритм примерно такой:
  • Берём яркость пикселя с маски разрушения или прямо с главной текстуры
  • Сравниваем эту яркость с N
  • Если яркость больше N то альфу пикселя ставим в нолик


В итоге получится не так красиво как на первом видео.



Нам не хватает карт нормалей, и самого крутого. Линий!
Алгоритм инлайнов у меня такой:
  • Берём яркость пикселя с маски разрушения, но с оффсетом по UV + LineSize. И ещё один но оффсет уже UV — LineSize
  • Если хотя бы один из пикселей меньше N, то мы устанавливаем цвет пикселя из текстуры для линий
  • Иначе ставим альфу в ноль (Это как замена аналогичной операции в первом алгоритме)


Резюмирую выше сказанное, мы получаем отсечение по маске и если пиксели отсекаем то проверяем нету ли впритык к нему не отсечённые, если они есть мы становимся краем и ставим себе определённый цвет.

Ближе к телу коду


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

Основной шейдер
 Shader "HolyMonkey/Dissolve/Bumped" {

	Properties {
	    _MainColor ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Mask("Mask To Dissolve", 2D) = "white" {}
		_LineTexture("Line Texture", 2D) = "white" {}
		_Range ("Range", Range(0,3)) = 0
		_LineSize ("LineSize", Float) = 0.001
		_Color ("Line Color", Color) = (1,1,1,1)
		_BumpMap ("Normalmap", 2D) = "bump" {}
		_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
	}
	
	SubShader {
		Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
		LOD 300
		ZWrite On
		Cull Off
		CGPROGRAM 
		#pragma target 3.0
		#include "UnityCG.cginc"
		#pragma surface surf Lambert alphatest:_Cutoff
        

		sampler2D _MainTex;
		sampler2D _LineTexture;
		sampler2D _BumpMap;
		sampler2D _Mask;
		half4 _Color;
		half4 _MainColor;
		float _Range;
		float _LineSize;
		           
		struct Input {
			float2 uv_MainTex;
			float2 uv_BumpMap;
                        float2 uv_Detail;
		};
            
		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			half4 m = tex2D (_Mask, IN.uv_MainTex);
		        half4 lc =  tex2D (_Mask, IN.uv_MainTex - _LineSize);
		        half4 lc2 = tex2D (_Mask, IN.uv_MainTex + _LineSize);
			half4 lc3 = tex2D(_LineTexture, IN.uv_MainTex + _SinTime) * _Color;    
			 
		        o.Albedo = c *  _MainColor;
		        o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		        o.Alpha = 1;
		    
			float factor = m.rgb.x + m.rgb.y + m.rgb.z;
			if(factor >= _Range)
			{
			   float factor2 = lc.rgb.x + lc.rgb.y + lc.rgb.z;
			   float factor3 = lc2.rgb.x + lc2.rgb.y + lc2.rgb.z;
			   if(factor2 < _Range || factor3 < _Range)
			   {
			      o.Albedo = lc3;
			   }
			   else
			   {
                  o.Alpha = 0.0;
               }
            }
		}
		ENDCG
	} 
	Fallback "Diffuse"
}



Ещё можно поиграться с не которыми аспектами и получить следующее

С включённым кулингом
Shader "HolyMonkey/Dissolve/Culling-Mobile" {

	Properties {
	    _MainColor ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Mask("Mask To Dissolve", 2D) = "white" {}
		_LineTexture("Line Texture", 2D) = "white" {}
		_Range ("Range", Range(0,3)) = 0
		_LineSize ("LineSize", Float) = 0.001
		_Color ("Line Color", Color) = (1,1,1,1)
		_BumpMap ("Normalmap", 2D) = "bump" {}
	}
	
	SubShader {
		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
		LOD 300
		CGPROGRAM 
		#pragma target 2.0
		#include "UnityCG.cginc"
		#pragma surface surf Lambert alpha 
        

		sampler2D _MainTex;
		sampler2D _LineTexture;
		sampler2D _BumpMap;
		sampler2D _Mask;
		half4 _Color;
		half4 _MainColor;
		float _Range;
		float _LineSize;
		           
		struct Input {
			float2 uv_MainTex;
			float2 uv_BumpMap;
            float2 uv_Detail;
		};
            
		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			half4 m = tex2D (_Mask, IN.uv_MainTex);
		    half4 lc =  tex2D (_Mask, IN.uv_MainTex - _LineSize);
		    half4 lc2 = tex2D (_Mask, IN.uv_MainTex + _LineSize);
			half4 lc3 = tex2D(_LineTexture, IN.uv_MainTex + _SinTime) * _Color;    
			 
		    o.Albedo = c *  _MainColor;
		    o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		    o.Alpha = 1;
		    
			float factor = m.rgb.x + m.rgb.y + m.rgb.z;
			if(factor >= _Range)
			{
			   float factor2 = lc.rgb.x + lc.rgb.y + lc.rgb.z;
			   float factor3 = lc2.rgb.x + lc2.rgb.y + lc2.rgb.z;
			   if(factor2 < _Range || factor3 < _Range)
			   {
			      o.Albedo = lc3;
			   }
			   else
			   {
                  o.Alpha = 0.0;
               }
            }
		}
		ENDCG
	} 
	Fallback "Diffuse"
}



С ещё одной текстурой вместо прозрачности
Shader "HolyMonkey/Dissolve/NotTransparent" {

	Properties {
	    _MainColor ("Main Color", Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BackTexture ("Back Texture", 2D) = "white" {}
		_Mask("Mask To Dissolve", 2D) = "white" {}
		_LineTexture("Line Texture", 2D) = "white" {}
		_Range ("Range", Range(0,3)) = 0
		_LineSize ("LineSize", Float) = 0.001
		_Color ("Line Color", Color) = (1,1,1,1)
		_BumpMap ("Normalmap", 2D) = "bump" {}
	}
	
	SubShader {
		LOD 300
		ZWrite On
		Cull Off
		
		CGPROGRAM 
		#pragma target 2.0
		#include "UnityCG.cginc"
		#pragma surface surf Lambert
        

		sampler2D _MainTex;
		sampler2D _LineTexture;
		sampler2D _BumpMap;
		sampler2D _Mask;
		sampler2D _BackTexture;
		half4 _Color;
		half4 _MainColor;
		float _Range;
		float _LineSize;
		           
		struct Input {
			float2 uv_MainTex;
			float2 uv_BumpMap;
            float2 uv_Detail;
		};
            
		void surf (Input IN, inout SurfaceOutput o) {
			half4 c = tex2D (_MainTex, IN.uv_MainTex);
			half4 m = tex2D (_Mask, IN.uv_MainTex);
		    half4 lc =  tex2D (_Mask, IN.uv_MainTex - _LineSize);
		    half4 lc2 = tex2D (_Mask, IN.uv_MainTex + _LineSize);
			half4 lc3 = tex2D(_LineTexture, IN.uv_MainTex + _SinTime) * _Color;    
			half4 bc  = tex2D(_BackTexture, IN.uv_MainTex);
		    o.Albedo = c *  _MainColor;
		    o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
		    
			float factor = m.rgb.x + m.rgb.y + m.rgb.z;
			if(factor >= _Range)
			{
			   float factor2 = lc.rgb.x + lc.rgb.y + lc.rgb.z;
			   float factor3 = lc2.rgb.x + lc2.rgb.y + lc2.rgb.z;
			   if(factor2 < _Range || factor3 < _Range)
			   {
			      o.Albedo = lc3;
			   }
			   else
			   {
			      o.Albedo = bc;
			      o.Normal = float3(1,1,1);
			   }
            }
		}
		ENDCG
	} 
	Fallback "Diffuse"
}



Заключение


Написать получилось мало, но думаю дедуктивный код это искупает. Там используются простые вещи, и на хабре есть статьи с их ним разбором. Исходники со всеми нужными ресурсами Вы можете скачать из репозитория на GitHub
Tags:Unity3DShadersShaderLab
Hubs: Unity3D
+4
15.2k 38
Comments 14

Popular right now

Lead Unity Developer на RPG игру
from 250,000 to 450,000 ₽AngryBangersRemote job
Senior Unity 3D developer
to 4,000 $On The Spot DevelopmentСанкт-ПетербургRemote job
Unity-разработчик на VR- и AR-проекты
from 60,000 ₽JetStyleЕкатеринбургRemote job
Game designer
from 130,000 ₽SkyengRemote job
Unity game developer
from 150,000 ₽SkyengRemote job