模型的动画纹理;如何编写着色器?


9

在看火箭联盟,发现那里有动画贴花和车轮。

图片

我想实现类似于上图中的效果。

我该如何编写Unity Shader来制作滚轮效果?

我对着色器了解不多,但是可以编辑标准Unity Shader来制作动画效果吗?


3
着色器绝对是此处的首选解决方案,即使对于诸如滚动纹理之类的简单操作也是如此。Unity有一个内置_Time变量,您可以在(顶点)着色器中将其添加到纹理坐标中,从而获得便宜的滚动效果。六边形效果也非常简单。如果您编辑问题以仅突出显示一种效果并询问“我将如何在Unity着色器中实现此效果”,我们可能会帮您解决。确保指定着色器是否需要对照明/阴影做出响应,因为这会影响其编写方式。
DMGregory

谢谢(你的)信息。改变了我的问题,好一点了吗?我并不是很了解光照和阴影,所以我暂时将其省略,直到我对Shaders有了更好的了解。我想我会想要它们,但是我可以只复制标准着色器中的零件,对吗?
JacketPotatoeFan

3
今晚晚些时候,我将在一个答案中对此进行扩展(如果有人想立即回答,请随意打败我!)-但您通常会编写一个将照明用作“ Surface Shader”的Unity着色器。而不需要照明的通常更容易写为“ Unlit Shader”。在我看来,那些车轮没有点亮(边缘上的轻微阴影看起来像SSAO),所以我不建议初学者使用照明。您能澄清一下吗:您是否想要这里显示的几何图案,还是想要向外滚动任意纹理并像这样对它们进行彩虹着色?
DMGregory

非常感谢,您能提供的任何信息都可以帮助我入门。我真的很佩服可以编写着色器的人,我看过几篇有关Unity Shaders的文章,是的,非常令人困惑。我认为仅滚动纹理(或纹理uvs?)就足够了,并且具有着色能力。
JacketPotatoeFan

Answers:


13

我将在几层中进行构建,以便您了解它们如何结合在一起。

首先在Unity中创建一个新的着色器,方法Create --> Shader --> Unlit是在“项目”窗口中的“资产”菜单或右键单击上下文菜单中进行选择。

在最上面的块中,我们将添加一个_ScrollSpeeds参数来控制纹理在每个方向上移动的速度:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}

这将在材质检查器中公开一个新的4分量浮点属性,其友好名称为“ Scroll Speeds”(类似于向脚本中添加publicSerialized变量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;
}

2
可以做几个小改进:1)在v方向上使用对数曲线可以帮助纹理在向外滚动时保持其形状(而不是缩小到中间的某个点,您只会得到无数个较小的副本链,直到mip会将其涂抹)2)计算您自己的纹理渐变,并使用tex2Dgrad修复过滤伪像,其中角度从-pi到+ pi(这是左侧模糊像素的细线)环绕。这是带有更多蓝色的
修饰

太棒了,也感谢您的协助。我觉得着色器是我可能永远无法写的东西(尤其是像“极地”所做的那样,数学的东西对我而言毫无意义,因为我只有非常基本的数学技能)。
JacketPotatoeFan

1
这样的人通常只会抬起头来。;)我所做的足以使它从键盘上滑下来。不过,请尝试使用不同的数学函数并查看结果-拥有这样的工具来帮助我直观地了解数学的几何形状,这是我一开始就得到练习的方式。(不是专门用于着色器的,而是一个旧的WinAmp可视化工具WhiteCap ...)即使着色器数学非常错误,但查看它通常也很酷。:D
DMGregory
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.