#unity3d #shader #hlsl #cg
#unity3d #шейдер #hlsl #cg
Вопрос:
Я пытаюсь создать шейдер деколи для использования с проектором в Unity. Вот что я собрал:
Shader "Custom/color_projector"
{
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
_MainTex ("Cookie", 2D) = "gray" {}
}
Subshader {
Tags {"Queue"="Transparent"}
Pass {
ZTest Less
ColorMask RGB
Blend One OneMinusSrcAlpha
Offset -1, -1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 uvShadow : TEXCOORD0;
float4 pos : SV_POSITION;
};
float4x4 unity_Projector;
float4x4 unity_ProjectorClip;
v2f vert (float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos (vertex);
o.uvShadow = mul (unity_Projector, vertex);
return o;
}
sampler2D _MainTex;
fixed4 _Color;
fixed4 frag (v2f i) : SV_Target
{
fixed4 tex = tex2Dproj (_MainTex, UNITY_PROJ_COORD(i.uvShadow));
return _Color * tex.a;
}
ENDCG
}
}
}
Это хорошо работает в большинстве ситуаций:
Однако всякий раз, когда он проецируется на прозрачную поверхность (или несколько поверхностей), кажется, что для каждой поверхности требуется дополнительное время. Здесь я разделил траву и дорожное покрытие, используя текстуры травы с прозрачными областями:
Я перепробовал множество вариантов наложения и все варианты ZTesting. Это лучшее, что я могу заставить его выглядеть.
Из чтения я понял, что это может быть связано с тем, что прозрачный шейдер не записывает данные в буфер глубины. Я попытался добавить ZWrite On
и попытался выполнить проход перед основным проходом:
Pass {
ZWrite On
ColorMask 0
}
Но ни то, ни другое не имело никакого эффекта вообще.
Как можно изменить этот шейдер, чтобы он проецировал текстуру только один раз на ближайшие геометрии?
Желаемый результат (фотошоп):
Ответ №1:
Проблема связана с тем, как работают проекторы. По сути, они повторно отображают все сетки в пределах своего поля зрения, за исключением другого шейдера. В вашем случае это означает, что и земля, и плоскость с травой будут отображаться дважды и накладываться друг на друга. Я думаю, что это можно было бы исправить с помощью двух шагов;
Сначала добавьте следующее к тегам прозрачного (grass) шейдера:
"IgnoreProjector"="True"
Затем измените очередь рендеринга вашего проектора с «Прозрачного» на «Прозрачный 1». Это означает, что сначала будет отображаться земля, затем края травы, и, наконец, проектор будет проецироваться на землю (за исключением появления сверху, поскольку он отображается последним).
Что касается смешивания, я думаю, вам нужно обычное альфа-смешивание:
Blend SrcAlpha OneMinusSrcAlpha
Другой вариант, если вы используете отложенный рендеринг, — использовать отложенные деколи. Они дешевле и обычно проще в использовании, чем проекторы.
Комментарии:
1. Сработало отлично, спасибо. Я еще раз проверю отложенные надписи, хотя я только бегло взглянул, когда Unity впервые выпустила его.