Unity 中的多层纹理着色器
Multi layer texture shader in Unity
我对着色器真的很陌生。我在不同的论坛上搜索了类似的问题,但 none 的论坛帮助我解决了我正在努力解决的问题。
基本上,我必须制作一个将 6 个纹理合并为一个的着色器。我有6层。每一层都是一个纹理,所有这些层都必须显示在彼此之上。每层纹理大小相同。
这是我现在的代码:
Shader "TileShaders/Chunk"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_SecondLayer ("Second Layer", 2D) = "white" {}
_ThirdLayer ("Third Layer", 2D) = "white" {}
_FourthLayer ("Fourth Layer", 2D) = "white" {}
_FifthLayer ("Fifth Layer", 2D) = "white" {}
_SixthLayer ("Sixth Layer", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
sampler2D _AlphaTex;
float _AlphaSplitEnabled;
fixed4 SampleSpriteTexture (float2 uv)
{
fixed4 color = tex2D (_MainTex, uv);
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
if (_AlphaSplitEnabled)
color.a = tex2D (_AlphaTex, uv).r;
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
return color;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
ENDCG
}
}
}
它是 Sprites/Default 统一着色器,我只是添加了这几个变量,因为我知道我需要这些数据才能将其显示为一个纹理:
_SecondLayer ("Second Layer", 2D) = "white" {}
_ThirdLayer ("Third Layer", 2D) = "white" {}
_FourthLayer ("Fourth Layer", 2D) = "white" {}
_FifthLayer ("Fifth Layer", 2D) = "white" {}
_SixthLayer ("Sixth Layer", 2D) = "white" {}
我就是这么做的。我真的不知道如何开始。这是我第一次使用着色器
编辑
感谢您的回复!
lerp() 帮了我很多,没有它,所有层都同时可见,看起来真的很奇怪。
现在一切看起来都很棒。但是有一个小问题。只要所有图层都包含纹理,它看起来就很棒。但如果只有一层分配了纹理而其余层没有分配,则整个结果纹理为白色。
代码现在是这样的:
fixed4 c = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 returnTexture = c;
returnTexture.rgba = lerp(c, c2, c2.a).rgba;
return returnTexture;
为了避免这个问题,我正在考虑 if 语句来检查每层上的纹理是否存在,但我听说如果可能的话我应该随时避免它。有更好的处理方式吗?
编辑 2
由于我对 post 删除的最新回答,我不知道最终帮助我的答案是否仍然是 visiblue,但这里是来自:trojanfoe
的解决方案
请编辑您的原始问题,而不是创建一个答案来扩展您的问题。不要使用 if 语句;相反,您可能想使用着色器功能并使用 #if HAVE_TEXTURE_2
等。这种方式统一将生成多个版本的着色器,每个版本都针对转向 "on" 的纹理进行定制。此外,您不需要 .rgba
swizzles,因为这是默认设置。
编辑 3
所以我尝试将所有这些图层显示在彼此之上,但我在 lerp() 方面遇到了一些问题。
fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);
fixed4 returnTexture1 = c1;
fixed4 returnTexture2 = c2;
fixed4 returnTexture3 = c3;
fixed4 returnTexture4 = c4;
fixed4 returnTexture5 = c5;
fixed4 returnTexture6 = c6;
returnTexture1 = lerp(c1, c2, c2.a);
returnTexture2 = lerp(c3, c4, c4.a);
returnTexture3 = lerp(c5, c6, c6.a);
fixed4 returnAlmostFinalTexture = lerp(returnTexture1, returnTexture2, returnTexture2.a);
fixed4 returnFinalTexture = lerp(returnTexture2, returnTexture3, returnTexture3.a);
return returnFinalTexture;
'returnAlmostFinalTexture' 正常工作,但 'returnFinalTexture' 中断。
现在我有 4 个工作层,但我无法让所有 6 个层都工作。但我很确定如果我总共有 8 层它就可以完成工作。
我可以更简单地 lerp 2 个以上的纹理吗?
编辑 4
好吧,我让它工作了,这就是它现在的样子:
fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);
fixed4 returnTexture1 = lerp(c1, c2, c2.a);
fixed4 returnTexture2 = lerp(c3, c4, c4.a);
fixed4 returnTexture3 = lerp(c5, c6, c6.a);
fixed4 returnAlmostFinalTexture = lerp(returnTexture1, returnTexture2, returnTexture2.a);
fixed4 returnFinalTexture = lerp(returnAlmostFinalTexture, returnTexture3, returnTexture3.a);
return returnFinalTexture;
这是我能做到的最好方法吗?或者我可以以某种方式 lerp 所有这些纹理 easier/more 优化方式?
您通常会乘以颜色值以合并它们,因此在片段着色器中您会执行如下操作:
fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);
return c1 * c2 * c3 * c4 * c5 * c6 * _Color;
如果您想配置每个图层对最终颜色的影响程度,您可以使用 lerp()
每个纹理的标准化输入值。
编辑:您已经扩展了您的问题,提到当纹理未分配给着色器输入时这不起作用。我对此的回应是使用 shader features,您可以在其中为每个可选纹理输入添加切换,然后使用 #if HAVE_TEXTURE_2
... #endif
。这样,Unity 将根据转换的纹理生成不同的着色器 "on",这将比使用 if (have_texture_2)
等更有效
我对着色器真的很陌生。我在不同的论坛上搜索了类似的问题,但 none 的论坛帮助我解决了我正在努力解决的问题。
基本上,我必须制作一个将 6 个纹理合并为一个的着色器。我有6层。每一层都是一个纹理,所有这些层都必须显示在彼此之上。每层纹理大小相同。
这是我现在的代码:
Shader "TileShaders/Chunk"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_SecondLayer ("Second Layer", 2D) = "white" {}
_ThirdLayer ("Third Layer", 2D) = "white" {}
_FourthLayer ("Fourth Layer", 2D) = "white" {}
_FifthLayer ("Fifth Layer", 2D) = "white" {}
_SixthLayer ("Sixth Layer", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
sampler2D _AlphaTex;
float _AlphaSplitEnabled;
fixed4 SampleSpriteTexture (float2 uv)
{
fixed4 color = tex2D (_MainTex, uv);
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
if (_AlphaSplitEnabled)
color.a = tex2D (_AlphaTex, uv).r;
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
return color;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
ENDCG
}
}
}
它是 Sprites/Default 统一着色器,我只是添加了这几个变量,因为我知道我需要这些数据才能将其显示为一个纹理:
_SecondLayer ("Second Layer", 2D) = "white" {}
_ThirdLayer ("Third Layer", 2D) = "white" {}
_FourthLayer ("Fourth Layer", 2D) = "white" {}
_FifthLayer ("Fifth Layer", 2D) = "white" {}
_SixthLayer ("Sixth Layer", 2D) = "white" {}
我就是这么做的。我真的不知道如何开始。这是我第一次使用着色器
编辑
感谢您的回复! lerp() 帮了我很多,没有它,所有层都同时可见,看起来真的很奇怪。
现在一切看起来都很棒。但是有一个小问题。只要所有图层都包含纹理,它看起来就很棒。但如果只有一层分配了纹理而其余层没有分配,则整个结果纹理为白色。
代码现在是这样的:
fixed4 c = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 returnTexture = c;
returnTexture.rgba = lerp(c, c2, c2.a).rgba;
return returnTexture;
为了避免这个问题,我正在考虑 if 语句来检查每层上的纹理是否存在,但我听说如果可能的话我应该随时避免它。有更好的处理方式吗?
编辑 2 由于我对 post 删除的最新回答,我不知道最终帮助我的答案是否仍然是 visiblue,但这里是来自:trojanfoe
的解决方案请编辑您的原始问题,而不是创建一个答案来扩展您的问题。不要使用 if 语句;相反,您可能想使用着色器功能并使用 #if HAVE_TEXTURE_2
等。这种方式统一将生成多个版本的着色器,每个版本都针对转向 "on" 的纹理进行定制。此外,您不需要 .rgba
swizzles,因为这是默认设置。
编辑 3
所以我尝试将所有这些图层显示在彼此之上,但我在 lerp() 方面遇到了一些问题。
fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);
fixed4 returnTexture1 = c1;
fixed4 returnTexture2 = c2;
fixed4 returnTexture3 = c3;
fixed4 returnTexture4 = c4;
fixed4 returnTexture5 = c5;
fixed4 returnTexture6 = c6;
returnTexture1 = lerp(c1, c2, c2.a);
returnTexture2 = lerp(c3, c4, c4.a);
returnTexture3 = lerp(c5, c6, c6.a);
fixed4 returnAlmostFinalTexture = lerp(returnTexture1, returnTexture2, returnTexture2.a);
fixed4 returnFinalTexture = lerp(returnTexture2, returnTexture3, returnTexture3.a);
return returnFinalTexture;
'returnAlmostFinalTexture' 正常工作,但 'returnFinalTexture' 中断。 现在我有 4 个工作层,但我无法让所有 6 个层都工作。但我很确定如果我总共有 8 层它就可以完成工作。
我可以更简单地 lerp 2 个以上的纹理吗?
编辑 4
好吧,我让它工作了,这就是它现在的样子:
fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);
fixed4 returnTexture1 = lerp(c1, c2, c2.a);
fixed4 returnTexture2 = lerp(c3, c4, c4.a);
fixed4 returnTexture3 = lerp(c5, c6, c6.a);
fixed4 returnAlmostFinalTexture = lerp(returnTexture1, returnTexture2, returnTexture2.a);
fixed4 returnFinalTexture = lerp(returnAlmostFinalTexture, returnTexture3, returnTexture3.a);
return returnFinalTexture;
这是我能做到的最好方法吗?或者我可以以某种方式 lerp 所有这些纹理 easier/more 优化方式?
您通常会乘以颜色值以合并它们,因此在片段着色器中您会执行如下操作:
fixed4 c1 = tex2D(_MainTex, IN.texcoord);
fixed4 c2 = tex2D(_SecondLayer, IN.texcoord);
fixed4 c3 = tex2D(_ThirdLayer, IN.texcoord);
fixed4 c4 = tex2D(_FourthLayer, IN.texcoord);
fixed4 c5 = tex2D(_FifthLayer, IN.texcoord);
fixed4 c6 = tex2D(_SixthLayer, IN.texcoord);
return c1 * c2 * c3 * c4 * c5 * c6 * _Color;
如果您想配置每个图层对最终颜色的影响程度,您可以使用 lerp()
每个纹理的标准化输入值。
编辑:您已经扩展了您的问题,提到当纹理未分配给着色器输入时这不起作用。我对此的回应是使用 shader features,您可以在其中为每个可选纹理输入添加切换,然后使用 #if HAVE_TEXTURE_2
... #endif
。这样,Unity 将根据转换的纹理生成不同的着色器 "on",这将比使用 if (have_texture_2)
等更有效