简单修改Shader实现“墙着火”的效果
准备工作
首先,我们需要准备一个用于显示霓虹几何体的Shader。这个Shader会使用两张不同的贴图,一张用于表现白色霓虹中心,另一张用于呈现围绕中心的发光效果。
以下是Shader的代码:
fixed4 frag_mult(v2f_vct i) : COLOR
{
fixed4 result = tex2D(_MainTex, i.texcoord) * i.color;
return result;
}
该Shader代码实现了基本的纹理采样和颜色混合,其效果如下图所示(此处应插入对应效果图片)。
垂直偏移
接下来,我们要对纹理的查找位置进行简单的垂直偏移。通过修改纹理坐标,我们可以实现这一效果。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(0, .05);
fixed4 result = tex2D(_MainTex, displacedTexCoord) * i.color;
return result;
上述代码中,我们将纹理坐标在y轴方向上偏移了0.05。虽然此效果可能不太理想,但它有助于我们理解纹理在垂直方向上的偏移原理。这里的瓷砖地图的瓷砖贴图来自精灵表单(Spritesheet)中的不同精灵(Sprite)。
垂直移动
为了让纹理动起来,我们可以使用Unity提供给Shader的_Time组件。这是实现纹理移动最简便的方法。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(0, cos(_Time.x * 50))/20;
在这个代码中,有两个关键值可以进行调整。其中,50影响循环的进行方式,而20则与摆动宽度相关。
以下是不同参数设置下的示例:
- 使用float2(0, cos(_Time.x * 25))/20可形成缓慢移动的效果。
- 使用float2(0, cos(_Time.x * 50))/40可形成不那么宽幅移动的效果。
全移动
我们不仅可以在y轴上实现移动,还可以在x轴上添加移动效果。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(sin(_Time.x * 50), cos(_Time.x * 50))/40;
通过上述代码,纹理在x轴和y轴上都会产生移动效果。
静态随机性
为了给效果增添一些随机性,我们可以引入噪声贴图。在Shader代码中直接添加随机数比较麻烦,因此使用噪声贴图是最简单的方法。
Perlin noise(柏林噪声)算法生成的图像很容易找到。我们将其添加到Shader的一个属性中并进行设置,就可以使用了。不过,在使用过程中可能会遇到奇怪的截断线问题,这是由于使用非无缝柏林噪音贴图造成的。只要找到一张无缝的贴图,这些问题就可以得到解决。
以下是未添加移动效果时的代码:
float2 displacedTexCoord = i.texcoord + float2(tex2D(_NoiseTex, i.texcoord).xy)/20;
代码末尾的20控制着偏移大小。若将其改为40,锯齿会相对变小。
动画化的随机性
为了让效果更加生动,我们可以添加随机动画。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(tex2D(_NoiseTex, i.texcoord + float2(_SinTime.w, _CosTime.w)/10).xy)/25;
然而,仔细观察会发现,动画的方向会循环变换,垂直的墙在上下变化,水平的墙在左右变化。这个问题我们将在后面解决。此外,还存在一个隐藏问题,由于当前使用的值,可能不太容易发现。仔细观察图像,会发现其具有重复性,并且在瓷砖边缘会出现凸出的小点。
无缝随机性
在之前的实现中,噪声贴图的查找是基于原始贴图的位置,这会导致相邻且相同的瓷砖具有完全相同的噪声。为了解决这个问题,我们需要基于瓷砖的绝对位置来进行查找。
首先,我们需要在vertex方法中添加以下代码:
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
然后,我们可以按照以下方式修改代码:
float2 displacedTexCoord = i.texcoord + float2(tex2D(_NoiseTex, i.vertex.xy/300 + float2(_SinTime.w, _CosTime.w)/10).xy)/20;
通过使用i.vertex.xy,我们确保了输出中相邻的像素会从噪声贴图中抓取相邻的位置,从而解决了“平铺”问题。但这也使得循环移动的问题更加明显。
无缝无方向的随机性
为了解决动画的方向性问题,我们可以进行直线偏移。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(tex2D(_NoiseTex, i.vertex.xy/300 + float2((_Time.w%50)/50, (_Time.w%50)/50)).xy)/20;
在上述代码中,(_Time.w%50)/50的技巧是为了将_Time.w(一个持续增加的值)约束在0 - 1的范围内。此时,动画的方向将一直指向左上角。
准确定位问题
为了更准确地解释解决方案,我们先去掉x轴的偏移。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(0, tex2D(_NoiseTex, i.vertex.xy/300 + float2((_Time.w%50)/50, (_Time.w%50)/50)).z)/20;
在这个代码中,我们在x轴上做了0偏移,而对.z分量使用了在噪声贴图上按y轴的查找。选择.z是为了减少混乱,实际上我们只是从查找的噪声像素中选择了一个颜色通道。此时,垂直方向是静止的。
我们可以通过一个类比来理解这个问题。假设有一个很宽但不高的信箱口(例如1英尺宽,0.1英尺高),我们通过这个口观察另一边挂着的扎染T恤(随机图案)的移动。当T恤自左向右移动时,我们能明显看到其方向性,因为之前看到的画面框架还有一部分在视觉中作为参考;而当T恤上下移动时,所有东西看起来就是随机的。
以下是运行中的画面对比:全移动的图像和通过一个很小的视口观察的效果(此处应插入对应对比图片)。可以看到,左图的侧向移动有明显规则,而右图的垂直运动看起来却是随机的。
解决问题
回到我们的Shader问题,我们需要让贴图在垂直方向也能产生波动。我们可以使用与x轴相同的原则来实现。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(tex2D(_NoiseTex, i.vertex.xy/300 + float2((_Time.w%50)/50, 0)).z, tex2D(_NoiseTex, i.vertex.xy/300 + float2(0, (_Time.w%50)/50)).z)/20;
一个小修正
为了让偏移更加对称,我们需要对偏移值进行修正。由于我们使用的是.z分量(蓝色通道),其值范围在0 - 1之间。我们将其减去0.5,使其范围变为 - 0.5 - 0.5,这样偏移就会居中。
代码如下:
float2 displacedTexCoord = i.texcoord + float2(tex2D(_NoiseTex, i.vertex.xy/300 + float2((_Time.w%50)/50, 0)).z - .5, tex2D(_NoiseTex, i.vertex.xy/300 + float2(0, (_Time.w%50)/50)).z - .5)/20;
结合文章开头提到的霓虹效果,整个效果就完整了。
最终结果
最后,我们可以对之前提到的一些参数进行细微调节,并添加一些背景(使用同一个Shader的修改版本),这样就大功告成了。
文章来源于腾讯gad游戏开发平台。
