- UID
- 24
- 积分
- 1778
- 帖子
- 99
- 主题
- 5
- 论坛币
- 3492
- 威望
- 8
- EP值
- 1453
- MP值
- 11
- 阅读权限
- 100
- 注册时间
- 2011-8-5
- 在线时间
- 137 小时
- 最后登录
- 2014-6-18
|
本帖最后由 showjim 于 2013-1-11 10:56 编辑
河亲,给我时间想想
打算写这次《AKB48 29th - 桜の花びら~前田敦子solo ver.》的制作经历吧,以后再写别的。
第一部分:均速贝塞尔曲线(Constant Bézier Curves)
1. 何为均速贝塞尔呢?
首先说说贝塞尔曲线,这里是WIKI: Bezier Curve。详细的不说,由于贝塞尔函数的变量是参数t,而且其与坐标x、y在除1阶贝塞尔的情况下都不是线性关系,如下图:
而均速贝塞尔曲线就是指曲线上的点均匀分布,如下图:
2. 计算曲线长度
这个function主要是为了解决字体随贝塞尔曲线运动而做的。使用工具是NyuFX。
为了实现这个函数,我们首先要解决的是曲线长度的问题。但由于高阶贝塞尔的公式一般都比较复杂,所以我们可以用积分来得到想要的量。实现代码如下:- function curve_len(pct, spoint, tot_step)
- local sum = 0
- for i = 1, math.floor(pct * tot_step) do
- if i==1 then
- sum = 0
- else
- sum = sum + math.sqrt((spoint[i][1]-spoint[i-1][1])^2 + (spoint[i][2]-spoint[i-1][2])^2)
- end
- end
- return sum
- end
复制代码 其中,pct代表贝塞尔变量t,spoint是所有控制点。这段代码的主要思想就是设想曲线是有n个点组成(n足够大),所有相邻点的距离之和就会无限趋近于曲线的实际长度。如下两图:
a) 分割曲线为5部分,误差较大
b) 分割曲线为8部分,误差较小
这个求长度的方法比较粗糙,但简单,在1080P以下的字幕分辨率下足够了。
3. 新的参量t'的计算
假设我们已经用N个点点描述了一条贝塞尔曲线,接下来就是如何实现点的均匀分布的问题
由于贝塞尔曲线上点的坐标轴在除一阶的情况下不与变量t是线性关系,所以为了实现均速取点,我们假设一个新的参量t',并认为t'满足以下关系:- t' = curve_len^-1[curve_len(1) * t]
复制代码 式中,curve_len代表曲线长度公式,^-1 代表取反函数。
由于直接计算任意n阶贝塞尔曲线公式的反函数比较困难,我们可以用牛顿法来取接近值,牛顿法可以参考维基百科:牛顿法
下面是Lua源码实现:- function constant_bezier(t1, spt, pts, tot_step, accuracy) --spt stands for the control points list, pts are the points on the curve, tot_step is the number of total step
- local len = curve_len(1, spt, tot_step) * t1
- local t2
- local t3
- local k
- repeat
- t2 = t1 - (curve_len(t1, spt, tot_step) - len)/DeriveSA(t1, pts)[1] --Newton's method
- t3 = t1
- if t2 <= 1 then
- t1 = t2
- else
- t1 = 1
- end
- until math.abs(t2 - t3) < accuracy
- local k = math.round(t3 * tot_step)
- return spt[k][1],spt[k][2]
- end
复制代码 4. 贝塞尔曲线函数的导函数
上一节代码中有个DeriveSA函数,这个我本意是用来计算导数的。实际上我换成了计算切向量与X轴的夹角。
任意阶贝塞尔函数的一阶导为:
式中,B代表Betnstein基函数,n代表总控制点数,i=0,1,2,…,n
而Betnstein基函数公式为:
Lua代码实现如下:- function D_bezier(pct, p)
- --Factorial
- function fac(n)
- local k = 1
- if n > 1 then
- for i=2, n do
- k = k * i
- end
- end
- return k
- end
- --Binomial coefficient
- function bin(i, n)
- return fac(n) / (fac(i) * fac(n-i))
- end
- --Bernstein polynom
- function bernstein(pct, i, n)
- return bin(i, n) * math.pow(pct,i) * math.pow((1 - pct), n - i)
- end
- local point = {0, 0}
- local n = table.maxn(p) - 1
- for i=1, n do
- local bern = bernstein(pct, i-1, n-1)
- point[1] = point[1] + (p[i+1][1] - p[i][1]) * bern * n
- point[2] = point[2] + (p[i+1][2] - p[i][2]) * bern * n
- end
- return point
- end
- function DeriveSA(pct, p)
- local D_pt = D_bezier(pct, p)
- local D_f = math.sqrt(D_pt[1]^2 + D_pt[2]^2)
- local theta = math.atan(D_pt[2]/D_pt[1])*180/math.pi
- return {D_f, theta}
- end
复制代码 下面视频是这次代码的实验效果:
以上,第一部分完结。
参考:
1. Warping Text to a Bézier curves
2. 匀速贝塞尔曲线运动的实现 |
-
2
查看全部评分
-
|