MY BLOG DIRECTORY:
小IVan:专题概述及目录INTRODUCTION:
当遇见某些特殊需求,比如对游戏效果有很多变化的要求,这时使用静态的贴图就不太适合了,这时候就需要实时计算绘制出来的数据。下面就来总结一些常用的程序化UI效果,下面的效果将全部使用公式计算的方式生成,不使用一张贴图。
MAIN CONTENT:
【1】律动波形
使用 这个简单的公式,把图像画出来
然后重复一次相减即可得到一条细线。然后再重叠第二个波上去,即可得到更有变化的效果。我这里只叠加了两个波,如果还想更丰富还可以继续往上叠。
上面的材质连线是不是太乱了呢,而且如果想做更复杂的叠波就比较困难了,材质编辑器能把人头连晕,所以我下面封装了一个框架来帮助我解决这个事情。
void GetWaveFeild(
out float Up,
out float Down,
in float A,
in float T,
in float WaveWidth,
in float2 UV,
in float time
)
{
float TwoPI = 3.1415926 * 2;
Up = A * sin(TwoPI / T * UV.x + time);
Down = A * sin(TwoPI / T * UV.x + time) + WaveWidth;
}
float GetWaveColor(in float FeildUp, in float FeildDown, in float UVY)
{
UVY = UVY - 0.5;
return step(UVY, FeildDown) - step(UVY, FeildUp);
}
float CalculateWave(in float2 UV, float A, float T, float WaveWidth, in float time)
{
float Output = 0;
float WaveFeildUp = 0;
float WaveFeildDown = 0;
// If you need more wave feild. Adding them here!
float WaveUp_01 = 0;
float WaveDown_01 = 0;
GetWaveFeild(WaveUp_01, WaveDown_01, A, T, WaveWidth, UV, time);
float WaveUp_02 = 0;
float WaveDown_02 = 0;
GetWaveFeild(WaveUp_02, WaveDown_02, A, T * 2, WaveWidth, UV, time * -1.2);
float WaveUp_03 = 0;
float WaveDown_03 = 0;
GetWaveFeild(WaveUp_03, WaveDown_03, A * 5, T * 0.2, WaveWidth, UV, time * 8);
//Wave field blend operations
WaveFeildUp = WaveUp_01 + WaveUp_02 * WaveUp_03 + WaveUp_03 * 0.1;
WaveFeildDown = WaveDown_01 + WaveDown_02 * WaveDown_03 + WaveDown_03 * 0.1;
Output = GetWaveColor(WaveFeildUp, WaveFeildDown, UV.y);
return Output;
}
float RenderWave(in float2 UV, in float t)
{
float Output = 0;
Output = CalculateWave(UV, 0.1, 0.2, 0.02, t * 2);
return Output;
}
//return RenderWave(UV, t);
最后的效果:
我这里只叠了三层,其实还可以叠更多。我来稍微讲解下这个框架。首先是要计算出波场,上波场和下波场,上下波场相减就能得到波的细线。GetWaveColor便是相减上下波场。我们如果要混合不同波,只能在计算波场的时候混合,在GetWaveColor之前。计算波场有很多种混合方式,相加相减相乘等。
把自变量x变成整数就可以得到柱状波形
如果不和Y的绝对值比较的话,可以得到一边的效果
【2】技能冷却
先用代码画个圆然后画冷却线
圆
step(distance(uv, float2(0.5, 0.5)) , R);
冷却线
step(H, uv.y );
如果想要制作扇形效果,可以使用极坐标的方法,先把UV变换到中心点
然后用极坐标判断即可
float SkillTime(float2 uv, float R, float Radius)
{
float2 centeruv = uv - 0.5f;
float cos = dot(normalize(centeruv), float2(1,0));
return step(Radius, cos);
}
【3】旋转扭曲
使用旋转矩阵变换一下就好了,还是非常简单的
float2 RotateUV(float2 uv,float2 center, float angle)
{
float PI = 3.1415927f;
float Ang = angle * (2.0f * PI/ 360.0f);
float2 RowX = float2(cos(Ang), -sin(Ang));
float2 RowY = float2(sin(Ang), cos(Ang));
float ArgOne = dot(RowX, uv - center);
float ArgTwo = dot(RowY, uv - center);
return float2(ArgOne, ArgTwo) + center;
}
有了最基础的旋转,就可以在此基础上做些其它效果了
代码如下:
float2 Twirl(float2 uv, float2 centeroffset, float strenth)
{
//Transform uv to center
uv = uv - float2(0.5, 0.5);
//Offset the uv center
float2 delta = uv - centeroffset;
float angle = strenth * length(delta);
float x = cos(angle) * delta.x - sin(angle) * delta.y;
float y = sin(angle) * delta.x + cos(angle) * delta.y;
float RowX = x + centeroffset.x;
float RowY = y + centeroffset.y;
return float2(RowX, RowY);
}
【4】SubUVAnimation
SubUVAnimation其实是经常会使用到的东西,原理非常简单,就是整块整块挪动UV,虽然很简单但是很容易出错。
float2 SubUV(float t, float2 coord)
{
float SubUVX = 4;
float SubUVY = 4;
float Time = floor(t);
float row = floor(Time / SubUVX);
float cloum = Time + row * SubUVX;
float2 UV = coord + float2(cloum, row);
UV.x /= SubUVX;
UV.y /= SubUVY;
return UV;
}
首先需要确保UV的方向和贴图的Animation方向是相同的
float row = floor(Time / SubUVX);
float cloum = Time + row * SubUVX;
Cloum的数量是Row的横轴格数的倍数加当前横轴位置。
这里的UV偏移是非常大的,需要把UV限制到0-1所以除以X和Y的SubUV数目,这里一定是最后缩放到0-1,因为如果先缩放到0-1会产生累积误差。最后可以再采一次time+1时候的图像和当前图像做插值,这样来做帧缓存让图像更加顺滑。
SUMMARY AND OUTLOOK:
Enjoy it!