本文主要关注的是公式的推导。
在讲贝塞尔曲线之前先复习下组合数学。
组合数学
排列 permutation
注意,排列的英文是permutation,这个词也就是线性代数里的“置换”。联想置换矩阵的概念,就可以近似理解“排列”的意义。
permutation的公式是:
含义:从n个数取出某k个数总共有多少种排列。
所以,排列是有顺序的。
组合 combination
组合这个东西可以用“排列”来理解,比如对于某3个不同的数a、b、c,有
所以,组合是没有顺序的。
combination的公式是:
性质1:
性质2:
(当k<=0或k>n时,右边的式子会出现负数的阶乘。负数阶乘在组合数公式的计算中,可以认为等于0)
combination很重要,比如说二项式定理里的展开式就用到了它:
combination还有一条公式要注意下:
顺便给个简单证明:
贝塞尔曲线
定义
给定n个控制点
其中的
Bézier曲线的特性
特性1:改变单个控制点会引起整条曲线的改变。
Bernstein polynomial的特性
递归性
证明:
归一性
证明:
根据二项式定理有:
Partition of Unity
证明:
利用递归公式,有:
因为:
(这里利用了上文提到的组合数公式性质2)
所以可简化为:
对称性
证明:
由定义:
有:
得证。
非负性
当 t = 0 时:
当 t = 1 时:
当 t= (0,1) 时:
证明:把数值代入定义公式就可以了。
Bernstein基(Bernstein Basis)到幂基(Power Basis)的转换
由二项式定理:
得到:
所以:
这时设g = k + i ,则有 k = g - i,i = g - k,上式变成:
把g换成k,上式变成:
其中
所以:
综上:
设
则上式变成:
展开后:
Bézier曲线的递推形式(de Casteljau算法)
前面讲的是Bézier曲线的曲线方程定义,现在介绍一个简单实用的算法:de Casteljau's Algorithm。
先分享我找到的一些演示程序:
http://myst729.github.io/bezier-curve/。
https://www.jasondavies.com/animated-bezier/。
以及油管上的:https://www.youtube.com/watch?v=YATikPP2q70。
递推公式如下:
以上图为例演示下这条公式:
因为有
然后求该贝塞尔曲线在 t = 1/2时的坐标点B(1/2)的步骤如下:
k = 0时,
k = 1时,
k = 2时,
k = 3时,
Bézier曲线的矩阵形式
由上上一节推导出来的2条式子:
可以推导出矩阵:
再由Bézier曲线的公式:
有:
注意,中间的矩阵B是常量(取决于阶数):
n = 2时:
n = 3时:
测试一下正确性
测试代码基于我正在开发中的renderer
#include "transform.hpp"
#include "geometry.hpp"
using namespace renderer;
int main(int argc, char ** argv){
Matrix3x3 P = {
20.f, 20.f, 0, //P0
770.f, 30.f, 0, //P1
400.f, 780.f, 0, //P2
};
Matrix3x3 B = {
1, 0, 0,
-2, 2, 0,
1, -2, 1,
};
Matrix3x3 BP = B * P;
typedef Matrix<MxN<float, 1, 3>> Matrix1x3;
cil::CImg<unsigned char> img(800, 800, 1, 3);
img.atXYZC(P[0], P[1], 0, 1) = 255;
img.atXYZC(P[3], P[4], 0, 1) = 255;
img.atXYZC(P[6], P[7], 0, 1) = 255;
for (float t = 0; t < 1; t += 0.0001f) {
Matrix1x3 T = { 1, t, t * t };
Matrix1x3 TBP = T * BP;
int x = int(TBP[0]);
int y = int(TBP[1]);
img.atXYZC(x, y, 0, 0) = 255;
}
img.display("");
return 0;
}
写作不易,您的支持是我写作的动力!

