Введение
Перед новым годом, я решил попробовать себя в написании шейдеров. В качестве цели я избрал для себя шейдер растворяющегося объекта. 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
Автор: romaan27