Работаем с Stencil в Unity3D ShaderLab

Как такое можно сделать?
1 Вариант — «Латать» экран
Заделываем экран кусками — тут думаю понятно) У нас есть одна текстура круга, а остальную часть мы заделываем кусками. Реализовывать дополнительно ничего не нужно, достаточно сверстать. НО! Как только понадобиться несколько «дыр», верстка усложнится. А если нужно динамически подсвечивать часть экрана?2 Вариант — Шейдеры
С шейдерами можно сделать очень многое. Но нам нужно лишь небольшая их часть — отсечение пикселей. Что я бы хотел видеть?1. Какая-то текстура на весь экран, у которой выбираем цвет и прозрачность
2. Вторая текстура, которая уже задает форму «дыры», ее можно перемещать, вращать и тд, отключать/включать при надобности.
3. Все это должно работать в рамках NGUI и UITexture
Начнем!
Вначале рекомендую прочесть про Stencil:
Стенсил буфер (Stencil Buffer — буфер шаблона или буфер трафарета) — это дополнительный буфер, соответствующий размеру выводимого кадра, то есть каждому пикселю изображения на экране соответствует свое значение в стенсил буфере. Каждый раз когда точка рисуется на экран, то кроме тестов, вроде сравнения с глубиной в Z-буфере, она проходит еще и стенсил тест. То есть, например, можно сказать — точка рисуется, только если в стенсиле значение больше единицы. С другой стороны, можно сказать, как изменить значение стенсила после того как пиксель в этом месте отрисуется.1. www.gamedev.ru/terms/StencilBuffer
2. docs.unity3d.com/Manual/SL-Stencil.html
За основу я брал код по второй ссылке.
Вот так выглядит наш код шейдера для UITexture, которая является дырой
Shader "Unlit/Transparent Mask" {
Properties
{
_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
}
SubShader {
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass {
Stencil {
Ref 2
Comp always
Pass replace
Fail keep
ZFail decrWrap
}
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
Offset -1, -1
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
struct appdata {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
v2f o;
v2f vert(appdata v)
{
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.texcoord = v.texcoord;
o.color = v.color;
return o;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 color = tex2D(_MainTex, IN.texcoord);
color.rgb *= IN.color.rgb;
if(color.a > 0.1){
color.a = 0;
}else{
color.a = IN.color.a;
}
return color;
}
ENDCG
}
}
}
Что нужно учесть?
1. Добавили текстуру
2. Изменили очеред и тип отрисовки — Transparent
3. Добавили Блендинг, чтобы была видна альфа и правильно смешивалась
4. Изменили фрагментный шейдер. Вообще это не совсем нужная вещь, но я сделал так, что прозрачная часть текстуры заливается цветом и альфой из цвета в UITexture, а непрозрачная часть становиться прозрачной. 0.1 — это порог отличия прозрачного от непрозрачного. Костыль короч)
5. Параметры Stencil говорят, что рисуем все пиксели, и ставим в буфер значение 2
Теперь магия — шейдер для полноэкранной UITexture
Shader "Unlit/Transparent Shadow" {
Properties
{
//_MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
}
SubShader {
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Pass {
Stencil {
Ref 2
Comp NotEqual
Pass keep
Fail keep
ZFail keep
}
Cull Off
Lighting Off
ZWrite Off
Fog { Mode Off }
Offset -1, -1
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// sampler2D _MainTex;
//float4 _MainTex_ST;
struct appdata {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
v2f o;
v2f vert(appdata v)
{
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
//o.texcoord = v.texcoord;
o.color = v.color;
return o;
}
fixed4 frag(v2f IN) : COLOR
{
return IN.color;
}
ENDCG
}
}
}
Можете заметить, что здесь текстура закоменчена, т е я просто заливаю цветом весь меш, не используя данные о пикселях из самой текстуры(да тут ее и нет).
1. Самое главное отличие — Stencil параметры. Теперь мы рисуем только те пиксели, которые не равны значению 2, т е все, кроме той области, в которой отрисовывается предыдущий меш. Круто?)
2. Цвет берется из параметра цвета в UITexture
Вот и все) Пример работы ниже. Единственное, если две маски накладываются друг на друга, то получается не оч красиво(пересечение текстур дает большую плотность и область затемняется). Фиксить думаю нужно Blend параметры. Если кто-нибудь захочет и поделиться — буду рад и благодарен!

0 комментариев