【图形学】双三次贝塞尔曲线绘制方法

双三次贝塞尔曲线的定义

双三次贝塞尔曲面是由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=03j=03Pi,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)=(1u)3B1,3(u)=3u(1u)2B2,3(u)=3u2(1u)B3,3(u)=u3 B0,3(v)=(1v)3B1,3(v)=3v(1v)2B2,3(v)=3v2(1v)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) 1331363033001000 P00P10P20P30P01P11P21P31P02P12P22P32P03P13P23P33 1331363033001000 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= 1331363033001000 ,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的过程中就会产生一系列的三次贝塞尔曲线,这一组曲线就构成了一个曲面。
在这里插入图片描述

需要项目代码的可以评论区留言或者私信

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/438456.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue-40、Vue中TodoList案例

1、MyHeader.vue <template><div class"todo-header"><input type"text" placeholder"请输入你的任务名称&#xff0c;按回车键确认" v-model"title" keyup.enter"add"></div> </template>&…

类加载子系统

类加载子系统 文章目录 类加载子系统1. 内存结构概述2. 类加载器与类的加载过程2.1 类加载器ClassLoader角色2.2 类的加载过程2.2.1 加载2.2.2 链接2.2.3 初始化2.2.4 补充 3. 类加载器分类3.1 虚拟机自带的加载器3.2 用户自定义类加载器 4. 关于ClassLoader5. 双亲委派机制5.1…

Mysql-存储引擎-InnoDB

数据文件 下面这条SQL语句执行的时候指定了ENGINE InnoDB存储引擎为InnoDB: CREATE TABLE tb_album (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 编号,title varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 相册名称,image varc…

ORB-SLAM策略思考之RANSAC

ORB-SLAM策略思考之RANSAC 1. 初始化器的RANSAC ORB-SLAM中的初始化器是一个端到端的地图初始化策略&#xff0c;即不需要人的参与双线程同时计算本质矩阵和单应性矩阵使用基于RANSAC和卡方检验的评价方法 为了保证两种算法评价的一致性&#xff0c;计算本质矩阵F和单应性矩阵…

shell脚本基础之循环语句

目录 一、循环语句的概念 二、for循环语句 1、列表循环 2、列表for循环案例大全 案例一 案例二 案例三 案例四 案例五 案例六 案例七 案例八 3、不带列表循环 4、类似C语言风格的for循环 5、for循环总结 三、while循环语句 1、while循环语句格式 2、while死循…

FreeRTOS

1.新建一个无FreeRTOS的工程&#xff0c;取名为Motor&#xff0c;根据风扇模块PDF原理图和操作文档让风扇转动 2.新建一个包含FreeRTOS的工程&#xff0c;取名为Semaphore 具体步骤&#xff1a;创建两个任务和一个共享资源&#xff0c;在两个任务中使用信号量来同时访问共享资源…

GPT store和Assistants API横空出世,AI Agent创业公司将何去何从?

Look&#xff01;&#x1f440;我们的大模型商业化落地产品&#x1f4d6;更多AI资讯请&#x1f449;&#x1f3fe;关注Free三天集训营助教在线为您火热答疑&#x1f469;&#x1f3fc;‍&#x1f3eb; 根据OpenAI发布的产品时间线&#xff0c;我们可以看到OpenAI在短短一年内迅…

2024年数学建模美赛C题(预测 Wordle)——思路、程序总结分享

1: 问题描述与要求 《纽约时报》要求您对本文件中的结果进行分析&#xff0c;以回答几个问题。 问题1&#xff1a;报告结果的数量每天都在变化。开发一个模型来解释这种变化&#xff0c;并使用您的模型为2023年3月1日报告的结果数量创建一个预测区间。这个词的任何属性是否会…

鸿蒙原生应用开发已全面启动,你还在等什么?

2019年&#xff0c;鸿蒙系统首次公开亮相&#xff0c;你们说&#xff0c;等等看&#xff0c;还不成熟&#xff1b; 2021年&#xff0c;鸿蒙系统首次在手机端升级&#xff0c;你们说&#xff0c;等等看&#xff0c;还不完善&#xff1b; 2024年&#xff0c;鸿飞计划发布&#…

【JAVA语言-第16话】集合框架(三)——Set、HashSet、LinkedHashSet、TreeSet集合的详细解析

目录 Set集合 1.1 概述 1.2 特点 1.3 HashSet集合 1.3.1 概述 1.3.2 哈希表 1.3.3 哈希值 1.3.4 练习 1.3.5 HashSet存储自定义类型元素 1.4 LinkedHashSet集合 1.4.1 概述 1.4.2 特点 1.4.3 练习 1.5 TreeSet集合 1.5.1 概述 1.5.2 练习 1.6 HashSet、Lin…

排序【数据结构】

文章目录 一、 稳定性二、排序1. 插入排序(1) 直接插入排序(2) 希尔排序 2. 选择排序(1) 直接选择排序(2) 堆排序 3. 交换排序(1) 冒泡排序(2) 快速排序① 普通版快排② 关于优化快排③ 快速排序的非递归方式 4. 归并排序5. 计数排序 三、 总结 一、 稳定性 在计算机科学中&am…

81.网游逆向分析与插件开发-背包的获取-装备栏数据结构的逆向分析

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;自动化助手显示物品数据-CSDN博客 然后游戏中有弓箭&#xff0c;弓箭有数量&#xff0c;可以作为突破口&#xff0c;也可以使用物品id 获取弓的方式 获取弓箭的方式 然后搜索250 然后搜索出一个 然后…