前兩天看到一個水波的動畫圖,形如下面這樣子,突然波動方程在記憶深處就復活了。發現這個方程可以很好玩,雖然只是二維的,但還是分享給大家。
* 波動方程的推導
已知波源A的振動方程:y=Acos(ωt+φA?)。假設波沿x軸正方向傳播速率記為u,沿x軸負方向傳播速率記為V。以O點為坐標原點,A點的坐標為Xa,以水平向右為x軸的正方向建立坐標系。
當波沿x軸正方向傳播的時候,如下圖所示,在x軸正方向上任取一點P,與o點相距Xp。當得波向右傳播時,P點的振動落后于A點,落后的相位為ω (Xp-Xa)/u。可得P點在任意時刻t的位移:y=Acos[ωt-ω (Xp-Xa)/u+φa]= Acos[ωt- ω Xp/u+(φa+ ω Xa/u)]。可參考:如何求解平面簡諧波的波動方程。
得到的這個方程叫平面簡諧波動方程,形如:
y=Acos[ωt- ω Xp/u+(φa+ ω Xa/u)]
它能描述任意時刻任意點在Y軸方向上的位移y。加上一個微分的思想,取一個X+dx值,然后計算出對應的y值,將這些n個y值連成一條線就是任意時刻的波形圖。當t在流逝的時候這個波形圖不斷的在重畫,就形成了一個波動圖了!在代碼前先分析一下這個方程:y=Acos[ωt- ω Xp/u+(φa+ ω Xa/u)]
* 方程的物理意義
- y是y坐標的位置,也就是我們要計算點的y值
- A是振幅,就這個波振動幅度的大小
- ω是頻率,可以理解為是振動的快慢,ω = 2 / T(T是周期)
- t就是時間了,時間是均勻(絕對時空觀)往前走的
- u描述的是波的傳播速率,波速=波長/周期
- φa描述的波源的初相位,在圖上看就是這個點是在x軸(φa=0)上開始振動,還是在x軸上多少或下多少(cosφa)的地方開始振動。
只是為了實現這種效果,我們就利用最簡單的簡諧波了。此時:
- 波的初相為φa = 0
- 波源就在Xa處,即Xa = 0
此時波動方程就變成了:
* 最簡方程
y=Acos(ωt- ω Xp/u)
是不是簡單了好多!這樣再設兩個變量,一個是周期T和波長K,根據 “波速=波長/周期(u = K / T)” 和 ω = 2π / T。波動方程就可以推導成這個:
y = Acos2π(t/T - Xp/K)
這樣代碼的樣子就出來了,忍不住開始代碼了吧!
*代碼實現
利用Quartz2D就可以簡單實現,用一個UIView來實現,命名為WaveView(所有代碼都在WaveView.m文件里面)。
設置波的基本屬性:
@interface WaveView ()
{
CGFloat screenWidth;
float A; // 振幅
float t; // 時間變量
float T; // 周期
float K; // 波長
}
@end
在init方法里面初始化波的基本屬性值:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 設置波的基本屬性()
A = 6;
t = 0;
T = 1.0;
K = 80;
[self setBackgroundColor:[UIColor clearColor]];
// 設置刷新圖像的頻率,0.3秒人眼就很難分辨出來了
[NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:@selector(animateWave) userInfo:nil repeats:YES];
}
return self;
}
啟動動畫:
-(void)animateWave
{
// 這里是時間機器,如果和刷新圖像的時間間隔一樣,那么就是正常時間的速度
// 如果大于刷新時間間隔,那么時間就走的很快,是平常的多少倍自己去計算
// 如果小于刷新時間間隔,那么時間就走的慢
t+=0.05; // 這里比實際時間快
[self setNeedsDisplay];
動畫算法的實現:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef path = CGPathCreateMutable();
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
float y=axleXOnScreenHeight;
CGPathMoveToPoint(path, NULL, 0, y);
// 這是將x軸微分單位為1pt,就是每隔1pt計算一個y值,將所有的y值連起來就是一個波圖
// 可以將微分單位設置大一點,有不一樣的效果
// y 值的計算就用推導出來的公式:y = Acos2π(t/T - Xp/K)
for(float x=0;x <= screenWidth;x+=1){
y = A * cos(2*M_PI * (t / T - x / K)) + axleXOnScreenHeight;
CGPathAddLineToPoint(path, nil, x, y);
}
CGContextAddPath(context, path);
CGContextDrawPath(context, kCGPathStroke);
CGPathRelease(path);
}
效果如下,波長為80,屏幕寬了375剛好5個波峰的樣子:
其它好玩的效果(比如波動過程中改變振幅的大小,就可以增加波的節奏感等):
是不是挺好玩的,可惜沒有接觸過3D引擎,不然整個世界都可以波動起來。
趕緊試試!
源碼位置:https://github.com/stoull/Wave