双三次贝塞尔曲线的定义
双三次贝塞尔曲面是由16个控制点定义的曲面,通常表示为4x4矩阵。
曲面的公式如下:
p ( u , v ) = ∑ i = 0 3 ∑ j = 0 3 P i , j B i , 3 ( u ) B j , 3 ( v ) , ( u , v ) ∈ [ 0 , 1 ] × [ 0 , 1 ] p(u,v)=\sum_{i=0}^3\sum_{j=0}^3P_{i,j}B_{i,3}(u)B_{j,3}(v),\\(u,v)\in[0,1]\times[0,1] p(u,v)=i=0∑3j=0∑3Pi,jBi,3(u)Bj,3(v),(u,v)∈[0,1]×[0,1]
其中,
P
i
,
j
,
i
,
j
=
0
,
1
,
2
,
3
P_{i,j},i,j=0,1,2,3
Pi,j,i,j=0,1,2,3是曲面上的16控制点;
B
i
,
3
(
u
)
和
B
j
,
3
(
v
)
B_{i, 3} ( u )和 B_{j,3}( v )
Bi,3(u)和Bj,3(v)是三次Bernstein基函数,定义如下:
{
B
0
,
3
(
u
)
=
(
1
−
u
)
3
B
1
,
3
(
u
)
=
3
u
(
1
−
u
)
2
B
2
,
3
(
u
)
=
3
u
2
(
1
−
u
)
B
3
,
3
(
u
)
=
u
3
{
B
0
,
3
(
v
)
=
(
1
−
v
)
3
B
1
,
3
(
v
)
=
3
v
(
1
−
v
)
2
B
2
,
3
(
v
)
=
3
v
2
(
1
−
v
)
B
3
,
3
(
v
)
=
v
3
\begin{cases} B_{0,3}(u)=(1-u)^3\\ B_{1,3}(u)=3u(1-u)^2\\ B_{2,3}(u)=3u^2(1-u)\\ B_{3,3}(u)=u^3 \end{cases}\begin{cases} B_{0,3}(v)=(1-v)^3\\ B_{1,3}(v)=3v(1-v)^2\\ B_{2,3}(v)=3v^2(1-v)\\ B_{3,3}(v)=v^3 \end{cases}
⎩
⎨
⎧B0,3(u)=(1−u)3B1,3(u)=3u(1−u)2B2,3(u)=3u2(1−u)B3,3(u)=u3⎩
⎨
⎧B0,3(v)=(1−v)3B1,3(v)=3v(1−v)2B2,3(v)=3v2(1−v)B3,3(v)=v3
用矩阵表示公式如下
p
(
u
,
v
)
=
(
B
0
,
3
(
u
)
B
1
,
3
(
u
)
B
2
,
3
(
u
)
B
3
,
3
(
u
)
)
(
P
00
P
01
P
02
P
03
P
10
P
11
P
12
P
13
P
20
P
21
P
22
P
23
P
30
P
31
P
32
P
33
)
(
B
0
,
3
(
v
)
B
0
,
1
(
v
)
B
0
,
2
(
v
)
B
0
,
3
(
v
)
)
p(u,v)=\begin{pmatrix}B_{0,3}(u)&B_{1,3}(u)&B_{2,3}(u)&B_{3,3}(u) \end{pmatrix}\begin{pmatrix} P_{00}&P_{01}&P_{02}&P_{03}\\ P_{10}&P_{11}&P_{12}&P_{13}\\ P_{20}&P_{21}&P_{22}&P_{23}\\ P_{30}&P_{31}&P_{32}&P_{33}\end{pmatrix}\begin{pmatrix} B_{0,3}(v)&B_{0,1}(v)&B_{0,2}(v)&B_{0,3}(v) \end{pmatrix}
p(u,v)=(B0,3(u)B1,3(u)B2,3(u)B3,3(u))
P00P10P20P30P01P11P21P31P02P12P22P32P03P13P23P33
(B0,3(v)B0,1(v)B0,2(v)B0,3(v))
将Bernstein基数带入得到如下公式
p
(
u
,
v
)
=
(
u
3
u
2
u
1
)
(
−
1
3
−
3
1
3
−
6
3
0
−
3
3
0
0
1
0
0
0
)
(
P
00
P
01
P
02
P
03
P
10
P
11
P
12
P
13
P
20
P
21
P
22
P
23
P
30
P
31
P
32
P
33
)
(
−
1
3
−
3
1
3
−
6
3
0
−
3
3
0
0
1
0
0
0
)
(
v
3
v
2
v
1
)
p(u,v)=\begin{pmatrix}u^3&u^2&u&1\end{pmatrix}\begin{pmatrix} -1&3&-3&1\\3&-6&3&0\\-3&3&0&0\\1&0&0&0 \end{pmatrix}\begin{pmatrix} P_{00}&P_{01}&P_{02}&P_{03}\\ P_{10}&P_{11}&P_{12}&P_{13}\\ P_{20}&P_{21}&P_{22}&P_{23}\\ P_{30}&P_{31}&P_{32}&P_{33}\end{pmatrix}\begin{pmatrix} -1&3&-3&1\\3&-6&3&0\\-3&3&0&0\\1&0&0&0 \end{pmatrix}\begin{pmatrix}v^3\\v^2\\v\\1\end{pmatrix}
p(u,v)=(u3u2u1)
−13−313−630−33001000
P00P10P20P30P01P11P21P31P02P12P22P32P03P13P23P33
−13−313−630−33001000
v3v2v1
令
U
=
(
u
3
u
2
u
1
)
,
V
=
(
v
3
v
2
v
1
)
M
=
(
−
1
3
−
3
1
3
−
6
3
0
−
3
3
0
0
1
0
0
0
)
,
P
=
(
P
00
P
01
P
02
P
03
P
10
P
11
P
12
P
13
P
20
P
21
P
22
P
23
P
30
P
31
P
32
P
33
)
那么
p
(
u
,
v
)
=
U
M
P
M
T
V
T
令U=\begin{pmatrix}u^3&u^2&u&1\end{pmatrix},V=\begin{pmatrix}v^3&v^2&v&1\end{pmatrix}\\M=\begin{pmatrix} -1&3&-3&1\\3&-6&3&0\\-3&3&0&0\\1&0&0&0 \end{pmatrix},P=\begin{pmatrix} P_{00}&P_{01}&P_{02}&P_{03}\\ P_{10}&P_{11}&P_{12}&P_{13}\\ P_{20}&P_{21}&P_{22}&P_{23}\\ P_{30}&P_{31}&P_{32}&P_{33}\end{pmatrix} \\那么p(u,v)=UMPM^TV^T
令U=(u3u2u1),V=(v3v2v1)M=
−13−313−630−33001000
,P=
P00P10P20P30P01P11P21P31P02P12P22P32P03P13P23P33
那么p(u,v)=UMPMTVT
对得到的这个式子进行编程就可以绘制出一个双三次贝塞尔曲线。
绘制双贝塞尔曲面
双三次贝塞尔曲面可以看着一个弯曲的四面体,采用四叉树递归划分法进行细分,递归细分次数足够时分割出来的子曲面近似为一个平面四边形,这些平面四边形拼成了双三次贝塞尔曲线。
递归绘制细分曲面
递归函数
void CBezier::Recursion(CDC* pDC, int ReNumber, CMesh Mesh)
{
if (0 == ReNumber)
{
Tessellation(Mesh);//细分曲面,根据公式求坐标,将(u,v)点转换为(x,y)点
DrawQuadrilateral(pDC);//绘制小平面四边形
return;
}
else
{
CDimension2 Mid = (Mesh.m_BottomLeft + Mesh.m_TopRight) / 2.0;
CMesh SubMesh[4];//一分为四个
//左下子长方形
SubMesh[0].m_BottomLeft = Mesh.m_BottomLeft;
SubMesh[0].m_BottomRight = CDimension2(Mid.m_u, Mesh.m_BottomLeft.m_v);
SubMesh[0].m_TopRight = CDimension2(Mid.m_u, Mid.m_v);
SubMesh[0].m_TopLeft = CDimension2(Mesh.m_BottomLeft.m_u, Mid.m_v);
//右下子长方形
SubMesh[1].m_BottomLeft = SubMesh[0].m_BottomRight;
SubMesh[1].m_BottomRight = Mesh.m_BottomRight;
SubMesh[1].m_TopRight = CDimension2(Mesh.m_BottomRight.m_u, Mid.m_v);
SubMesh[1].m_TopLeft = SubMesh[0].m_TopRight;
//右上子长方形
SubMesh[2].m_BottomLeft = SubMesh[1].m_TopLeft;
SubMesh[2].m_BottomRight = SubMesh[1].m_TopRight;
SubMesh[2].m_TopRight = Mesh.m_TopRight;
SubMesh[2].m_TopLeft = CDimension2(Mid.m_u, Mesh.m_TopRight.m_v);
//左上子长方形
SubMesh[3].m_BottomLeft = SubMesh[0].m_TopLeft;
SubMesh[3].m_BottomRight = SubMesh[2].m_BottomLeft;
SubMesh[3].m_TopRight = SubMesh[2].m_TopLeft;
SubMesh[3].m_TopLeft = Mesh.m_TopLeft;
Recursion(pDC, ReNumber - 1, SubMesh[0]);//递归绘制4个子曲面
Recursion(pDC, ReNumber - 1, SubMesh[1]);
Recursion(pDC, ReNumber - 1, SubMesh[2]);
Recursion(pDC, ReNumber - 1, SubMesh[3]);
}
}
求细分曲面四个顶点的坐标
void CBezier::Tessellation(CMesh Mesh)
{
double M[4][4];//系数矩阵M
M[0][0] = -1, M[0][1] = 3, M[0][2] = -3, M[0][3] = 1;
M[1][0] = 3, M[1][1] = -6, M[1][2] = 3, M[1][3] = 0;
M[2][0] = -3, M[2][1] = 3, M[2][2] = 0, M[2][3] = 0;
M[3][0] = 1, M[3][1] = 0, M[3][2] = 0, M[3][3] = 0;
CPoint3 P3[4][4];//曲线计算用控制点数组
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
P3[i][j] = m_CtrPt[i][j];
LeftMultiplyMatrix(M, P3);//系数矩阵左乘三维点矩阵
TransposeMatrix(M);//计算转置矩阵
RightMultiplyMatrix(P3, M);//系数矩阵右乘三维点矩阵
double u0, u1, u2, u3, v0, v1, v2, v3;//u、v参数的幂
double u[4] = { Mesh.m_BottomLeft.m_u,Mesh.m_BottomRight.m_u ,Mesh.m_TopRight.m_u ,Mesh.m_TopLeft.m_u };
double v[4] = { Mesh.m_BottomLeft.m_v,Mesh.m_BottomRight.m_v ,Mesh.m_TopRight.m_v ,Mesh.m_TopLeft.m_v };
for (int i = 0; i < 4; i++)
{
u3 = pow(u[i], 3.0), u2 = pow(u[i], 2.0), u1 = u[i], u0 = 1;
v3 = pow(v[i], 3.0), v2 = pow(v[i], 2.0), v1 = v[i], v0 = 1;
CPoint3 Pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
+ (u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
+ (u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
+ (u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
m_QuadrPoint[i] = Pt;
}
}
绘制细分曲面,就是知道了四个点绘制四边形
void CBezier::DrawQuadrilateral(CDC* pDC)
{
CPoint2 ScreenPoint[4];//二维投影点
for (int nPoint = 0; nPoint < 4; nPoint++)
ScreenPoint[nPoint] = m_QuadrPoint[nPoint];//正交投影
CPen NewPen, * pOldPen;
NewPen.CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
pOldPen = pDC->SelectObject(&NewPen);
pDC->MoveTo(ROUND(ScreenPoint[0].m_x), ROUND(ScreenPoint[0].m_y));
pDC->LineTo(ROUND(ScreenPoint[1].m_x), ROUND(ScreenPoint[1].m_y));
pDC->LineTo(ROUND(ScreenPoint[2].m_x), ROUND(ScreenPoint[2].m_y));
pDC->LineTo(ROUND(ScreenPoint[3].m_x), ROUND(ScreenPoint[3].m_y));
pDC->LineTo(ROUND(ScreenPoint[0].m_x), ROUND(ScreenPoint[0].m_y));
pDC->SelectObject(pOldPen);
NewPen.DeleteObject();
}
第二种方法
在四根贝塞尔曲线p(t),q(t),r(t),s(t)上去相同的t,根据定义可以得到四个点P,Q,R,S,这四个点又可以画一个三次贝塞尔曲线,那么在t从0递增至1的过程中就会产生一系列的三次贝塞尔曲线,这一组曲线就构成了一个曲面。
需要项目代码的可以评论区留言或者私信