Unity: CutOut-шейдер отбрасывает неверную тень только при запекании.
Привет всем.
В юнити есть возможность задать Standard-шейдер как CutOut и все работает отлично: реалтайм и бэйкед тени нормально рисуются. Я взял стандартный поверхностный шейдер, добавил FallBack "Transparent/Cutout/VertexLit" и сделал свой Cutout шейдер:
Картинка:
+ Показать
− Скрыть
Properties {
_Metallic ("Metallic", Range(0,1)) = 0.0
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_MainTex ("Base (R Compressed BC4)", 2D) = "white" {}
_Color ("_Color", Color) = (1,1,1,1)
_BumpMap ("Mormals (RGB)", 2D) = "white" {}
_Scale ("Texture Scale", Float) = 1.0
_NormalK ("Normal koef", Float) = 1.0
_Cutoff("_Cutoff",float) = 0.5
}
SubShader {
Tags {
"Queue"="AlphaTest"
"IgnoreProjector"="True"
"RenderType"="TransparentCutout"
}
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows alphatest:_Cutoff
#pragma target 3.0
sampler2D _MainTex, _BumpMap;
float4 _MainTex_ST;
fixed4 _Color;
half _Metallic;
half _Glossiness;
float _Scale;
float _NormalK;
struct Input {
float2 uv_MainTex;
float3 worldRefl;
float3 worldNormal;
float3 worldPos;
INTERNAL_DATA
} ;
void surf (Input IN, inout SurfaceOutputStandard o){
float2 UV;
half2 faceUpFront = WorldNormalVector ( IN , float3( 0, 0, 1 ) ).yz;
if(abs(faceUpFront.x)>0.5){
UV = IN.worldPos.xz*faceUpFront.x;
}else{
if(abs(faceUpFront.y)>0.5){
UV = IN.worldPos.xy;
}else
UV = IN.worldPos.zy;
}
UV *= _Scale;
UV += _MainTex_ST.zw;
fixed c = tex2D(_MainTex, UV).r;
o.Albedo = c*_Color.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Normal = UnpackNormal (tex2D (_BumpMap, UV))*float3(_NormalK, _NormalK,1.0);
o.Alpha = c;
}
ENDCG
Pass {
Name "Caster"
Tags { "LightMode" = "ShadowCaster" }
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
half3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
};
uniform float4 _MainTex_ST;
v2f vert( appdata_base v )
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex.xyz).xyz;
return o;
}
uniform sampler2D _MainTex;
uniform fixed _Cutoff;
uniform fixed4 _Color;
uniform float _Scale;
float4 frag( v2f i ) : SV_Target
{
fixed4 texcol;
float2 UV;
if(abs(i.worldNormal.x)>0.5) {
UV = i.worldPos.yz;
} else if(abs(i.worldNormal.z)>0.5) {
UV = i.worldPos.yx;
} else {
UV = i.worldPos.xz;
}
UV *= _Scale;
UV += _MainTex_ST.zw;
texcol = tex2D( _MainTex, UV );
clip( texcol.a*_Color.a - _Cutoff );
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
Все бы хорошо, этот шейдер тоже тени отбрасывает, но мой шейдер не использует tilling и offset, эти параметры считаются в самом шейдере. В таком случае что происходит: реалтайм тень рисуется по вычисленным мною тайлингу и офсету, а вот запеченая тень рисуется по тайлингу 1,1, офсету 0,0. Хотя, если поменять значения тайла и оффсета на материале руками, например задать тайл 2,1, то запеченая тень учтет именно эти значения:
Картинка:
+ Показать
Да, ещё когда беру модель с данной текстурой, то новая оранжевая обводка юнити обводит мешь с моим материалом в соответствии с тем какие параметры тайла и оффсета выставлены в инспекторе. Как переназначить параметры тайлинга и оффсета прямо в шейдере? Почему при запекании используются знчения тайла и оффсета из _MainTex_ST?
Там вроде meta pass должен за это отвечать.
Нашёл в сети пример мета пасса, описание: https://docs.unity3d.com/530/Documentation/Manual/MetaPass.html.
Вставил пасс, поизменял цвета в фрагментной функции, вообще ничего не происходит, тень запекается согласно стандартным параметрам тайлинга. Никому не попадался пример метапасса с настройкой тайлинга и оффсета?
Вот найденный мета-пасс:
+ Показать
− Скрыть
Pass
{
Name "META"
Tags { "LightMode" = "Meta" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityMetaPass.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uvMain : TEXCOORD0;
float2 uvIllum : TEXCOORD1;
half3 worldNormal : TEXCOORD2;
float3 worldPos : TEXCOORD3;
};
float4 _MainTex_ST;
float4 _Illum_ST;
v2f vert (appdata_full v)
{
v2f o;
o.pos = UnityMetaVertexPosition(v.vertex, v.texcoord1.xy, v.texcoord2.xy, unity_LightmapST, unity_DynamicLightmapST);
o.uvMain = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uvIllum = TRANSFORM_TEX(v.texcoord, _Illum);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex.xyz).xyz;
return o;
}
sampler2D _MainTex;
sampler2D _Illum;
fixed4 _Color;
fixed _Emission;
uniform float _Scale;
half4 frag (v2f i) : SV_Target
{
UnityMetaInput metaIN;
UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN);
fixed4 tex;
float2 UV;
if(abs(i.worldNormal.x)>0.5) {
UV = i.worldPos.yz;
} else if(abs(i.worldNormal.z)>0.5) {
UV = i.worldPos.yx;
} else {
UV = i.worldPos.xz;
}
UV *= _Scale;
UV += _MainTex_ST.zw;
tex = tex2D( _MainTex, UV );
fixed4 c = tex * _Color;
metaIN.Albedo = 0;
metaIN.Emission = c.rgb * tex2D(_Illum, UV).a;
#if defined (UNITY_PASS_META)
o.Emission *= _Emission.rrr;
#endif
return UnityMetaFragment(metaIN);
}
ENDCG
}
Alerr
а галочка "static" стоит? запекаются только объекты со static, если память не подводит
bool, стоит. Даже поставил галочки ShadowCast/ShadowReceive.
Пальцем в небо: а если параметр "addshadow" добавить?
#pragma surface surf Standard fullforwardshadows alphatest:_Cutoff addshadow
У тебя в метапассе нет самого катаута.
clip( texcol.a*_Color.a - _Cutoff );
alt3d, не прокатило.
Bonus, поставил клип и ничего не происходит. Кажется метапасс вообще не исполняется при запекании.
Попробуй удали лайтмапы и перезапеки снова с новым метапассом, в котором есть катаут. Иногда такое бывает.
Bonus
> Попробуй удали лайтмапы и перезапеки снова с новым метапассом, в котором есть катаут. Иногда такое бывает.
Пробовал, даже создал отдельную сцену с парой объектов исключительно для тестов этого шейдера.
насколько я знаю, при запекании Юнити сам определяет, где в объекте дыры, где прозрачное стекло и тп. Для этого Юнити использует переменные с именами _MainTex, _Color и _Cutoff. То есть нужно просто соблюдать name conventions и все. Соответственно, при запекании Юнити использует тайл и оффсет из _MainTex_ST. Твой код в мета пассе никак повлиять на это не может, clip'ать там ничего не нужно, потому что мета пасс нужен просто чтобы указать лайтмэпперу, в какую часть lightmap'а и с каким scale'ом запекать свет для данного объекта, и цветовые свойства поверхности (albedo и emission).
arcturgray, спасибо за разъяснения.