文章目录

  • 说明
  • B样条曲线
  • 代码

说明

在阅读此博客前,请访问2018级山东大学计算机学院图形学实验汇总。

原笔记通过latex编写,csdn只支持latex部分功能,所以下面主要是将pdf截屏上传。部分内容参考课本。

B样条曲线

前面讲解了Bézier曲线,这个曲线有很多用处,但是也有不少缺点:

  1. 一旦确定了特征多边形的顶点数(n+1个),也就决定了曲线的阶次(n次)
  2. Bézier曲线或曲面的拼接比较复杂
  3. Bezier曲线或曲面不能作局部修改,移动一个控制顶点,整个曲线都会变化(因为每个Bernstein多项式在整个[0,1]区间上都有支撑(函数值不为0),并且曲线是这些函数的混合,所以每个控制项对0到1之间t值处的曲线都有影响。)

B样条曲线保留了Bézier曲线的所有优点,同时克服了缺点。所谓样条,就是分段连续多项式。

整条曲线用一个完整的表达形式,但是内在是一段一段的,比如三次曲线,两条之间满足二次连续。这样不仅克服了波动,并且曲线是低次的。有统一的表达和统一的算法。

比如5个点,通过Bézier是四阶多项式。但是B样条可以构造四段曲线,每一段都是三次的,段与段之间满足二阶几何连续。

代码

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "shader.h"
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;struct Point{//像素点double x,y;Point(double _x=0,double _y=0):x(_x),y(_y){};Point&operator=(const Point &p){x=p.x; y=p.y;return *this;};bool operator ==(const Point &p){return (x==p.x&&y==p.y);}
}templine[2],LockPoint(-1,-1);//临时线和锁定点
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const unsigned int MAXN=600+1;
const char* vertexShaderPath="/Users/longzhengtian/Desktop/代码/C++代码/ComputerGraphics/shader.vs";
const char* fragmentShaderPath="/Users/longzhengtian/Desktop/代码/C++代码/ComputerGraphics/shader.fs";
vector<Point> ControlPloy;//控制多边形
vector<Point> ControlLine;//控制线
vector<Point> BSplinePoint;//BSpline曲线上的点bool DrawControlPoly=false,DrawControlPolyEnd=false;
bool change=false;//曲线要发生变化
bool moving=false;unsigned int VBO[4], VAO[4];
unsigned int k=0;//阶数
int LockPos=-1;void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void CalcuBspline(double &t,double pre);
float transX(double x){return (float)((float)(2*x)-SCR_WIDTH)/(SCR_WIDTH*1.0f);}
float transY(double y){return (float)(SCR_HEIGHT-2*(float)y)/(SCR_HEIGHT*1.0f);}int main(){//实例化glfw函数glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif//创建glfw窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "B-Spline", nullptr, nullptr);if (window == nullptr){cout << "Failed to create GLFW window" << endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);//将window设置为接下来操作的主窗口glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);glfwSetKeyCallback(window, key_callback);glfwSetMouseButtonCallback(window, mouse_button_callback);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){//使用glad加载glfw的所有函数指针cout << "Failed to initialize GLAD" << endl;return -1;}Shader ourShader(vertexShaderPath,fragmentShaderPath);//创建着色器程序GLfloat vertices[MAXN*MAXN];//B样条曲线上的点GLfloat lintVertices[7];//临时线GLfloat linVertices[MAXN];//现存线GLfloat ControlVertices[MAXN*MAXN];//控制线glGenVertexArrays(4, VAO);glGenBuffers(4, VBO);glBindVertexArray(VAO[0]);glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);glEnableVertexAttribArray(0);glBindVertexArray(VAO[1]);//临时边glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);glBufferData(GL_ARRAY_BUFFER, sizeof(lintVertices), lintVertices, GL_DYNAMIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);glEnableVertexAttribArray(0);glBindVertexArray(VAO[2]);//现存边glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);glBufferData(GL_ARRAY_BUFFER, sizeof(linVertices), linVertices, GL_DYNAMIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);glEnableVertexAttribArray(0);glBindVertexArray(VAO[3]);//控制边glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);glBufferData(GL_ARRAY_BUFFER, sizeof(ControlVertices), ControlVertices, GL_DYNAMIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) 0);glEnableVertexAttribArray(0);double precision=0.005;double t=-precision;//时间控制while (!glfwWindowShouldClose(window)){//输入processInput(window);//渲染命令glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);//临时边ourShader.setVec4("ourColor", 1.0f, 1.0f, 1.0f, 1.0f);ourShader.use();glBindVertexArray(VAO[1]);glDrawArrays(GL_LINES, 0, 2);//现存边ourShader.setVec4("ourColor", 1.0f, 1.0f, 1.0f, 1.0f);ourShader.use();glBindVertexArray(VAO[2]);glDrawArrays(GL_LINE_STRIP, 0, ControlPloy.size());//BSpline Curveif (change && k!=0) {if(t==-precision){//初次绘画ControlLine.clear();BSplinePoint.clear();}CalcuBspline(t, precision);vector<float> tempvecV;GLfloat *tempvec;tempvecV.clear();for (int i = 0; i < BSplinePoint.size(); i++) {tempvecV.push_back(transX(BSplinePoint[i].x));tempvecV.push_back(transY(BSplinePoint[i].y));tempvecV.push_back(1.0f);}tempvec = tempvecV.data();glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);glBufferSubData(GL_ARRAY_BUFFER, 0, tempvecV.size() * sizeof(float), tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);if(t<ControlPloy.size()){tempvecV.clear();for (int i = 0; i < ControlLine.size(); i++) {tempvecV.push_back(transX(ControlLine[i].x));tempvecV.push_back(transY(ControlLine[i].y));tempvecV.push_back(1.0f);}tempvec = tempvecV.data();glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);glBufferSubData(GL_ARRAY_BUFFER, 0, tempvecV.size() * sizeof(float), tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);}else {t=-precision;change=false;//不写会无限循环}}if(t!=-precision){ourShader.setVec4("ourColor", 1.0f, 0.0f, 0.0f, 1.0f);ourShader.use();glBindVertexArray(VAO[3]);glDrawArrays(GL_LINES, 0, ControlLine.size());}ourShader.setVec4("ourColor", 0.0f, 1.0f, 0.0f, 1.0f);ourShader.use();glBindVertexArray(VAO[0]);glDrawArrays(GL_LINE_STRIP, 0, BSplinePoint.size());//检查并调用事件,交换缓冲glfwSwapBuffers(window);glfwPollEvents();}glDeleteVertexArrays(4, VAO);glDeleteBuffers(4, VBO);glfwTerminate();return 0;
}
void CalcuBspline(double &t,double precision){t+=precision;//时间分为1/precison份if(t<k-1) t=k-1;//直接跳到合适的区间,[t_{k-1},t_{n+1}]int j=t;//直接向下取整获得区间vector<vector<Point>> tempConLine;//tempConLine[i][j]是指第i层,第j个点vector<Point> tempv;for (int i=j-k+1; i<=j; i++){//第0次迭代,将P_{j-k+1},...P_j都加入控制线中tempv.push_back(ControlPloy[i]);}tempConLine.push_back(tempv);for (int r=1; r<=k-1; r++){//迭代k-1次tempv.clear();for (int i=j-k+r+1,ii=1; i<=j,ii<tempConLine[r-1].size(); i++,ii++){double alpha=t-i;double dev=(k-r);//t_{i+k-r}-t_{i};alpha=(dev!=0)?alpha/dev:0;//获得alpha的值tempv.emplace_back((1.0-alpha)*tempConLine[r-1][ii-1].x+alpha*tempConLine[r-1][ii].x,(1.0-alpha)*tempConLine[r-1][ii-1].y+alpha*tempConLine[r-1][ii].y);}tempConLine.push_back(tempv);}ControlLine.clear();for (int i=1; i<=k-1; i++){ControlLine.push_back(tempConLine[i][0]);for (int l=1; l<tempConLine[i].size(); l++){ControlLine.push_back(tempConLine[i][l]);ControlLine.push_back(tempConLine[i][l]);}ControlLine.push_back(tempConLine[i][tempConLine[i].size()-1]);}BSplinePoint.push_back(tempConLine[k-1][0]);
}
void processInput(GLFWwindow *window){if(DrawControlPoly){//此时处于画直线状态double xpos,ypos;glfwGetCursorPos(window,&xpos,&ypos);templine[0]=ControlPloy[ControlPloy.size()-1];templine[1]=Point(xpos,ypos);float tempvec[]={transX(templine[0].x),transY(templine[0].y),1.0f,transX(templine[1].x),transY(templine[1].y),1.0f};glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempvec), &tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);}if(moving){double xpos,ypos;glfwGetCursorPos(window,&xpos,&ypos);float tempvec[]={transX(xpos),transY(ypos),1.0f};glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);glBufferSubData(GL_ARRAY_BUFFER, LockPos*3*sizeof(float), sizeof(tempvec), &tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);}
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height){glViewport(0, 0, width, height);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);else if (key == GLFW_KEY_P && action == GLFW_PRESS){//如果按下"P",开始画控制多边形,也可以用于增加点if(!DrawControlPoly){DrawControlPoly=true;double xpos,ypos;glfwGetCursorPos(window,&xpos,&ypos);float tempvec[]={transX(xpos),transY(ypos),1.0f};glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);glBufferSubData(GL_ARRAY_BUFFER, ControlPloy.size()*3*sizeof(float), sizeof(tempvec), &tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);ControlPloy.emplace_back(xpos,ypos);}else {//这次是为了结束DrawControlPoly=false;templine[0]=Point(0,0);templine[1]=Point(0,0);float tempvec[]={0.0f,0.0f,1.0f,0.0f,0.0f,1.0f};glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempvec), &tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);change=true;//发生改变,需要重新计算控制线}}else if (key == GLFW_KEY_M && action == GLFW_PRESS) {//如果按下"M",开始移动if(!moving && LockPos!=-1){//本来不移动,结果点击开始移动了moving=true;}else{//移动停止cout<<"You moved the "<<LockPos+1<<"th vertex in the control polygon."<<endl;moving=false;double xpos,ypos;glfwGetCursorPos(window,&xpos,&ypos);ControlPloy[LockPos]=Point(xpos,ypos);cout<<"New ControlPoly:"<<endl;for (int i=0; i<ControlPloy.size(); i++)cout<<"("<<ControlPloy[i].x<<','<<ControlPloy[i].y<<") ";cout<<endl;change=true;//发生改变,需要重新计算控制线}}else if (key == GLFW_KEY_D && action == GLFW_PRESS) {//如果按下"D",删除点if(LockPos!=-1){//此时刚刚锁定了一个点,可以删除了int tempsize=(ControlPloy.size()-LockPos)*3,tempcnt=0;float tempvec[tempsize];memset(tempvec,0,sizeof(tempvec));for (int i=LockPos+1; i<ControlPloy.size(); i++){tempvec[tempcnt++]=transX(ControlPloy[i].x);tempvec[tempcnt++]=transY(ControlPloy[i].y);tempvec[tempcnt++]=1.0f;}//控制多边形数组中删除auto it=find(ControlPloy.begin(),ControlPloy.end(),LockPoint);ControlPloy.erase(it);//缓冲区中删除glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);glBufferSubData(GL_ARRAY_BUFFER, LockPos*3*sizeof(float), sizeof(tempvec), &tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);cout<<"You deleted the "<<LockPos+1<<"th vertex in the control polygon."<<endl;cout<<"New ControlPoly:"<<endl;for (int i=0; i<ControlPloy.size(); i++)cout<<"("<<ControlPloy[i].x<<','<<ControlPloy[i].y<<") ";cout<<endl;//解除锁定LockPoint=Point(-1,-1); LockPos=-1;change=true;//发生改变,需要重新计算控制线DrawControlPolyEnd=true;}}else if (key == GLFW_KEY_K && action == GLFW_PRESS) {//如果按下"K",改变阶数cout<<"Please enter the order of the B-spline:"<<endl;cin>>k;//输入阶数。我们规定,节点表从0开始,逐次+1。t向下取整可以直接确定区间。change=true;}
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods){if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS){//鼠标点击了左键double xpos,ypos;glfwGetCursorPos(window,&xpos,&ypos);if(DrawControlPoly){//此时是画线状态,需要增加线float tempvec[]={transX(xpos),transY(ypos),1.0f};glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);glBufferSubData(GL_ARRAY_BUFFER, ControlPloy.size()*3*sizeof(float), sizeof(tempvec), &tempvec);glBindBuffer(GL_ARRAY_BUFFER, 0);ControlPloy.emplace_back(xpos,ypos);}else if(!moving){//此时锁定点,看情况是否删除LockPoint=Point(-1,-1); LockPos=-1;//初始化for (int i=0; i<ControlPloy.size(); i++){if(fabs(xpos-ControlPloy[i].x)<=5 && fabs(ypos-ControlPloy[i].y)<=5){//给定一个误差范围LockPoint=ControlPloy[i]; LockPos=i;cout<<"You locked the "<<LockPos+1<<"th vertex in the control polygon."<<endl;break;}}}}
}

B Spline(B样条曲线)相关推荐

  1. cad.net 依照旧样条曲线数据生成一条新样条曲线的代码段. spline生成

    Spline spl = entity as Spline; //拿到旧的spline图元... //样条曲线生成条件var controlPoints = new Point3dCollection ...

  2. un4 unreal4 创建路径 曲线 管道 Spline组件 使用方法

    想创建自定义曲线,自定义的管道,路径,或者任何跟随路径的模型 找了半天,发现个 Spline 和 SplineMesh 这两个组件 找来找去没什么资料... 最后在油管找到个视频,抱歉无法搬运 在上面 ...

  3. 基于HTML5+JavaScript实现的网页录屏器设计

    资源下载地址:https://download.csdn.net/download/sheziqiong/85773105 资源下载地址:https://download.csdn.net/downl ...

  4. Mathematics English Vocabulary (Cited)

    一般词汇 数学 mathematics, maths(BrE), math(AmE) 公理 axiom 定理 theorem 计算 calculation 运算 operation 证明 prove ...

  5. AutoCad 修改多段线

    对于用"pline"命令创建多段线对象,用户可使用"pedit"命令来进行修改.该命令调用方式为: 工具栏:"Modify II(修改 II)&quo ...

  6. MathNet.Numerics主要类功能简述

    MathNet.Numerics主要类功能简述 MathNet.Numerics是一个.NET的开源数学库,包含了.NET平台上的面向对象数字计算的基础类.类似 NMath ,但 NMath 是收费的 ...

  7. 数学专业英语词汇英汉对照

    数学专业英语词汇英汉对照 A absolute value 绝对值 accept 接受 acceptable region 接受域 additivity 可加性 adjusted 调整的 altern ...

  8. CAD快捷键_CAD常用快捷键大全

    在 CAD 软件操作中,为方便使用者,利用快捷键代替鼠标.可以利用键盘快捷键发出命令,完成绘图,修改,保存等操作.这些命令键就是 CAD快捷键 .下面是 学习啦 为大家整理的一些CAD常用快捷键: 目 ...

  9. AutoCAD机械制图英语词汇

    2006-04-05 21:25:53 AutoCAD机械制图英语词汇 2D Solid 二维实体 2D 实面 2D Wireframe 二维线框 3D Array 三维阵列 3D 阵列 3D Dyn ...

  10. cad页面布局快捷键_CAD绘图中的常用按键以及功效

    CAD软件中的常用快捷键有很多,下面就来介绍一些比较常见的功能的快捷键吧! 复制对象CO或CP 创建属性定义AT 编辑单个块的可变属性ATE 修改对象的颜色.图层.线型和厚度CH 设置新对象的颜色CO ...

最新文章

  1. WebRTC音频预处理单元APM的整体编译及使用
  2. 如何采用python语言绘制一条_如何使用matplotlib绘制一条线?
  3. java同名不同包文件_java – 如何从不同的JAR读取同名的几个资源文件?
  4. python预测发展趋势_Python中的趋势“预测器”?
  5. 使用java代码发送zip文件到邮箱_Azkaban安装与使用(下)
  6. 三菱驱动器参数表_三菱伺服驱动器参数都设置什么啊 详细点 谢谢
  7. 计算机管理员绩效指标,网络管理员绩效kpi考核标准..doc
  8. JavaScript document对象
  9. Android 高通8909 系统之路之 裁剪系统 一
  10. Ubuntu18.04 安装360 WIFI驱动
  11. @JsonProperty
  12. 【Ubuntu】 Ubuntu16.04中设置使用root用户登录图形界面
  13. python中的numpy标准正态分布_Numpy创建正态分布和均匀分布
  14. 【win11】关闭 Windows 安全中心中的Defender 防病毒保护
  15. 拳王虚拟项目公社:拳王的个人IP打造之路,助你百度霸屏,与个人成长学习经历分享
  16. 钉钉OA流程审批,Jenkins自动授权通知用户密码
  17. 初级程序员的苦逼日子
  18. 星际旅行飞船乘坐票制作生成微信小程序源码下载
  19. 【深度学习】word2vec(上)
  20. JavaWeb项目——基于Servlet实现的在线OJ平台 (项目问答+代码详解)

热门文章

  1. Golang + HTML5 实现多文件上传
  2. 用Squid和DNSPod打造自己的CDN详细教程
  3. Activities介绍
  4. 5g多卡聚合路由器/5g多卡汇聚路由器,多网融合,弱网通信,多卡聚合路由器,5G多卡聚合路由,工业路由器,移动物联,商用路由, 视频直播, 融合通讯, 多链路聚合,5G多卡聚合,5G多链路聚合
  5. [BZOJ3772]精神污染 主席树上树+欧拉序
  6. java语言要学多久_零基础学JAVA语言难吗!需要多久
  7. 【数据科学】迄今最全面的数据科学应用总结:16个分析学科及落地应用
  8. Surface Defect Detection Methods for Industrial Products : A Review
  9. 第九届中国云计算大会讲师团探秘 ——数位院士领衔、近20个国家的学者共聚、多个行业领头人及专家参与, 共话云计算大数据生态、应用...
  10. sonar代码审查问题分析