双三次贝塞尔曲线的定义
双三次贝塞尔曲面是由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];//系数矩阵MM[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的过程中就会产生一系列的三次贝塞尔曲线,这一组曲线就构成了一个曲面。
需要项目代码的可以评论区留言或者私信