在看火箭联盟,发现那里有动画贴花和车轮。
。
我想实现类似于上图中的效果。
我该如何编写Unity Shader来制作滚轮效果?
我对着色器了解不多,但是可以编辑标准Unity Shader来制作动画效果吗?
在看火箭联盟,发现那里有动画贴花和车轮。
。
我想实现类似于上图中的效果。
我该如何编写Unity Shader来制作滚轮效果?
我对着色器了解不多,但是可以编辑标准Unity Shader来制作动画效果吗?
Answers:
我将在几层中进行构建,以便您了解它们如何结合在一起。
首先在Unity中创建一个新的着色器,方法Create --> Shader --> Unlit
是在“项目”窗口中的“资产”菜单或右键单击上下文菜单中进行选择。
在最上面的块中,我们将添加一个_ScrollSpeeds
参数来控制纹理在每个方向上移动的速度:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}
这将在材质检查器中公开一个新的4分量浮点属性,其友好名称为“ Scroll Speeds”(类似于向脚本中添加public
或Serialized
变量MonoBehaviour
)
接下来,我们将在顶点着色器中使用此变量来移动纹理坐标(o.uv
),方法是在默认着色器中仅添加两行:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our new parameter here so it's visible to the CG shader
float4 _ScrollSpeeds;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Shift the uvs over time.
o.uv += _ScrollSpeeds * _Time.x;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
在四边形上拍一下(肯尼(Kenney)拥有可爱的免费长颈鹿纹理),您将获得:
为了使纹理在一个环中向外滚动,我们可以使用像蜘蛛网一样细分的网格,其中uv v坐标从中心向外增加。但这将自行产生一些锯齿状的文物。相反,我将展示如何计算每个片段的UV。
这既昂贵又昂贵,这是因为触发和长度运算(比基本数学运算要昂贵),并且因为在计算每个片段的texcoords时,与仅对它们进行插值相比,在硬件中预测和缓存纹理数据效率不高顶点之间。但是对于这样的特殊效果,这并不算过多。
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// Shift the UVs so (0, 0) is in the middle of the quad.
o.uv = v.uv - 0.5f;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// Convert our texture coordinates to polar form:
float2 polar = float2(
atan2(i.uv.y, i.uv.x)/(2.0f * 3.141592653589f), // angle
length(i.uv) // radius
);
// Apply texture scale
polar *= _MainTex_ST.xy;
// Scroll the texture over time.
polar += _ScrollSpeeds.xy * _Time.x;
// Sample using the polar coordinates, instead of the original uvs.
// Here I multiply by MainTex
fixed4 col = tex2D(_MainTex, polar);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
这给了我们类似的东西(这里我增加了材质中的平铺参数,这样更清楚了发生了什么—仅将瓷砖的单个重复包裹在圆圈上看起来是扭曲和怪异的)
最后,要通过滚动渐变为纹理着色,我们只需将渐变添加为第二个纹理并将它们相乘即可。
首先,我们在顶部添加新的纹理参数:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TintTex("Tint Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}
并在我们的CGPROGRAM块中声明它,以便CG着色器可以看到它:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;
float4 _ScrollSpeeds;
然后更新我们的片段着色器以同时使用两种纹理:
fixed4 frag(v2f i) : SV_Target
{
float2 polar = float2(
atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
length(i.uv) // radius
);
// Copy the polar coordinates before we scale & shift them,
// so we can scale & shift the tint texture independently.
float2 tintUVs = polar * _TintTex_ST.xy;
tintUVs += _ScrollSpeeds.zw * _Time.x;
polar *= _MainTex_ST.xy;
polar += _ScrollSpeeds.xy * _Time.x;
fixed4 col = tex2D(_MainTex, polar);
// Tint the colour by our second texture.
col *= tex2D(_TintTex, tintUVs);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
现在,我们的长颈鹿变得非常迷幻:
通过对纹理和滚动速率进行更具艺术性的选择,可以创建与问题所示效果非常相似的效果。
您可能会注意到上面显示的版本中的两个小工件:
圆心附近的脸部被拉伸/变窄/变尖,然后朝外侧移动时,它们被挤压/变宽。
之所以会发生这种扭曲,是因为我们在周长上有固定数量的面,但是随着半径的增加,它们所跨越的周长会变宽,而其高度却保持不变。
我们可以通过重新映射纹理样本的垂直分量以遵循对数曲线的方式来解决此问题,因此,随着半径的增加,纹理的重复部分之间的距离会越来越远,并且朝中心的方向越来越近。(实际上,这使我们不断缩小的长颈鹿数量无限减少...)
方形的左中角有一排一到两个模糊像素。
发生这种情况是因为GPU会查看两个相邻的纹理样本坐标以找出要使用的过滤。当样品靠在一起时,它将显示出纹理正以较大/紧密显示,并显示最详细的Mip级别。当样本相距很远时,它猜测我们必须以很小的缩放或很远的距离显示纹理,并且它从更小/更模糊的mipmap中进行采样,以确保不会产生闪烁的锯齿失真。
问题出在这里,我们在极坐标中的环绕点(从-180到180度)。因此,实际上我们是在重复的纹理空间中从非常相似的点采样,即使它们的数字坐标使它们看起来相距甚远。因此,我们可以提供自己的采样梯度矢量来对此进行更正。
这是经过更正的版本:
fixed4 frag(v2f i) : SV_Target
{
float2 polar = float2(
atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
log(dot(i.uv, i.uv)) * 0.5f // log-radius
);
// Check how much our texture sampling point changes between
// neighbouring pixels to the sides (ddx) and above/below (ddy)
float4 gradient = float4(ddx(polar), ddy(polar));
// If our angle wraps around between adjacent samples,
// discard one full rotation from its value and keep the fraction.
gradient.xz = frac(gradient.xz + 1.5f) - 0.5f;
// Copy the polar coordinates before we scale & shift them,
// so we can scale & shift the tint texture independently.
float2 tintUVs = polar * _TintTex_ST.xy;
tintUVs += _ScrollSpeeds.zw * _Time.x;
polar *= _MainTex_ST.xy;
polar += _ScrollSpeeds.xy * _Time.x;
// Sample with our custom gradients.
fixed4 col = tex2Dgrad(_MainTex, polar,
_MainTex_ST.xy * gradient.xy,
_MainTex_ST.xy * gradient.zw
);
// Since our tint texture has its own scale,
// its gradients also need to be scaled to match.
col *= tex2Dgrad(_TintTex, tintUVs,
_TintTex_ST.xy * gradient.xy,
_TintTex_ST.xy * gradient.zw
);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
_Time
变量,您可以在(顶点)着色器中将其添加到纹理坐标中,从而获得便宜的滚动效果。六边形效果也非常简单。如果您编辑问题以仅突出显示一种效果并询问“我将如何在Unity着色器中实现此效果”,我们可能会帮您解决。确保指定着色器是否需要对照明/阴影做出响应,因为这会影响其编写方式。