虚幻4渲染编程(UI篇)【第二卷:程序化UI特效-[1]】

MY BLOG DIRECTORY:

小IVan:专题概述及目录​zhuanlan.zhihu.com图标

INTRODUCTION:

当遇见某些特殊需求,比如对游戏效果有很多变化的要求,这时使用静态的贴图就不太适合了,这时候就需要实时计算绘制出来的数据。下面就来总结一些常用的程序化UI效果,下面的效果将全部使用公式计算的方式生成,不使用一张贴图。


MAIN CONTENT:

【1】律动波形

v2-317773f6929bd55a90d0e5e346bde0ce_b.gif
v2-b4ed86effa7f88394c1c55434061355b_b.jpg

使用 y = asin(bx + c) + d 这个简单的公式,把图像画出来

v2-23cd690a7f39cbbad8d66e41071a6206_b.gif

然后重复一次相减即可得到一条细线。然后再重叠第二个波上去,即可得到更有变化的效果。我这里只叠加了两个波,如果还想更丰富还可以继续往上叠。

上面的材质连线是不是太乱了呢,而且如果想做更复杂的叠波就比较困难了,材质编辑器能把人头连晕,所以我下面封装了一个框架来帮助我解决这个事情。

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);
v2-525516d54de51d7c2839b0e8a58a90e3_b.jpg

最后的效果:

v2-239190e2dc07efc0146ace94ab06da91_b.gif

我这里只叠了三层,其实还可以叠更多。我来稍微讲解下这个框架。首先是要计算出波场,上波场和下波场,上下波场相减就能得到波的细线。GetWaveColor便是相减上下波场。我们如果要混合不同波,只能在计算波场的时候混合,在GetWaveColor之前。计算波场有很多种混合方式,相加相减相乘等。


把自变量x变成整数就可以得到柱状波形

v2-050d6a57304846cd6b0fa37c0b62349f_b.jpg
v2-5c152269bee1e1fd93a92fda1c9c5b2c_b.gif
v2-eaaad36599f3b91d7c766f053fd308c5_b.jpg

如果不和Y的绝对值比较的话,可以得到一边的效果

v2-e13536f5a991e29529dcbc7cbbd8df89_b.gif


【2】技能冷却

v2-578b2b10be88ab5cbf2902ff4bd1ed1e_b.gif
v2-982f1b3bdbab92c705e04054bf792f75_b.jpg

先用代码画个圆然后画冷却线

step(distance(uv, float2(0.5, 0.5)) , R);

冷却线

step(H, uv.y );

如果想要制作扇形效果,可以使用极坐标的方法,先把UV变换到中心点

v2-8cf438a980b72838b0f6a8b1fde99d15_b.jpg

然后用极坐标判断即可

v2-5726f6788388b6552b8c04758c76b469_b.gif
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】旋转扭曲

使用旋转矩阵变换一下就好了,还是非常简单的

v2-b743f4c4b6b3e6cb00abba5157021a42_b.gif
请无视gif压缩产生的马赛克
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;
}

有了最基础的旋转,就可以在此基础上做些其它效果了

v2-20c26e8be1d497d29b363bb59b29708e_b.gif

代码如下:

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

v2-9e6f50d0c890e54e757ad5a861d3e760_b.gif
v2-0890053568a52e7d6d21cb93e4373bfd_b.jpg
把水印P掉即可使用

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方向是相同的

v2-dd1cecb3de228529d149e5c41f569311_b.jpg
 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时候的图像和当前图像做插值,这样来做帧缓存让图像更加顺滑。

v2-75b1dff047fe6cc64344052aef78414e_b.jpg



SUMMARY AND OUTLOOK:

Enjoy it!


NEXT:

YivanLee:虚幻4渲染编程(UI篇)【第三卷:程序化UI特效-[2]】

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值